aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/functions.c15
-rw-r--r--src/test/regress/expected/create_function_sql.out17
-rw-r--r--src/test/regress/sql/create_function_sql.sql10
3 files changed, 40 insertions, 2 deletions
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5509013521d..cff75b2f647 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1116,6 +1116,19 @@ sql_compile_callback(FunctionCallInfo fcinfo,
*/
func->num_queries = list_length(source_list);
+ /*
+ * Edge case: empty function body is OK only if it returns VOID. Normally
+ * we validate that the last statement returns the right thing in
+ * check_sql_stmt_retval, but we'll never reach that if there's no last
+ * statement.
+ */
+ if (func->num_queries == 0 && rettype != VOIDOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("return type mismatch in function declared to return %s",
+ format_type_be(rettype)),
+ errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.")));
+
/* Save the source trees in pcontext for now. */
MemoryContextSwitchTo(pcontext);
func->source_list = copyObject(source_list);
@@ -2103,7 +2116,7 @@ check_sql_stmt_retval(List *queryTreeList,
}
else
{
- /* Empty function body, or last statement is a utility command */
+ /* Last statement is a utility command, or it rewrote to nothing */
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
diff --git a/src/test/regress/expected/create_function_sql.out b/src/test/regress/expected/create_function_sql.out
index 2ee7631044e..963b6f863ff 100644
--- a/src/test/regress/expected/create_function_sql.out
+++ b/src/test/regress/expected/create_function_sql.out
@@ -757,9 +757,23 @@ LINE 2: AS 'SELECT $2;';
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
AS 'a', 'b';
ERROR: only one AS item needed for language "sql"
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
+ AS '';
+ERROR: return type mismatch in function declared to return integer
+DETAIL: Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.
+CONTEXT: SQL function "test1"
+-- make sure empty-body case is handled at execution time, too
+SET check_function_bodies = off;
+CREATE FUNCTION test1 (anyelement) RETURNS anyarray LANGUAGE SQL
+ AS '';
+SELECT test1(0);
+ERROR: return type mismatch in function declared to return integer[]
+DETAIL: Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.
+CONTEXT: SQL function "test1" during startup
+RESET check_function_bodies;
-- Cleanup
DROP SCHEMA temp_func_test CASCADE;
-NOTICE: drop cascades to 34 other objects
+NOTICE: drop cascades to 35 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()
@@ -794,5 +808,6 @@ drop cascades to function create_and_insert()
drop cascades to table ddl_test
drop cascades to function alter_and_insert()
drop cascades to function double_append(anyarray,anyelement)
+drop cascades to function test1(anyelement)
DROP USER regress_unpriv_user;
RESET search_path;
diff --git a/src/test/regress/sql/create_function_sql.sql b/src/test/regress/sql/create_function_sql.sql
index 68776be4c8d..6d1c102d780 100644
--- a/src/test/regress/sql/create_function_sql.sql
+++ b/src/test/regress/sql/create_function_sql.sql
@@ -449,6 +449,16 @@ CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
AS 'a', 'b';
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
+ AS '';
+
+-- make sure empty-body case is handled at execution time, too
+SET check_function_bodies = off;
+CREATE FUNCTION test1 (anyelement) RETURNS anyarray LANGUAGE SQL
+ AS '';
+SELECT test1(0);
+RESET check_function_bodies;
+
-- Cleanup
DROP SCHEMA temp_func_test CASCADE;
DROP USER regress_unpriv_user;