diff --git a/src/common/backend/catalog/system_views.sql b/src/common/backend/catalog/system_views.sql index a33c6993d08fdaba541a7b50f74ff13a8ce4202d..c3b8625efca0c0985bff6fcff9e46a8779262b8d 100644 --- a/src/common/backend/catalog/system_views.sql +++ b/src/common/backend/catalog/system_views.sql @@ -2328,11 +2328,16 @@ returns text AS '$libdir/plpgsql','regexp_substr' LANGUAGE C STRICT IMMUTABLE NOT FENCED; -CREATE OR REPLACE FUNCTION pg_catalog.report_application_error( - IN log text, - IN code integer default null +CREATE OR REPLACE FUNCTION pg_catalog.raise_application_error( + IN code integer, + IN log text default null )RETURNS void -AS '$libdir/plpgsql','report_application_error' +AS '$libdir/plpgsql','raise_application_error' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE FUNCTION pg_catalog.format_error_backtrace() +RETURNS text +AS '$libdir/plpgsql','format_error_backtrace' LANGUAGE C VOLATILE NOT FENCED; create or replace function pg_catalog.bitand(bigint,bigint) diff --git a/src/common/backend/utils/error/elog.cpp b/src/common/backend/utils/error/elog.cpp index 31ee6fdbb6d6a11215b641a37ddf271aab53fd4f..4d0f642278fdc9ad4a7f5d640291515bffdc3215 100644 --- a/src/common/backend/utils/error/elog.cpp +++ b/src/common/backend/utils/error/elog.cpp @@ -171,6 +171,7 @@ static void eraseSingleQuotes(char* query_string); static int output_backtrace_to_log(StringInfoData* pOutBuf); static void write_asp_chunks(char *data, int len, bool end); static void write_asplog(char *data, int len, bool end); +static void MallocErrorBacktrace(); const char* password_mask = "********"; #define MASK_OBS_PATH() \ @@ -552,6 +553,10 @@ void errfinish(int dummy, ...) */ oldcontext = MemoryContextSwitchTo(ErrorContext); + if (elevel >= ERROR) { + MallocErrorBacktrace(); + } + /* * Call any context callback functions. Errors occurring in callback * functions will be treated as recursive errors --- this ensures we will @@ -6352,3 +6357,20 @@ void getDiagnosticsInfo(List* condInfo, bool hasCondNum, List* condNum) FreeStringInfo(&buf); copyErrorDataArea(u_sess->dolphin_errdata_ctx.lastErrorDataArea, u_sess->dolphin_errdata_ctx.errorDataArea); } + +static void MallocErrorBacktrace() +{ + if (unlikely(u_sess->plsql_cxt.error_backtrace_cxt == NULL)) { + u_sess->plsql_cxt.error_backtrace_cxt = AllocSetContextCreate(u_sess->top_mem_cxt, + "error backtrace context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + MemoryContext oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.error_backtrace_cxt); + u_sess->plsql_cxt.error_backtrace = makeStringInfo(); + MemoryContextSwitchTo(oldCxt); + } else { + if (u_sess->plsql_cxt.error_backtrace != NULL) + resetStringInfo(u_sess->plsql_cxt.error_backtrace); + } +} \ No newline at end of file diff --git a/src/common/backend/utils/fmgr/fmgr.cpp b/src/common/backend/utils/fmgr/fmgr.cpp index 59c49474e22eeb5eaa42ff6bb80a27aabc9996e8..282209cfaadcba9c380713dcd730b2b2dc82692f 100755 --- a/src/common/backend/utils/fmgr/fmgr.cpp +++ b/src/common/backend/utils/fmgr/fmgr.cpp @@ -150,13 +150,14 @@ typedef struct { /* notice: order by ascii code */ static RegExternFunc plpgsql_function_table[] = { + {"format_error_backtrace", format_error_backtrace}, {"intervaltonum", intervaltonum}, {"plpgsql_call_handler", plpgsql_call_handler}, {"plpgsql_inline_handler", plpgsql_inline_handler}, {"plpgsql_validator", plpgsql_validator}, + {"raise_application_error", raise_application_error}, {"rawtohex", rawtohex}, - {"regexp_substr", regexp_substr}, - {"report_application_error", report_application_error}, + {"regexp_substr", regexp_substr} }; /* diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index f074af450ef2cb1b25a1fcf35ec1220668604409..5b04164921fc47e3ae6055576372f8bfb2b5ab81 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -77,7 +77,7 @@ bool will_shutdown = false; * ********************************************/ -const uint32 GRAND_VERSION_NUM = 93000; +const uint32 GRAND_VERSION_NUM = 93001; /******************************************** * 2.VERSION NUM FOR EACH FEATURE diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index 413cba7aee93fe1fe44d19a10da63f0e1657aefd..97152908eac04f368a2383bb4e550c661f68ca4e 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -2634,23 +2634,73 @@ HeapTuple plpgsql_exec_trigger(PLpgSQL_function* func, TriggerData* trigdata) return rettup; } +static void plpgsql_add_error_backtrace(PLpgSQL_execstate* estate) +{ + char sqlstate[64] = {0}; + char funcname[128] = {0}; + char *nspname = NULL; + ErrorData* edata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth]; + PLpgSQL_function *func = estate->func; + + if (func->fn_searchpath != NULL && + u_sess->plsql_cxt.error_backtrace != NULL && + OidIsValid(func->fn_oid)) { + errno_t rc = snprintf_s(sqlstate, sizeof(sqlstate), sizeof(sqlstate) - 1, + "%s", plpgsql_get_sqlstate(edata->sqlerrcode)); + + securec_check_ss(rc, "", ""); + + Oid nspid = get_func_namespace(func->fn_oid); + if (OidIsValid(nspid)) + nspname = get_namespace_name(nspid); + + if (nspname == NULL) { + return; + } + + rc = strncpy_s(funcname, sizeof(funcname), func->fn_signature, sizeof(funcname)-1); + securec_check_ss(rc, "", ""); + + /* delete the () */ + if (strlen(funcname) >= 2) { + funcname[strlen(funcname) - 2] = '\0'; + } + + if (estate->err_stmt != NULL) { + appendStringInfo(u_sess->plsql_cxt.error_backtrace, "%s: at \"%s.%s\", line %d\n", + sqlstate, + nspname, + funcname, + estate->err_stmt->lineno); + } else { + appendStringInfo(u_sess->plsql_cxt.error_backtrace, "%s: at \"%s.%s\"\n", + sqlstate, + nspname, + funcname); + } + } +} + /* * error context callback to let us supply a call-stack traceback */ static void plpgsql_exec_error_callback(void* arg) { + int elevel; + int sqlState; PLpgSQL_execstate* estate = (PLpgSQL_execstate*)arg; + getElevelAndSqlstate(&elevel, &sqlState); + + if (elevel >= ERROR) + plpgsql_add_error_backtrace(estate); + /* if we are doing RAISE, don't report its location */ if (estate->err_text == raise_skip_msg) { return; } if (AUDIT_EXEC_ENABLED) { - int elevel; - int sqlState; - - getElevelAndSqlstate(&elevel, &sqlState); if (elevel >= ERROR && estate->func->fn_oid >= FirstNormalObjectId) { errno_t ret = EOK; char details[PGAUDIT_MAXLENGTH] = {0}; diff --git a/src/common/pl/plpgsql/src/plsql_packages.cpp b/src/common/pl/plpgsql/src/plsql_packages.cpp index 54294bfd310f4bb3a7229920a8d5739b9ba4db59..224dd81f8cb9d8326aec28b7f85597ce1d14c692 100644 --- a/src/common/pl/plpgsql/src/plsql_packages.cpp +++ b/src/common/pl/plpgsql/src/plsql_packages.cpp @@ -39,17 +39,20 @@ PG_MODULE_MAGIC; extern Datum textregexsubstr_enforce_a(PG_FUNCTION_ARGS); extern Datum regexp_substr(PG_FUNCTION_ARGS); extern Datum intervaltonum(PG_FUNCTION_ARGS); -extern Datum report_application_error(PG_FUNCTION_ARGS); +extern Datum raise_application_error(PG_FUNCTION_ARGS); +extern Datum format_error_backtrace(PG_FUNCTION_ARGS); extern "C" { Datum regexp_substr(PG_FUNCTION_ARGS); Datum intervaltonum(PG_FUNCTION_ARGS); -Datum report_application_error(PG_FUNCTION_ARGS); +Datum raise_application_error(PG_FUNCTION_ARGS); +Datum format_error_backtrace(PG_FUNCTION_ARGS); } PG_FUNCTION_INFO_V1(regexp_substr); PG_FUNCTION_INFO_V1(intervaltonum); -PG_FUNCTION_INFO_V1(report_application_error); +PG_FUNCTION_INFO_V1(raise_application_error); +PG_FUNCTION_INFO_V1(format_error_backtrace); // Convert interval(day) to numeric Datum intervaltonum(PG_FUNCTION_ARGS) @@ -90,35 +93,31 @@ Datum rawtohex(PG_FUNCTION_ARGS) } /* report self-defined error: -20999 <= errorcode <= -20000 */ -Datum report_application_error(PG_FUNCTION_ARGS) +Datum raise_application_error(PG_FUNCTION_ARGS) { -#if ((!defined(ENABLE_MULTIPLE_NODES)) && (!defined(ENABLE_PRIVATEGAUSS))) - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); -#endif - char* log = NULL; - int errnum = 0; - - if (PG_GETARG_DATUM(0) == 0) { - log = ""; + const char* log = PG_GETARG_DATUM(1) == 0 ? "" : text_to_cstring(PG_GETARG_TEXT_P(1)); + const int errnum = PG_ARGISNULL(0) ? 0 : PG_GETARG_INT32(0); + int maxCustomNum = -20000; + int minCustomNum = -20999; + if ((errnum > maxCustomNum) || (errnum < minCustomNum)) { + ereport( ERROR, (errcode( ERRCODE_RAISE_EXCEPTION ), + errmsg("custom error code must be between -20000 and -20999"))); } else { - log = text_to_cstring(PG_GETARG_TEXT_P(0)); - } + ereport( ERROR, (errcode( ERRCODE_RAISE_EXCEPTION ), errmsg("ORA%d: %s", errnum, log))); + } - if (PG_ARGISNULL(1)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s", log))); - } else { - errnum = PG_GETARG_INT32(1); - if ((errnum > -20000) || (errnum < -20999)) { - ereport( - ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("custom error code must be between -20000 and -20999"))); - } else { - ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("ORA%d: %s", errnum, log))); - } + PG_RETURN_TEXT_P(cstring_to_text(u_sess->plsql_cxt.error_backtrace->data)); +} + +Datum format_error_backtrace(PG_FUNCTION_ARGS) +{ + if (u_sess->plsql_cxt.error_backtrace != NULL) { + PG_RETURN_TEXT_P(cstring_to_text(u_sess->plsql_cxt.error_backtrace->data)); } - - PG_RETURN_VOID(); + PG_RETURN_TEXT_P(cstring_to_text("")); } + Datum regexp_substr(PG_FUNCTION_ARGS) { Oid collation = PG_GET_COLLATION(); diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index 3cc387b5ad550f0587aab11e7155e3d494847ab1..7c75eddd1e2e539202be3e91f844a4d1fb764977 100755 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -882,6 +882,8 @@ static void knl_u_plpgsql_init(knl_u_plpgsql_context* plsql_cxt) plsql_cxt->isCreatePkg = false; plsql_cxt->isCreatePkgFunction = false; plsql_cxt->currCompilingObjStatus = true; + plsql_cxt->error_backtrace_cxt = NULL; + plsql_cxt->error_backtrace = NULL; plsql_cxt->need_init = true; plsql_cxt->parallel_cursor_arg_name = NULL; } diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_93_001.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_93_001.sql new file mode 100644 index 0000000000000000000000000000000000000000..bf987bbd73ab258462c6d9d888db6e18d33a2e64 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_93_001.sql @@ -0,0 +1,9 @@ +DROP FUNCTION IF EXISTS pg_catalog.raise_application_error() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.format_error_backtrace() CASCADE; + +CREATE OR REPLACE FUNCTION pg_catalog.report_application_error( + IN log text, + IN code integer default null +)RETURNS void +AS '$libdir/plpgsql','report_application_error' +LANGUAGE C VOLATILE NOT FENCED; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_93_001.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_93_001.sql new file mode 100644 index 0000000000000000000000000000000000000000..bf987bbd73ab258462c6d9d888db6e18d33a2e64 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_93_001.sql @@ -0,0 +1,9 @@ +DROP FUNCTION IF EXISTS pg_catalog.raise_application_error() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.format_error_backtrace() CASCADE; + +CREATE OR REPLACE FUNCTION pg_catalog.report_application_error( + IN log text, + IN code integer default null +)RETURNS void +AS '$libdir/plpgsql','report_application_error' +LANGUAGE C VOLATILE NOT FENCED; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_93_001.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_93_001.sql new file mode 100644 index 0000000000000000000000000000000000000000..db970d7281534cf854a4ddddfe4f3a6ac119d369 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_93_001.sql @@ -0,0 +1,13 @@ +DROP FUNCTION IF EXISTS pg_catalog.report_application_error() CASCADE; + +CREATE OR REPLACE FUNCTION pg_catalog.raise_application_error( + IN code integer, + IN log text default null +)RETURNS void +AS '$libdir/plpgsql','raise_application_error' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE FUNCTION pg_catalog.format_error_backtrace() +RETURNS text +AS '$libdir/plpgsql','format_error_backtrace' +LANGUAGE C VOLATILE NOT FENCED; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_93_001.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_93_001.sql new file mode 100644 index 0000000000000000000000000000000000000000..db970d7281534cf854a4ddddfe4f3a6ac119d369 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_93_001.sql @@ -0,0 +1,13 @@ +DROP FUNCTION IF EXISTS pg_catalog.report_application_error() CASCADE; + +CREATE OR REPLACE FUNCTION pg_catalog.raise_application_error( + IN code integer, + IN log text default null +)RETURNS void +AS '$libdir/plpgsql','raise_application_error' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE FUNCTION pg_catalog.format_error_backtrace() +RETURNS text +AS '$libdir/plpgsql','format_error_backtrace' +LANGUAGE C VOLATILE NOT FENCED; \ No newline at end of file diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 0f078c1447d44034aee2e3cb232e1006fcd72340..e992299c67270c5d33d73d3b0e710cc61c9c5714 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -1734,6 +1734,8 @@ typedef struct knl_u_plpgsql_context { int compile_check_node_level; int real_func_num; HTAB* plpgsql_lock_objects; + MemoryContext error_backtrace_cxt; + StringInfoData *error_backtrace; /* save error backtrace */ bool need_init; char* parallel_cursor_arg_name; diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index c13dd268287ef1de45062fe634c285cfae29e5b1..bb5bdaf95cad4437ca46bb642715d8f50f0ac988 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -1863,7 +1863,8 @@ extern "C" { Datum regexp_substr(PG_FUNCTION_ARGS); Datum intervaltonum(PG_FUNCTION_ARGS); Datum rawtohex(PG_FUNCTION_ARGS); -Datum report_application_error(PG_FUNCTION_ARGS); +Datum raise_application_error(PG_FUNCTION_ARGS); +Datum format_error_backtrace(PG_FUNCTION_ARGS); } extern THR_LOCAL PLpgSQL_execstate* plpgsql_estate; diff --git a/src/test/regress/expected/format_error_backtrace.out b/src/test/regress/expected/format_error_backtrace.out new file mode 100644 index 0000000000000000000000000000000000000000..a0f95a39d53c215c96be24568a57e6a09efddc46 --- /dev/null +++ b/src/test/regress/expected/format_error_backtrace.out @@ -0,0 +1,143 @@ +create SCHEMA schema_for_test_format_error_backtrace; +set current_schema = schema_for_test_format_error_backtrace; +--case1: format_error_backtrace save the error stack messages +Create or replace procedure proc1 is +Begin +raise NOTICE 'running proc1'; +Raise no_data_found; +End; +/ +create or replace procedure proc2 is +begin +raise NOTICE 'calling proc1'; +raise NOTICE '---------------'; +proc1; +end; +/ +create or replace procedure proc3 is +begin +raise NOTICE 'calling proc2'; +proc2; +exception +when no_data_found +then +raise NOTICE 'error stack at top level'; +raise NOTICE '%', format_error_backtrace(); +end; +/ +begin +raise NOTICE 'proc3->proc2->proc1 backtrace'; +proc3; +end; +/ +NOTICE: proc3->proc2->proc1 backtrace +NOTICE: calling proc2 +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: calling proc1 +CONTEXT: SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: --------------- +CONTEXT: SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: running proc1 +CONTEXT: SQL statement "CALL proc1()" +PL/pgSQL function proc2() line 4 at PERFORM +SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: error stack at top level +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 + +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +--case2: format_error_backtrace can be as the parameter of function +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'ddd') is +begin + raise NOTICE 'test_bt_a'; + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ +declare + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ +NOTICE: test_bt_a +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 + +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +NOTICE: ddd +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +--case3: format_error_backtrace can be as the parameter of function, and save error stack messages +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'aaa') is +begin + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ +create procedure test_bt_b is + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ +create procedure test_bt_c is + a int; +begin +test_bt_b; +end; +/ +begin + test_bt_c; +end; +/ +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 +22P02: at "schema_for_test_format_error_backtrace.test_bt_b", line 3 +22P02: at "schema_for_test_format_error_backtrace.test_bt_c", line 3 + +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function test_bt_b() line 6 at PERFORM +SQL statement "CALL test_bt_b()" +PL/pgSQL function test_bt_c() line 3 at PERFORM +SQL statement "CALL test_bt_c()" +PL/pgSQL function inline_code_block line 2 at PERFORM +NOTICE: aaa +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function test_bt_b() line 6 at PERFORM +SQL statement "CALL test_bt_b()" +PL/pgSQL function test_bt_c() line 3 at PERFORM +SQL statement "CALL test_bt_c()" +PL/pgSQL function inline_code_block line 2 at PERFORM +drop procedure test_bt_c; +drop procedure test_bt_b; +drop procedure test_bt_a; +drop procedure proc3; +drop procedure proc2; +drop procedure proc1; +drop schema schema_for_test_format_error_backtrace; \ No newline at end of file diff --git a/src/test/regress/expected/raise_application_error.out b/src/test/regress/expected/raise_application_error.out new file mode 100644 index 0000000000000000000000000000000000000000..cac3dca33f88010b9c44762563abbe92df4b9b3a --- /dev/null +++ b/src/test/regress/expected/raise_application_error.out @@ -0,0 +1,132 @@ +create SCHEMA schema_for_test_raise_application_error; +set current_schema = schema_for_test_raise_application_error; +--case1: Stored Procedures Called in Normal Flow +create or replace procedure a1 is + a int; +begin + null; + raise_application_error(-20001, 'dfff'); +end; +/ +begin + a1; +end; +/ +ERROR: ORA-20001: dfff +CONTEXT: SQL statement "CALL raise_application_error(-20001,'dfff')" +PL/pgSQL function a1() line 4 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case2: Stored Procedures Called in Normal Flow, Printing the call stack and error messages +create or replace procedure a1 is + a int; + b int; +begin + raise_application_error(-20015, 'abcc'); +end; +/ +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ +begin +a3; +end; +/ +ERROR: ORA-20015: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20015,'abcc')" +PL/pgSQL function a1() line 4 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function a2(numeric,character varying) line 4 at PERFORM +SQL statement "CALL a2(tt)" +PL/pgSQL function a3() line 6 at PERFORM +SQL statement "CALL a3()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case3: Stored procedure exception process call +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ +begin + a1; +end; +/ +ERROR: ORA-20002: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20002,'abcc')" +PL/pgSQL function a1() line 6 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case4: Calling on a procedure exception,Printing the Call Stack and Error Messages +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ +begin + a3; +end; +/ +ERROR: ORA-20002: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20002,'abcc')" +PL/pgSQL function a1() line 6 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function a2(numeric,character varying) line 4 at PERFORM +SQL statement "CALL a2(tt)" +PL/pgSQL function a3() line 6 at PERFORM +SQL statement "CALL a3()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case5: Allow select to be called directly +select raise_application_error(-20002, 'abcc'); +ERROR: ORA-20002: abcc +CONTEXT: referenced column: raise_application_error +--case6: NULL parameter test +select raise_application_error(NULL, 'abcc'); +ERROR: custom error code must be between -20000 and -20999 +CONTEXT: referenced column: raise_application_error +select raise_application_error(-20002, NULL); +ERROR: ORA-20002: +CONTEXT: referenced column: raise_application_error +select raise_application_error(NULL, NULL); +ERROR: custom error code must be between -20000 and -20999 +CONTEXT: referenced column: raise_application_error +drop procedure a1; +drop procedure a2; +drop procedure a3; +drop schema schema_for_test_raise_application_error; \ No newline at end of file diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 921478c21b86c54085efe45ca40850222ac45edd..002a361379e8aecfa03144ad35cc07b3ad0df3be 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -224,7 +224,7 @@ test: single_node_vacuum # Believe it or not, select creates a table, subsequent # tests need. # ---------- -test: single_node_errors +test: single_node_errors raise_application_error format_error_backtrace #test: single_node_select ignore: single_node_random diff --git a/src/test/regress/sql/format_error_backtrace.sql b/src/test/regress/sql/format_error_backtrace.sql new file mode 100644 index 0000000000000000000000000000000000000000..ebef6424f1cd52a797f5c1f40cdcb427953a686e --- /dev/null +++ b/src/test/regress/sql/format_error_backtrace.sql @@ -0,0 +1,93 @@ +create SCHEMA schema_for_test_format_error_backtrace; +set current_schema = schema_for_test_format_error_backtrace; + +--case1: format_error_backtrace save the error stack messages +Create or replace procedure proc1 is +Begin +raise NOTICE 'running proc1'; +Raise no_data_found; +End; +/ + +create or replace procedure proc2 is +begin +raise NOTICE 'calling proc1'; +raise NOTICE '---------------'; +proc1; +end; +/ + +create or replace procedure proc3 is +begin +raise NOTICE 'calling proc2'; +proc2; +exception +when no_data_found +then +raise NOTICE 'error stack at top level'; +raise NOTICE '%', format_error_backtrace(); +end; +/ + +begin +raise NOTICE 'proc3->proc2->proc1 backtrace'; +proc3; +end; +/ + +--case2: format_error_backtrace can be as the parameter of function +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'ddd') is +begin + raise NOTICE 'test_bt_a'; + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ + +declare + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ + +--case3: format_error_backtrace can be as the parameter of function, and save error stack messages +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'aaa') is +begin + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ + +create procedure test_bt_b is + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ + +create procedure test_bt_c is + a int; +begin +test_bt_b; +end; +/ + +begin + test_bt_c; +end; +/ + +drop procedure test_bt_c; +drop procedure test_bt_b; +drop procedure test_bt_a; +drop procedure proc3; +drop procedure proc2; +drop procedure proc1; +drop schema schema_for_test_format_error_backtrace; \ No newline at end of file diff --git a/src/test/regress/sql/raise_application_error.sql b/src/test/regress/sql/raise_application_error.sql new file mode 100644 index 0000000000000000000000000000000000000000..b88b902eeeb3958be43f262956429c710ef7dd10 --- /dev/null +++ b/src/test/regress/sql/raise_application_error.sql @@ -0,0 +1,124 @@ +create SCHEMA schema_for_test_raise_application_error; +set current_schema = schema_for_test_raise_application_error; + +--case1: Stored Procedures Called in Normal Flow +create or replace procedure a1 is + a int; +begin + null; + raise_application_error(-20001, 'dfff'); +end; +/ + + +begin + a1; +end; +/ + + +--case2: Stored Procedures Called in Normal Flow, Printing the call stack and error messages +create or replace procedure a1 is + a int; + b int; +begin + raise_application_error(-20015, 'abcc'); +end; +/ + + +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ + + +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ + + +begin +a3; +end; +/ + + +--case3: Stored procedure exception process call +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ + + +begin + a1; +end; +/ + + +--case4: Calling on a procedure exception,Printing the Call Stack and Error Messages +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ + + +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ + + +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ + + +begin + a3; +end; +/ + + +--case5: Allow select to be called directly +select raise_application_error(-20002, 'abcc'); + + +--case6: NULL parameter test +select raise_application_error(NULL, 'abcc'); +select raise_application_error(-20002, NULL); +select raise_application_error(NULL, NULL); + +drop procedure a1; +drop procedure a2; +drop procedure a3; +drop schema schema_for_test_raise_application_error;