mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-07-20 11:29:38 +02:00
Collect dependency information for parsed CallStmts.
Parse analysis of a CallStmt will inject mutable information,
for instance the OID of the called procedure, so that subsequent
DDL may create a need to re-parse the CALL. We failed to detect
this for CALLs in plpgsql routines, because no dependency information
was collected when putting a CallStmt into the plan cache. That
could lead to misbehavior or strange errors such as "cache lookup
failed".
Before commit ee895a655
, the issue would only manifest for CALLs
appearing in atomic contexts, because we re-planned non-atomic
CALLs every time through anyway.
It is now apparent that extract_query_dependencies() probably
needs a special case for every utility statement type for which
stmt_requires_parse_analysis() returns true. I wanted to add
something like Assert(!stmt_requires_parse_analysis(...)) when
falling out of extract_query_dependencies_walker without doing
anything, but there are API issues as well as a more fundamental
point: stmt_requires_parse_analysis is supposed to be applied to
raw parser output, so it'd be cheating to assume it will give the
correct answer for post-parse-analysis trees. I contented myself
with adding a comment.
Per bug #18131 from Christian Stork. Back-patch to all supported
branches.
Discussion: https://postgr.es/m/18131-576854e79c5cd264@postgresql.org
This commit is contained in:
parent
4a81ed29d3
commit
4435a8d5db
@ -2856,8 +2856,25 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
|
|||||||
if (query->commandType == CMD_UTILITY)
|
if (query->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Ignore utility statements, except those (such as EXPLAIN) that
|
* This logic must handle any utility command for which parse
|
||||||
* contain a parsed-but-not-planned query.
|
* analysis was nontrivial (cf. stmt_requires_parse_analysis).
|
||||||
|
*
|
||||||
|
* Notably, CALL requires its own processing.
|
||||||
|
*/
|
||||||
|
if (IsA(query->utilityStmt, CallStmt))
|
||||||
|
{
|
||||||
|
CallStmt *callstmt = (CallStmt *) query->utilityStmt;
|
||||||
|
|
||||||
|
/* We need not examine funccall, just the transformed exprs */
|
||||||
|
(void) extract_query_dependencies_walker((Node *) callstmt->funcexpr,
|
||||||
|
context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore other utility statements, except those (such as EXPLAIN)
|
||||||
|
* that contain a parsed-but-not-planned query. For those, we
|
||||||
|
* just need to transfer our attention to the contained query.
|
||||||
*/
|
*/
|
||||||
query = UtilityContainsQuery(query->utilityStmt);
|
query = UtilityContainsQuery(query->utilityStmt);
|
||||||
if (query == NULL)
|
if (query == NULL)
|
||||||
|
@ -376,3 +376,50 @@ BEGIN
|
|||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
NOTICE: <NULL>
|
NOTICE: <NULL>
|
||||||
|
-- check that we detect change of dependencies in CALL
|
||||||
|
-- atomic and non-atomic call sites do this differently, so check both
|
||||||
|
CREATE PROCEDURE inner_p (f1 int)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'inner_p(%)', f1;
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE FUNCTION f(int) RETURNS int AS $$ SELECT $1 + 1 $$ LANGUAGE sql;
|
||||||
|
CREATE PROCEDURE outer_p (f1 int)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'outer_p(%)', f1;
|
||||||
|
CALL inner_p(f(f1));
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE FUNCTION outer_f (f1 int) RETURNS void
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'outer_f(%)', f1;
|
||||||
|
CALL inner_p(f(f1));
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CALL outer_p(42);
|
||||||
|
NOTICE: outer_p(42)
|
||||||
|
NOTICE: inner_p(43)
|
||||||
|
SELECT outer_f(42);
|
||||||
|
NOTICE: outer_f(42)
|
||||||
|
NOTICE: inner_p(43)
|
||||||
|
outer_f
|
||||||
|
---------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP FUNCTION f(int);
|
||||||
|
CREATE FUNCTION f(int) RETURNS int AS $$ SELECT $1 + 2 $$ LANGUAGE sql;
|
||||||
|
CALL outer_p(42);
|
||||||
|
NOTICE: outer_p(42)
|
||||||
|
NOTICE: inner_p(44)
|
||||||
|
SELECT outer_f(42);
|
||||||
|
NOTICE: outer_f(42)
|
||||||
|
NOTICE: inner_p(44)
|
||||||
|
outer_f
|
||||||
|
---------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -357,3 +357,41 @@ BEGIN
|
|||||||
RAISE NOTICE '%', v_Text;
|
RAISE NOTICE '%', v_Text;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
|
-- check that we detect change of dependencies in CALL
|
||||||
|
-- atomic and non-atomic call sites do this differently, so check both
|
||||||
|
|
||||||
|
CREATE PROCEDURE inner_p (f1 int)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'inner_p(%)', f1;
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE FUNCTION f(int) RETURNS int AS $$ SELECT $1 + 1 $$ LANGUAGE sql;
|
||||||
|
|
||||||
|
CREATE PROCEDURE outer_p (f1 int)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'outer_p(%)', f1;
|
||||||
|
CALL inner_p(f(f1));
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE FUNCTION outer_f (f1 int) RETURNS void
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'outer_f(%)', f1;
|
||||||
|
CALL inner_p(f(f1));
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CALL outer_p(42);
|
||||||
|
SELECT outer_f(42);
|
||||||
|
|
||||||
|
DROP FUNCTION f(int);
|
||||||
|
CREATE FUNCTION f(int) RETURNS int AS $$ SELECT $1 + 2 $$ LANGUAGE sql;
|
||||||
|
|
||||||
|
CALL outer_p(42);
|
||||||
|
SELECT outer_f(42);
|
||||||
|
Loading…
Reference in New Issue
Block a user