summaryrefslogtreecommitdiff
path: root/src/test/regress
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/regress')
-rw-r--r--src/test/regress/expected/create_function_sql.out10
-rw-r--r--src/test/regress/expected/misc_functions.out50
-rw-r--r--src/test/regress/expected/rangefuncs.out4
-rw-r--r--src/test/regress/expected/returning.out4
-rw-r--r--src/test/regress/expected/rowtypes.out10
-rw-r--r--src/test/regress/regress.c121
-rw-r--r--src/test/regress/sql/create_function_sql.sql11
-rw-r--r--src/test/regress/sql/misc_functions.sql34
8 files changed, 244 insertions, 0 deletions
diff --git a/src/test/regress/expected/create_function_sql.out b/src/test/regress/expected/create_function_sql.out
index 73c6730d459..42524230d2b 100644
--- a/src/test/regress/expected/create_function_sql.out
+++ b/src/test/regress/expected/create_function_sql.out
@@ -455,6 +455,16 @@ DROP TABLE functest3 CASCADE;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to view functestv3
drop cascades to function functest_s_14()
+-- Check reporting of temporary-object dependencies within SQL-standard body
+-- (tests elsewhere already cover dependencies on arg and result types)
+CREATE TEMP SEQUENCE mytempseq;
+CREATE FUNCTION functest_tempseq() RETURNS int
+ RETURN nextval('mytempseq');
+NOTICE: function "functest_tempseq" will be effectively temporary
+DETAIL: It depends on temporary sequence mytempseq.
+-- This discards mytempseq and therefore functest_tempseq(). If it fails to,
+-- the function will appear in the information_schema tests below.
+DISCARD TEMP;
-- information_schema tests
CREATE FUNCTION functest_IS_1(a int, b int default 1, c text default 'foo')
RETURNS int
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index e76e28b95ce..d7d965d884a 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -808,6 +808,56 @@ false, true, false, true);
Function Scan on generate_series g (cost=N..N rows=1000 width=N)
(1 row)
+--
+-- Test SupportRequestInlineInFrom request
+--
+CREATE FUNCTION test_inline_in_from_support_func(internal)
+ RETURNS internal
+ AS :'regresslib', 'test_inline_in_from_support_func'
+ LANGUAGE C STRICT;
+CREATE FUNCTION foo_from_bar(colname TEXT, tablename TEXT, filter TEXT)
+RETURNS SETOF TEXT
+LANGUAGE plpgsql
+AS $function$
+DECLARE
+ sql TEXT;
+BEGIN
+ sql := format('SELECT %I::text FROM %I', colname, tablename);
+ IF filter IS NOT NULL THEN
+ sql := CONCAT(sql, format(' WHERE %I::text = $1', colname));
+ END IF;
+ RETURN QUERY EXECUTE sql USING filter;
+END;
+$function$ STABLE;
+ALTER FUNCTION foo_from_bar(TEXT, TEXT, TEXT)
+ SUPPORT test_inline_in_from_support_func;
+SELECT * FROM foo_from_bar('f1', 'text_tbl', NULL);
+ foo_from_bar
+-------------------
+ doh!
+ hi de ho neighbor
+(2 rows)
+
+SELECT * FROM foo_from_bar('f1', 'text_tbl', 'doh!');
+ foo_from_bar
+--------------
+ doh!
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM foo_from_bar('f1', 'text_tbl', NULL);
+ QUERY PLAN
+----------------------
+ Seq Scan on text_tbl
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM foo_from_bar('f1', 'text_tbl', 'doh!');
+ QUERY PLAN
+-------------------------------
+ Seq Scan on text_tbl
+ Filter: (f1 = 'doh!'::text)
+(2 rows)
+
+DROP FUNCTION foo_from_bar;
-- Test functions for control data
SELECT count(*) > 0 AS ok FROM pg_control_checkpoint();
ok
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 30241e22da2..5cc94011e97 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -2178,6 +2178,8 @@ alter table users drop column todrop;
create or replace function get_first_user() returns users as
$$ SELECT * FROM users ORDER BY userid LIMIT 1; $$
language sql stable;
+NOTICE: function "get_first_user" will be effectively temporary
+DETAIL: It depends on temporary type users.
SELECT get_first_user();
get_first_user
-------------------
@@ -2193,6 +2195,8 @@ SELECT * FROM get_first_user();
create or replace function get_users() returns setof users as
$$ SELECT * FROM users ORDER BY userid; $$
language sql stable;
+NOTICE: function "get_users" will be effectively temporary
+DETAIL: It depends on temporary type users.
SELECT get_users();
get_users
---------------------
diff --git a/src/test/regress/expected/returning.out b/src/test/regress/expected/returning.out
index d02c2ceab53..cfaaf015bb3 100644
--- a/src/test/regress/expected/returning.out
+++ b/src/test/regress/expected/returning.out
@@ -306,6 +306,8 @@ SELECT * FROM foo;
-- Check use of a whole-row variable for an inlined set-returning function
CREATE FUNCTION foo_f() RETURNS SETOF foo AS
$$ SELECT * FROM foo OFFSET 0 $$ LANGUAGE sql STABLE;
+NOTICE: function "foo_f" will be effectively temporary
+DETAIL: It depends on temporary type foo.
UPDATE foo SET f2 = foo_f.f2 FROM foo_f() WHERE foo_f.f1 = foo.f1
RETURNING foo_f;
foo_f
@@ -930,6 +932,8 @@ BEGIN ATOMIC
(SELECT count(*) FROM foo WHERE foo = o),
(SELECT count(*) FROM foo WHERE foo = n);
END;
+NOTICE: function "foo_update" will be effectively temporary
+DETAIL: It depends on temporary table foo.
\sf foo_update
CREATE OR REPLACE FUNCTION public.foo_update()
RETURNS void
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index 677ad2ab9ad..956bc2d02fc 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -907,6 +907,8 @@ create temp table compos (f1 int, f2 text);
create function fcompos1(v compos) returns void as $$
insert into compos values (v); -- fail
$$ language sql;
+NOTICE: function "fcompos1" will be effectively temporary
+DETAIL: It depends on temporary type compos.
ERROR: column "f1" is of type integer but expression is of type compos
LINE 2: insert into compos values (v); -- fail
^
@@ -914,12 +916,18 @@ HINT: You will need to rewrite or cast the expression.
create function fcompos1(v compos) returns void as $$
insert into compos values (v.*);
$$ language sql;
+NOTICE: function "fcompos1" will be effectively temporary
+DETAIL: It depends on temporary type compos.
create function fcompos2(v compos) returns void as $$
select fcompos1(v);
$$ language sql;
+NOTICE: function "fcompos2" will be effectively temporary
+DETAIL: It depends on temporary type compos.
create function fcompos3(v compos) returns void as $$
select fcompos1(fcompos3.v.*);
$$ language sql;
+NOTICE: function "fcompos3" will be effectively temporary
+DETAIL: It depends on temporary type compos.
select fcompos1(row(1,'one'));
fcompos1
----------
@@ -1012,6 +1020,8 @@ select last(f) from fullname f;
create function longname(fullname) returns text language sql
as $$select $1.first || ' ' || $1.last$$;
+NOTICE: function "longname" will be effectively temporary
+DETAIL: It depends on temporary type fullname.
select f.longname from fullname f;
longname
----------
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index a2db6080876..56cc0567b1c 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -28,6 +28,7 @@
#include "commands/sequence.h"
#include "commands/trigger.h"
#include "executor/executor.h"
+#include "executor/functions.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -39,6 +40,7 @@
#include "port/atomics.h"
#include "postmaster/postmaster.h" /* for MAX_BACKENDS */
#include "storage/spin.h"
+#include "tcop/tcopprot.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/geo_decls.h"
@@ -803,6 +805,125 @@ test_support_func(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+PG_FUNCTION_INFO_V1(test_inline_in_from_support_func);
+Datum
+test_inline_in_from_support_func(PG_FUNCTION_ARGS)
+{
+ Node *rawreq = (Node *) PG_GETARG_POINTER(0);
+
+ if (IsA(rawreq, SupportRequestInlineInFrom))
+ {
+ /*
+ * Assume that the target is foo_from_bar; that's safe as long as we
+ * don't attach this to any other function.
+ */
+ SupportRequestInlineInFrom *req = (SupportRequestInlineInFrom *) rawreq;
+ StringInfoData sql;
+ RangeTblFunction *rtfunc = req->rtfunc;
+ FuncExpr *expr = (FuncExpr *) rtfunc->funcexpr;
+ Node *node;
+ Const *c;
+ char *colname;
+ char *tablename;
+ SQLFunctionParseInfoPtr pinfo;
+ List *raw_parsetree_list;
+ List *querytree_list;
+ Query *querytree;
+
+ if (list_length(expr->args) != 3)
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func called with %d args but expected 3", list_length(expr->args))));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ /* Get colname */
+ node = linitial(expr->args);
+ if (!IsA(node, Const))
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-Const parameters")));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ c = (Const *) node;
+ if (c->consttype != TEXTOID || c->constisnull)
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-TEXT parameters")));
+ PG_RETURN_POINTER(NULL);
+ }
+ colname = TextDatumGetCString(c->constvalue);
+
+ /* Get tablename */
+ node = lsecond(expr->args);
+ if (!IsA(node, Const))
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-Const parameters")));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ c = (Const *) node;
+ if (c->consttype != TEXTOID || c->constisnull)
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-TEXT parameters")));
+ PG_RETURN_POINTER(NULL);
+ }
+ tablename = TextDatumGetCString(c->constvalue);
+
+ /* Begin constructing replacement SELECT query. */
+ initStringInfo(&sql);
+ appendStringInfo(&sql, "SELECT %s::text FROM %s",
+ quote_identifier(colname),
+ quote_identifier(tablename));
+
+ /* Add filter expression if present. */
+ node = lthird(expr->args);
+ if (!(IsA(node, Const) && ((Const *) node)->constisnull))
+ {
+ /*
+ * We only filter if $3 is not constant-NULL. This is not a very
+ * exact implementation of the PL/pgSQL original, but it's close
+ * enough for demonstration purposes.
+ */
+ appendStringInfo(&sql, " WHERE %s::text = $3",
+ quote_identifier(colname));
+ }
+
+ /* Build a SQLFunctionParseInfo with the parameters of my function. */
+ pinfo = prepare_sql_fn_parse_info(req->proc,
+ (Node *) expr,
+ expr->inputcollid);
+
+ /* Parse the generated SQL. */
+ raw_parsetree_list = pg_parse_query(sql.data);
+ if (list_length(raw_parsetree_list) != 1)
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func parsed to more than one node")));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ /* Analyze the parse tree as if it were a SQL-language body. */
+ querytree_list = pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list),
+ sql.data,
+ (ParserSetupHook) sql_fn_parser_setup,
+ pinfo, NULL);
+ if (list_length(querytree_list) != 1)
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func rewrote to more than one node")));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ querytree = linitial(querytree_list);
+ if (!IsA(querytree, Query))
+ {
+ ereport(WARNING, (errmsg("test_inline_in_from_support_func didn't parse to a Query")));
+ PG_RETURN_POINTER(NULL);
+ }
+
+ PG_RETURN_POINTER(querytree);
+ }
+
+ PG_RETURN_POINTER(NULL);
+}
+
PG_FUNCTION_INFO_V1(test_opclass_options_func);
Datum
test_opclass_options_func(PG_FUNCTION_ARGS)
diff --git a/src/test/regress/sql/create_function_sql.sql b/src/test/regress/sql/create_function_sql.sql
index 3d5f2a92093..4543273f93a 100644
--- a/src/test/regress/sql/create_function_sql.sql
+++ b/src/test/regress/sql/create_function_sql.sql
@@ -241,6 +241,17 @@ SELECT functest_S_14();
DROP TABLE functest3 CASCADE;
+-- Check reporting of temporary-object dependencies within SQL-standard body
+-- (tests elsewhere already cover dependencies on arg and result types)
+CREATE TEMP SEQUENCE mytempseq;
+
+CREATE FUNCTION functest_tempseq() RETURNS int
+ RETURN nextval('mytempseq');
+
+-- This discards mytempseq and therefore functest_tempseq(). If it fails to,
+-- the function will appear in the information_schema tests below.
+DISCARD TEMP;
+
-- information_schema tests
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
index 220472d5ad1..0fc20fbb6b4 100644
--- a/src/test/regress/sql/misc_functions.sql
+++ b/src/test/regress/sql/misc_functions.sql
@@ -360,6 +360,40 @@ SELECT explain_mask_costs($$
SELECT * FROM generate_series(25.0, 2.0, 0.0) g(s);$$,
false, true, false, true);
+--
+-- Test SupportRequestInlineInFrom request
+--
+
+CREATE FUNCTION test_inline_in_from_support_func(internal)
+ RETURNS internal
+ AS :'regresslib', 'test_inline_in_from_support_func'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION foo_from_bar(colname TEXT, tablename TEXT, filter TEXT)
+RETURNS SETOF TEXT
+LANGUAGE plpgsql
+AS $function$
+DECLARE
+ sql TEXT;
+BEGIN
+ sql := format('SELECT %I::text FROM %I', colname, tablename);
+ IF filter IS NOT NULL THEN
+ sql := CONCAT(sql, format(' WHERE %I::text = $1', colname));
+ END IF;
+ RETURN QUERY EXECUTE sql USING filter;
+END;
+$function$ STABLE;
+
+ALTER FUNCTION foo_from_bar(TEXT, TEXT, TEXT)
+ SUPPORT test_inline_in_from_support_func;
+
+SELECT * FROM foo_from_bar('f1', 'text_tbl', NULL);
+SELECT * FROM foo_from_bar('f1', 'text_tbl', 'doh!');
+EXPLAIN (COSTS OFF) SELECT * FROM foo_from_bar('f1', 'text_tbl', NULL);
+EXPLAIN (COSTS OFF) SELECT * FROM foo_from_bar('f1', 'text_tbl', 'doh!');
+
+DROP FUNCTION foo_from_bar;
+
-- Test functions for control data
SELECT count(*) > 0 AS ok FROM pg_control_checkpoint();
SELECT count(*) > 0 AS ok FROM pg_control_init();