diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index 3845f066fc41a68d772cf73867216f71051fd419..420529c78384e43dcfafdc6e1fc0ff825f3dc81c 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -379,7 +379,8 @@ static const struct behavior_compat_entry behavior_compat_options[OPT_MAX] = { {"char_coerce_compat", OPT_CHAR_COERCE_COMPAT}, {"pgformat_substr", OPT_PGFORMAT_SUBSTR}, {"truncate_numeric_tail_zero", OPT_TRUNC_NUMERIC_TAIL_ZERO}, - {"allow_orderby_undistinct_column", OPT_ALLOW_ORDERBY_UNDISTINCT_COLUMN} + {"allow_orderby_undistinct_column", OPT_ALLOW_ORDERBY_UNDISTINCT_COLUMN}, + {"select_into_return_null", OPT_SELECT_INTO_RETURN_NULL} }; // increase SQL_IGNORE_STRATEGY_NUM if we need more strategy diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index 2e9990a3a2ebbea519553b1e689037063198e7e6..44b681636afa39eae64bcc2718633aeeaec4fb9d 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -181,7 +181,7 @@ static char *NameOfDatum(PLwdatum *wdatum); static char *CopyNameOfDatum(PLwdatum *wdatum); static void check_assignable(PLpgSQL_datum *datum, int location); static bool read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, - bool *strict, bool bulk_collect); + bool *strict, int firsttoken, bool bulk_collect); static PLpgSQL_row *read_into_scalar_list(char *initial_name, PLpgSQL_datum *initial_datum, int initial_dno, @@ -5054,7 +5054,7 @@ stmt_dynexecute : K_EXECUTE if (newp->into) /* multiple INTO */ yyerror("syntax error"); newp->into = true; - (void)read_into_target(&newp->rec, &newp->row, &newp->strict, false); + (void)read_into_target(&newp->rec, &newp->row, &newp->strict, K_EXECUTE, false); endtoken = yylex(); } /* If we found "USING", collect the argument */ @@ -5203,7 +5203,7 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO PLpgSQL_row *row; /* We have already parsed everything through the INTO keyword */ - (void)read_into_target(&rec, &row, NULL, false); + (void)read_into_target(&rec, &row, NULL, -1, false); if (yylex() != ';') yyerror("syntax error"); @@ -5302,7 +5302,7 @@ fetch_into_target : PLpgSQL_datum *datum = NULL; PLpgSQL_rec *rec; PLpgSQL_row *row; - (void)read_into_target(&rec, &row, NULL, true); + (void)read_into_target(&rec, &row, NULL, -1, true); if (rec != NULL) { datum = (PLpgSQL_datum *)rec; @@ -9382,7 +9382,7 @@ make_execsql_stmt(int firsttoken, int location) into_start_loc = yylloc; } u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; - is_user_var = read_into_target(&rec, &row, &have_strict, have_bulk_collect); + is_user_var = read_into_target(&rec, &row, &have_strict, firsttoken, have_bulk_collect); if (is_user_var) { u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = save_IdentifierLookup; have_into = false; @@ -10764,15 +10764,24 @@ read_into_using_add_tableelem(char **fieldnames, int *varnos, int *nfields, int * INTO keyword. If it is into_user_defined_variable_list_clause return true. */ static bool -read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict, bool bulk_collect) +read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict, int firsttoken, bool bulk_collect) { int tok; /* Set default results */ *rec = NULL; *row = NULL; + if (strict) { + if (DB_IS_CMPT(PG_FORMAT | B_FORMAT) && firsttoken == K_SELECT && SELECT_INTO_RETURN_NULL) { + *strict = false; + } else { + *strict = true; + } + } +#ifdef ENABLE_MULTIPLE_NODES if (strict) *strict = true; +#endif tok = yylex(); if (tok == '@' || tok == SET_USER_IDENT) { return true; diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index 12f1b9b7cbbeca8e9cc20a67b49574cce5a8b4b4..d57f0cb3a81a309b883057db1825912a87ba1394 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -5895,10 +5895,16 @@ static int exec_stmt_execsql(PLpgSQL_execstate* estate, PLpgSQL_stmt_execsql* st * to enforce strictness. */ if (stmt->into) { + if (!stmt->mod_stmt & !stmt->bulk_collect) { + if (!DB_IS_CMPT(PG_FORMAT | B_FORMAT) || SELECT_INTO_RETURN_NULL == 0) { + stmt->strict = true; + } + } +#ifdef ENABLE_MULTIPLE_NODES if (!stmt->mod_stmt & !stmt->bulk_collect) { stmt->strict = true; } - +#endif if (stmt->bulk_collect) { tcount = 0; } else if (stmt->strict || stmt->mod_stmt) { diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 50a3bc21649723ae2c5db4f154020a6548a251d0..02caec33b116074b6dfe9ec0c6558318d2bed6e1 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -177,7 +177,8 @@ extern bool contain_backend_version(uint32 version_number); #define OPT_PGFORMAT_SUBSTR 8388608 #define OPT_TRUNC_NUMERIC_TAIL_ZERO 16777216 #define OPT_ALLOW_ORDERBY_UNDISTINCT_COLUMN 33554432 -#define OPT_MAX 26 +#define OPT_SELECT_INTO_RETURN_NULL 67108864 +#define OPT_MAX 27 #define PLPSQL_OPT_FOR_LOOP 1 #define PLPSQL_OPT_OUTPARAM 2 @@ -216,6 +217,8 @@ extern bool contain_backend_version(uint32 version_number); #define PLSQL_COMPILE_FOR_LOOP (u_sess->utils_cxt.plsql_compile_behavior_compat_flags & PLPSQL_OPT_FOR_LOOP) #define PLSQL_COMPILE_OUTPARAM (u_sess->utils_cxt.plsql_compile_behavior_compat_flags & PLPSQL_OPT_OUTPARAM) + +#define SELECT_INTO_RETURN_NULL (u_sess->utils_cxt.behavior_compat_flags & OPT_SELECT_INTO_RETURN_NULL) /* define database compatibility Attribute */ typedef struct { diff --git a/src/test/regress/expected/b_compatibility.out b/src/test/regress/expected/b_compatibility.out index e194ad6288298dda86b87f566ce6b73cb413cf52..78fe41d3124dafdbaad9280d6707ab1b627d27fa 100644 --- a/src/test/regress/expected/b_compatibility.out +++ b/src/test/regress/expected/b_compatibility.out @@ -2462,6 +2462,32 @@ until i >p1 end repeat; raise notice '%',i; end drop function if exists dorepeat; +set behavior_compat_options='select_into_return_null'; +create table test_table_030(id int, name text); +insert into test_table_030 values(1,'a'),(2,'b'); +CREATE OR REPLACE FUNCTION select_into_null_func(canshu varchar(16)) +returns int +as $$ +DECLARE test_table_030a int; +begin + SELECT ID into test_table_030a FROM test_table_030 WHERE NAME = canshu; + return test_table_030a; +end; $$ language plpgsql; +select select_into_null_func('aaa'); + select_into_null_func +----------------------- + +(1 row) + +select select_into_null_func('aaa') is null; + ?column? +---------- + t +(1 row) + +drop table test_table_030; +drop function select_into_null_func; +reset behavior_compat_options; \c regression drop database b; DROP USER test_c; diff --git a/src/test/regress/expected/function.out b/src/test/regress/expected/function.out index 3c2c9e6751bc58e4bdf9bcfb8ce53a325a0fab91..871e0bbf03379438bc7832d4de262a5d6fa00f63 100644 --- a/src/test/regress/expected/function.out +++ b/src/test/regress/expected/function.out @@ -1617,5 +1617,31 @@ DROP FUNCTION func_increment_sql_1; DROP FUNCTION func_increment_sql_2; DROP FUNCTION fun_test_1; DROP FUNCTION fun_test_2; +set behavior_compat_options='select_into_return_null'; +create table test_table_030(id int, name text); +insert into test_table_030 values(1,'a'),(2,'b'); +CREATE OR REPLACE FUNCTION select_into_null_func(canshu varchar(16)) +returns int +as $$ +DECLARE test_table_030a int; +begin + SELECT ID into test_table_030a FROM test_table_030 WHERE NAME = canshu; + return test_table_030a; +end; $$ language plpgsql; +select select_into_null_func('aaa'); + select_into_null_func +----------------------- + +(1 row) + +select select_into_null_func('aaa') is null; + ?column? +---------- + t +(1 row) + +drop table test_table_030; +drop function select_into_null_func; +reset behavior_compat_options; \c regression; drop database IF EXISTS pl_test_funcion; diff --git a/src/test/regress/sql/b_compatibility.sql b/src/test/regress/sql/b_compatibility.sql index 305ab4a2e4cef2452ae1e30a956d5a70033117e4..c0cff721d6505dafce12f68b322f92e46feed6b5 100644 --- a/src/test/regress/sql/b_compatibility.sql +++ b/src/test/regress/sql/b_compatibility.sql @@ -1481,6 +1481,25 @@ end; / drop function if exists dorepeat; + +set behavior_compat_options='select_into_return_null'; +create table test_table_030(id int, name text); +insert into test_table_030 values(1,'a'),(2,'b'); +CREATE OR REPLACE FUNCTION select_into_null_func(canshu varchar(16)) +returns int +as $$ +DECLARE test_table_030a int; +begin + SELECT ID into test_table_030a FROM test_table_030 WHERE NAME = canshu; + return test_table_030a; +end; $$ language plpgsql; +select select_into_null_func('aaa'); +select select_into_null_func('aaa') is null; + +drop table test_table_030; +drop function select_into_null_func; +reset behavior_compat_options; + \c regression drop database b; DROP USER test_c; diff --git a/src/test/regress/sql/function.sql b/src/test/regress/sql/function.sql index a733cfe8dc28eb3e33a0bb2990d69a8e519e8d0c..4968cf482852c7b0cb4c3a801eb72a4b9b035237 100644 --- a/src/test/regress/sql/function.sql +++ b/src/test/regress/sql/function.sql @@ -934,5 +934,22 @@ DROP FUNCTION func_increment_sql_2; DROP FUNCTION fun_test_1; DROP FUNCTION fun_test_2; +set behavior_compat_options='select_into_return_null'; +create table test_table_030(id int, name text); +insert into test_table_030 values(1,'a'),(2,'b'); +CREATE OR REPLACE FUNCTION select_into_null_func(canshu varchar(16)) +returns int +as $$ +DECLARE test_table_030a int; +begin + SELECT ID into test_table_030a FROM test_table_030 WHERE NAME = canshu; + return test_table_030a; +end; $$ language plpgsql; +select select_into_null_func('aaa'); +select select_into_null_func('aaa') is null; + +drop table test_table_030; +drop function select_into_null_func; +reset behavior_compat_options; \c regression; drop database IF EXISTS pl_test_funcion;