diff options
-rw-r--r-- | src/backend/catalog/pg_proc.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 9 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 16 | ||||
-rw-r--r-- | src/test/regress/expected/create_function_3.out | 27 | ||||
-rw-r--r-- | src/test/regress/sql/create_function_3.sql | 9 |
5 files changed, 64 insertions, 20 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 1454d2fb67b..25d35230d02 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -35,6 +35,7 @@ #include "parser/analyze.h" #include "parser/parse_coerce.h" #include "parser/parse_type.h" +#include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -891,12 +892,30 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) if (!isnull) { Node *n; + List *stored_query_list; n = stringToNode(TextDatumGetCString(tmp)); if (IsA(n, List)) - querytree_list = castNode(List, n); + stored_query_list = linitial(castNode(List, n)); else - querytree_list = list_make1(list_make1(n)); + stored_query_list = list_make1(n); + + querytree_list = NIL; + foreach(lc, stored_query_list) + { + Query *parsetree = lfirst_node(Query, lc); + List *querytree_sublist; + + /* + * Typically, we'd have acquired locks already while parsing + * the body of the CREATE FUNCTION command. However, a + * validator function cannot assume that it's only called in + * that context. + */ + AcquireRewriteLocks(parsetree, true, false); + querytree_sublist = pg_rewrite_query(parsetree); + querytree_list = lappend(querytree_list, querytree_sublist); + } } else { diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 7187f17da57..3412d31117a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -43,6 +43,7 @@ #include "parser/parse_agg.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" +#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -4470,6 +4471,12 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid, if (list_length(querytree_list) != 1) goto fail; querytree = linitial(querytree_list); + + /* + * Because we'll insist below that the querytree have an empty rtable + * and no sublinks, it cannot have any relation references that need + * to be locked or rewritten. So we can omit those steps. + */ } else { @@ -5022,6 +5029,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) goto fail; querytree = linitial(querytree_list); + /* Acquire necessary locks, then apply rewriter. */ + AcquireRewriteLocks(querytree, true, false); querytree_list = pg_rewrite_query(querytree); if (list_length(querytree_list) != 1) goto fail; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 4df8cc5abf6..c92958149e8 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2973,7 +2973,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS) } /* And finally the function definition ... */ - tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull); + (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull); if (proc->prolang == SQLlanguageId && !isnull) { print_function_sqlbody(&buf, proctup); @@ -3439,7 +3439,10 @@ print_function_sqlbody(StringInfo buf, HeapTuple proctup) { Query *query = lfirst_node(Query, lc); - get_query_def(query, buf, list_make1(&dpns), NULL, PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1); + /* It seems advisable to get at least AccessShareLock on rels */ + AcquireRewriteLocks(query, false, false); + get_query_def(query, buf, list_make1(&dpns), NULL, + PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1); appendStringInfoChar(buf, ';'); appendStringInfoChar(buf, '\n'); } @@ -3448,7 +3451,12 @@ print_function_sqlbody(StringInfo buf, HeapTuple proctup) } else { - get_query_def(castNode(Query, n), buf, list_make1(&dpns), NULL, 0, WRAP_COLUMN_DEFAULT, 0); + Query *query = castNode(Query, n); + + /* It seems advisable to get at least AccessShareLock on rels */ + AcquireRewriteLocks(query, false, false); + get_query_def(query, buf, list_make1(&dpns), NULL, + 0, WRAP_COLUMN_DEFAULT, 0); } } @@ -3467,7 +3475,7 @@ pg_get_function_sqlbody(PG_FUNCTION_ARGS) if (!HeapTupleIsValid(proctup)) PG_RETURN_NULL(); - SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull); + (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull); if (isnull) { ReleaseSysCache(proctup); diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out index 5955859bb57..a77df01042e 100644 --- a/src/test/regress/expected/create_function_3.out +++ b/src/test/regress/expected/create_function_3.out @@ -543,11 +543,13 @@ ERROR: cannot change routine kind DETAIL: "functest1" is a function. DROP FUNCTION functest1(a int); -- inlining of set-returning functions +CREATE TABLE functest3 (a int); +INSERT INTO functest3 VALUES (1), (2), (3); CREATE FUNCTION functest_sri1() RETURNS SETOF int LANGUAGE SQL STABLE AS ' - VALUES (1), (2), (3); + SELECT * FROM functest3; '; SELECT * FROM functest_sri1(); functest_sri1 @@ -558,17 +560,17 @@ SELECT * FROM functest_sri1(); (3 rows) EXPLAIN (verbose, costs off) SELECT * FROM functest_sri1(); - QUERY PLAN ------------------------------- - Values Scan on "*VALUES*" - Output: "*VALUES*".column1 + QUERY PLAN +-------------------------------------- + Seq Scan on temp_func_test.functest3 + Output: functest3.a (2 rows) CREATE FUNCTION functest_sri2() RETURNS SETOF int LANGUAGE SQL STABLE BEGIN ATOMIC - VALUES (1), (2), (3); + SELECT * FROM functest3; END; SELECT * FROM functest_sri2(); functest_sri2 @@ -579,12 +581,14 @@ SELECT * FROM functest_sri2(); (3 rows) EXPLAIN (verbose, costs off) SELECT * FROM functest_sri2(); - QUERY PLAN ------------------------------- - Values Scan on "*VALUES*" - Output: "*VALUES*".column1 + QUERY PLAN +-------------------------------------- + Seq Scan on temp_func_test.functest3 + Output: functest3.a (2 rows) +DROP TABLE functest3 CASCADE; +NOTICE: drop cascades to function functest_sri2() -- Check behavior of VOID-returning SQL functions CREATE FUNCTION voidtest1(a int) RETURNS VOID LANGUAGE SQL AS $$ SELECT a + 1 $$; @@ -643,7 +647,7 @@ SELECT * FROM voidtest5(3); -- Cleanup DROP SCHEMA temp_func_test CASCADE; -NOTICE: drop cascades to 30 other objects +NOTICE: drop cascades to 29 other objects DETAIL: drop cascades to function functest_a_1(text,date) drop cascades to function functest_a_2(text[]) drop cascades to function functest_a_3() @@ -668,7 +672,6 @@ drop cascades to function functest_s_13() drop cascades to function functest_s_15(integer) drop cascades to function functest_b_2(bigint) drop cascades to function functest_sri1() -drop cascades to function functest_sri2() drop cascades to function voidtest1(integer) drop cascades to function voidtest2(integer,integer) drop cascades to function voidtest3(integer) diff --git a/src/test/regress/sql/create_function_3.sql b/src/test/regress/sql/create_function_3.sql index 6e8b838ff2d..23a46b0b114 100644 --- a/src/test/regress/sql/create_function_3.sql +++ b/src/test/regress/sql/create_function_3.sql @@ -319,11 +319,14 @@ DROP FUNCTION functest1(a int); -- inlining of set-returning functions +CREATE TABLE functest3 (a int); +INSERT INTO functest3 VALUES (1), (2), (3); + CREATE FUNCTION functest_sri1() RETURNS SETOF int LANGUAGE SQL STABLE AS ' - VALUES (1), (2), (3); + SELECT * FROM functest3; '; SELECT * FROM functest_sri1(); @@ -333,12 +336,14 @@ CREATE FUNCTION functest_sri2() RETURNS SETOF int LANGUAGE SQL STABLE BEGIN ATOMIC - VALUES (1), (2), (3); + SELECT * FROM functest3; END; SELECT * FROM functest_sri2(); EXPLAIN (verbose, costs off) SELECT * FROM functest_sri2(); +DROP TABLE functest3 CASCADE; + -- Check behavior of VOID-returning SQL functions |