From 0db2ff5f9ee318197173847d79af7cccb8f09b4b Mon Sep 17 00:00:00 2001 From: JiaoShuntian Date: Tue, 27 Oct 2026 17:11:56 +0800 Subject: [PATCH] docs: add bilingual DBMS_UTILITY documentation --- .../dbms_utility_impl.adoc | 107 +++++++++++++ .../compat_dbms_utility.adoc | 140 ++++++++++++++++++ .../dbms_utility_impl.adoc | 107 +++++++++++++ .../compat_dbms_utility.adoc | 140 ++++++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 CN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc create mode 100644 CN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc create mode 100644 EN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc create mode 100644 EN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc diff --git a/CN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc b/CN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc new file mode 100644 index 00000000..21f12a40 --- /dev/null +++ b/CN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc @@ -0,0 +1,107 @@ +:sectnums: +:sectnumlevels: 5 + += DBMS_UTILITY 包设计 + +== 目的 + +通过 `ivorysql_ora` 中的三个包函数,读取 PL/iSQL 当前异常和调用上下文,并生成 Oracle 风格的诊断文本:`FORMAT_ERROR_BACKTRACE`、`FORMAT_ERROR_STACK` 和 `FORMAT_CALL_STACK`。 + +实现由包接口、C 格式转换和 PL/iSQL 上下文接口组成。使用示例见 xref:master/oracle_compatibility/compat_dbms_utility.adoc[DBMS_UTILITY 包]。 + +== 实现说明 + +=== 代码组织 + +包定义和 C 实现位于 `contrib/ivorysql_ora/src/builtin_packages/dbms_utility/`;PL/iSQL 文件位于 `src/pl/plisql/src/`。回归 SQL 和期望输出分别位于扩展的 `sql/`、`expected/` 目录。下表只列文件名。 + +[cols="3,4",options="header"] +|=== +|文件 |职责 +|`dbms_utility--1.0.sql` |注册 C 入口,定义包规范和包体 +|`dbms_utility.c` |动态查找上下文接口,转换错误位置、错误消息和调用栈 +|`pl_exec.c` |维护异常上下文,导出诊断接口,构造包成员限定名 +|`plisql.h` |声明并导出 PL/iSQL 诊断接口 +|`dbms_utility.sql` |异常、调用栈、上下文恢复和名称显示的回归测试 +|`dbms_utility.out` |对应诊断输出及校验结果 +|=== + +扩展的 `Makefile`、`meson.build` 和 `ivorysql_ora_merge_sqls` 将包的 C 模块、SQL 脚本及回归测试纳入构建和安装流程。 + +[source,text] +---- +DBMS_UTILITY.FORMAT_ERROR_BACKTRACE + → sys.ora_format_error_backtrace() + → PL/iSQL 异常上下文 API + → ORA-06512 位置文本 + +FORMAT_ERROR_STACK → 异常消息及 SQLSTATE API → 错误号与消息 +FORMAT_CALL_STACK → 当前 PL/iSQL 栈 API → 调用栈表头及栈帧 +---- + +=== 包接口与动态查找 + +包规范中的三个函数均无参数,返回 `text`;包体分别转调同名职责的 `sys.ora_format_*` C 函数。C 入口标记为 `VOLATILE`,因为结果随当前执行和异常上下文变化。 + +C 模块通过 `dlsym(RTLD_DEFAULT, ...)` 查找已加载的 PL/iSQL 模块导出符号,避免建立到 `plisql.so` 的链接期依赖。首次查找结果缓存在后端静态函数指针中,后续调用复用;当前实现只尝试查找一次。 + +四个 PL/iSQL 接口分别提供: + +* `plisql_get_current_exception_context()`:当前异常的上下文文本。 +* `plisql_get_current_exception_message()`:当前异常的原始消息。 +* `plisql_get_current_exception_sqlerrcode()`:当前异常的内部 SQLSTATE 编码,无异常时为 0。 +* `plisql_get_call_stack()`:当前 PL/iSQL 栈,每帧包含句柄、行号和签名。 + +接口无法取得时,相应包函数返回 `NULL`。异常消息和上下文由 PL/iSQL 持有;调用栈接口返回新分配的字符串,由使用方处理完后释放。 + +=== 异常上下文保存与恢复 + +`pl_exec.c` 使用 `exception_handling_estate` 指向正在处理异常的执行状态。进入匹配的异常处理器时保存原指针并切换到当前状态,处理器正常执行结束后恢复原值。 + +诊断函数虽然在另一次包函数调用中执行,仍可通过该指针读取异常处理器的 `cur_error`,而不是诊断函数自身的执行状态。内层异常被处理并正常返回后,外层异常上下文恢复,使前后两次读取外层回溯得到相同结果。 + +=== FORMAT_ERROR_BACKTRACE + +函数读取异常上下文并逐行转换。`transform_and_append_line()` 识别 `PL/iSQL function` 和 `PL/pgSQL function` 行,提取名称及 `line N`;跳过 `SQL statement` 和其他无法识别的行。 + +命名子程序输出 `ORA-06512: at "对象名", line N`;匿名块输出 `ORA-06512: at line N`。多行按原异常上下文的顺序拼接,形成从异常发生处到外层调用者的回溯。 + +包成员的名称由 PL/iSQL 根据包目录信息构造为模式、包、子程序限定名。格式转换检测名称是否已经含有限定部分,避免重复添加模式;未限定的独立子程序仍回退到 `public`,没有在该转换函数中查询其真实所属模式。 + +=== FORMAT_ERROR_STACK + +函数读取当前异常消息和 SQLSTATE,通过 `sqlstate_to_ora_errnum()` 选择错误号,并生成 `ORA-XXXXX: 原始消息`,末尾添加换行。不自动附加位置回溯,也不重新翻译原始消息。 + +[cols="3,2",options="header"] +|=== +|异常类型 |输出错误号 +|除零 |`ORA-01476` +|无数据或未找到数据 |`ORA-01403` +|返回行数过多 |`ORA-01422` +|不允许 NULL |`ORA-06502` +|无效游标状态 |`ORA-01001` +|`RAISE EXCEPTION` |`ORA-06510` +|其他 SQLSTATE |`ORA-06502` +|=== + +该映射保留常见错误分类,不能仅凭通用错误号区分全部底层异常。无异常消息或 SQLSTATE 为 0 时返回 `NULL`。 + +=== FORMAT_CALL_STACK + +`plisql_get_call_stack()` 遍历 `error_context_stack`,只收集回调为 `plisql_exec_error_callback` 的帧。每帧包含执行对象的地址、当前行号和签名,包成员使用限定名。 + +C 格式转换跳过诊断函数自身的第一个栈帧,将其余帧排在固定的 `PL/SQL Call Stack` 表头后,并统一使用 `function` 标签。没有可显示的调用者帧时返回 `NULL`。这条路径读取当前执行栈,与保存在 `cur_error->context` 中的异常路径不同。 + +=== 标识符显示 + +回溯和调用栈通过 `apply_identifier_case_switch()` 处理名称:未启用大小写切换或模式为 `normal` 时保留原文本;`lowercase` 将全大写文本转为小写;`interchange` 交换全大写和全小写文本,混合大小写保持不变。 + +该处理作用于取得的名称文本,显示结果受会话配置影响。调用栈句柄来自运行时地址,不能视为持久对象标识。 + +== 测试验证 + +`dbms_utility` 回归组覆盖基础异常、多层调用、函数和匿名块、重新抛出、包成员及模式限定名、嵌套异常上下文恢复、错误号映射、无异常返回值、调用栈结构及三个函数组合使用。 + +当前 master 的用例还核对非 `public` 模式中的包成员限定名,防止缺失包名或重复添加 `PUBLIC`。调用栈地址随运行变化,测试检查表头、名称和帧数量,不固定地址值。 + +在已配置、编译并安装的当前 master 源码目录中,可运行 `make -C contrib/ivorysql_ora oracle-installcheck ORA_REGRESS=dbms_utility`。目标实例需具备对应版本的 PL/iSQL 和 `ivorysql_ora`。 diff --git a/CN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc b/CN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc new file mode 100644 index 00000000..8324bc9e --- /dev/null +++ b/CN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc @@ -0,0 +1,140 @@ +:sectnums: +:sectnumlevels: 5 + += DBMS_UTILITY 包 + +== 目的 + +`DBMS_UTILITY` 提供错误消息、异常位置和当前调用栈的格式化接口,便于在 PL/iSQL 程序中记录诊断信息、定位异常发生位置。 + +== 功能说明 + +三个函数均无参数,返回类型为 `text`: + +[cols="3,4",options="header"] +|=== +|函数 |功能 +|`FORMAT_ERROR_BACKTRACE` |返回当前被处理异常的发生位置及传播路径,以 `ORA-06512` 行表示 +|`FORMAT_ERROR_STACK` |返回映射后的 `ORA-XXXXX` 错误号和原始错误消息 +|`FORMAT_CALL_STACK` |返回调用时仍在执行的 PL/iSQL 调用栈,不要求发生异常 +|=== + +在 PL/iSQL 中可直接将函数结果赋给变量: + +[source,sql] +---- +v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; +v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; +v_call_stack := DBMS_UTILITY.FORMAT_CALL_STACK; +---- + +=== 异常位置与错误消息 + +`FORMAT_ERROR_BACKTRACE` 描述异常发生时的调用路径;`FORMAT_ERROR_STACK` 描述当前异常的错误号和消息,当前实现不自动将位置回溯附加到错误消息中。记录异常时可同时保存二者。 + +没有正在处理的异常时,两个错误诊断函数返回 `NULL`。外层异常处理器调用带有自己异常处理器的子程序后,子程序正常返回时,外层异常上下文恢复,仍可读取原异常信息。 + +=== 当前调用栈 + +`FORMAT_CALL_STACK` 返回调用时的 PL/iSQL 栈,包含固定表头、对象句柄、行号和名称。异常发生后调用它,看到的是当前仍在执行的栈,不是已经退出的异常发生路径;异常路径应使用 `FORMAT_ERROR_BACKTRACE`。 + +对象句柄是运行时地址,会随执行环境变化。当前输出统一使用 `function` 标签表示栈帧,过程也可能使用该标签。没有可显示的调用者栈帧时返回 `NULL`。 + +== 使用示例 + +以下示例在 Oracle 兼容模式下执行,并假定调用者具备包的执行权限。为方便对照对象名,示例启用以下大小写设置,结束时恢复默认值。 + +[source,sql] +---- +SET ivorysql.enable_case_switch = true; +SET ivorysql.identifier_case_switch = interchange; +---- + +=== 同时记录错误消息与异常位置 + +[source,sql] +---- +CREATE OR REPLACE PROCEDURE public.utility_error_demo AS + v_error_stack text; + v_backtrace text; +BEGIN + RAISE EXCEPTION 'demo error'; +EXCEPTION + WHEN OTHERS THEN + v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; + v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; + RAISE INFO 'Error stack: %', v_error_stack; + RAISE INFO 'Backtrace: %', v_backtrace; +END; +/ + +CALL public.utility_error_demo(); +---- + +输出的主要内容如下;行号与过程体中的语句位置有关: + +[source,text] +---- +INFO: Error stack: ORA-06510: demo error +INFO: Backtrace: ORA-06512: at "PUBLIC.UTILITY_ERROR_DEMO", line 4 +---- + +函数只返回诊断文本,不会自动重新抛出已捕获的异常。需要继续向上传播异常时,在完成记录后执行 `RAISE;`。 + +=== 查看嵌套调用栈 + +[source,sql] +---- +CREATE OR REPLACE PROCEDURE public.utility_stack_inner AS + v_stack text; +BEGIN + v_stack := DBMS_UTILITY.FORMAT_CALL_STACK; + RAISE INFO 'Call stack: %', v_stack; +END; +/ + +CREATE OR REPLACE PROCEDURE public.utility_stack_outer AS +BEGIN + public.utility_stack_inner(); +END; +/ + +CALL public.utility_stack_outer(); +---- + +结果包含 `----- PL/SQL Call Stack -----` 表头,以及 `UTILITY_STACK_INNER`、`UTILITY_STACK_OUTER` 两个调用者的栈帧。诊断函数自身的首个栈帧被省略。 + +=== 没有正在处理的异常 + +[source,sql] +---- +DO $$ +DECLARE + v_backtrace text; + v_error_stack text; +BEGIN + v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; + v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; + RAISE INFO 'Backtrace: [%], error stack: [%]', v_backtrace, v_error_stack; +END; +$$; +---- + +这里两个返回值均为 `NULL`,psql 的 INFO 输出中显示为 ``。 + +[source,sql] +---- +DROP PROCEDURE public.utility_error_demo; +DROP PROCEDURE public.utility_stack_outer; +DROP PROCEDURE public.utility_stack_inner; +RESET ivorysql.identifier_case_switch; +RESET ivorysql.enable_case_switch; +---- + +== 输出说明 + +* 错误号是对 SQLSTATE 的部分映射,例如除零对应 `ORA-01476`,`RAISE EXCEPTION` 对应 `ORA-06510`。其他错误可能使用通用号 `ORA-06502`;诊断时应同时查看原始消息和位置回溯。 +* 对象名称的显示受 `ivorysql.enable_case_switch` 和 `ivorysql.identifier_case_switch` 影响,不应假定总是大写。 +* 包成员的栈帧名称包含模式、包和子程序;已带模式的名称不会重复添加 `PUBLIC`。未限定名的独立子程序仍可能采用 `public` 作为回溯前缀,不能仅据该前缀确认实际所属模式。 +* `FORMAT_CALL_STACK` 收集 PL/iSQL 栈帧。混合语言调用中,它不表示所有语言的完整运行栈。 +* 返回文本可以包含多行,保存完整诊断结果时宜使用 `text`,避免接收变量长度过小。PL/iSQL 上下文接口不可用时函数也可能返回 `NULL`。 diff --git a/EN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc b/EN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc new file mode 100644 index 00000000..b88f92b4 --- /dev/null +++ b/EN/modules/ROOT/pages/master/compatibility_features_design/dbms_utility_impl.adoc @@ -0,0 +1,107 @@ +:sectnums: +:sectnumlevels: 5 + += DBMS_UTILITY Package Design + +== Purpose + +Three package functions in `ivorysql_ora` read the current PL/iSQL exception and call context to produce Oracle-style diagnostic text: `FORMAT_ERROR_BACKTRACE`, `FORMAT_ERROR_STACK`, and `FORMAT_CALL_STACK`. + +The implementation combines a package interface, C formatting, and PL/iSQL context APIs. See xref:master/oracle_compatibility/compat_dbms_utility.adoc[DBMS_UTILITY Package] for usage. + +== Implementation + +=== Code organization + +The package definition and C implementation are under `contrib/ivorysql_ora/src/builtin_packages/dbms_utility/`. PL/iSQL files are under `src/pl/plisql/src/`. Regression SQL and expected output are in the extension's `sql/` and `expected/` directories. The table lists filenames only. + +[cols="3,4",options="header"] +|=== +|File |Responsibility +|`dbms_utility--1.0.sql` |Registers C entry points and defines the package specification and body +|`dbms_utility.c` |Looks up context APIs and formats exception locations, messages, and call stacks +|`pl_exec.c` |Maintains exception context, exports diagnostic APIs, and constructs qualified package member names +|`plisql.h` |Declares and exports PL/iSQL diagnostic APIs +|`dbms_utility.sql` |Tests exceptions, call stacks, context restoration, and displayed names +|`dbms_utility.out` |Expected diagnostics and verification results +|=== + +The extension's `Makefile`, `meson.build`, and `ivorysql_ora_merge_sqls` integrate the C module, SQL script, and regression tests into build and installation. + +[source,text] +---- +DBMS_UTILITY.FORMAT_ERROR_BACKTRACE + → sys.ora_format_error_backtrace() + → PL/iSQL exception context API + → ORA-06512 location text + +FORMAT_ERROR_STACK → Exception message and SQLSTATE APIs → Error number and message +FORMAT_CALL_STACK → Current PL/iSQL stack API → Stack header and frames +---- + +=== Package interface and dynamic lookup + +The three package functions take no arguments and return `text`. Their bodies call the corresponding `sys.ora_format_*` C functions. The C entry points are `VOLATILE` because results depend on the current execution and exception context. + +The C module uses `dlsym(RTLD_DEFAULT, ...)` to find symbols exported by the loaded PL/iSQL module, avoiding a link-time dependency on `plisql.so`. The first lookup results are cached in backend-static function pointers for subsequent calls. The current implementation attempts lookup only once. + +The four PL/iSQL APIs provide: + +* `plisql_get_current_exception_context()`: the current exception's context text. +* `plisql_get_current_exception_message()`: its original message. +* `plisql_get_current_exception_sqlerrcode()`: its internal SQLSTATE encoding, or 0 without an exception. +* `plisql_get_call_stack()`: current PL/iSQL frames containing handles, line numbers, and signatures. + +If an API cannot be obtained, the corresponding package function returns `NULL`. PL/iSQL owns the exception message and context. The call-stack API returns a newly allocated string that the consumer frees after use. + +=== Saving and restoring exception context + +`pl_exec.c` uses `exception_handling_estate` to identify the execution state handling an exception. On entry to a matching handler, it saves the previous pointer and switches to the current state. It restores the previous value when the handler completes normally. + +Although a diagnostic runs in another package function invocation, this pointer lets it read the handler's `cur_error` rather than its own execution state. After a nested exception is handled and the subprogram returns normally, the outer context is restored, so repeated reads of the outer backtrace agree. + +=== FORMAT_ERROR_BACKTRACE + +The function reads exception context and transforms it line by line. `transform_and_append_line()` recognizes `PL/iSQL function` and `PL/pgSQL function` lines, extracting names and `line N`. It skips `SQL statement` lines and other unrecognized formats. + +Named routines produce `ORA-06512: at "object name", line N`; anonymous blocks produce `ORA-06512: at line N`. Lines retain the original exception context order, from the exception origin toward outer callers. + +PL/iSQL constructs schema, package, and subprogram names for package members using package catalog information. Formatting detects already qualified names to avoid adding another schema. Unqualified standalone routines still fall back to `public`; this conversion function does not look up their actual owning schema. + +=== FORMAT_ERROR_STACK + +The function reads the exception message and SQLSTATE, selects a number with `sqlstate_to_ora_errnum()`, and produces `ORA-XXXXX: original message` followed by a newline. It does not automatically append a location backtrace or translate the original message. + +[cols="3,2",options="header"] +|=== +|Exception category |Output error number +|Division by zero |`ORA-01476` +|No data or no data found |`ORA-01403` +|Too many rows |`ORA-01422` +|NULL not allowed |`ORA-06502` +|Invalid cursor state |`ORA-01001` +|`RAISE EXCEPTION` |`ORA-06510` +|Other SQLSTATEs |`ORA-06502` +|=== + +The mapping preserves common categories, but the generic number cannot distinguish every underlying exception. A missing message or SQLSTATE of 0 returns `NULL`. + +=== FORMAT_CALL_STACK + +`plisql_get_call_stack()` walks `error_context_stack`, collecting only frames whose callback is `plisql_exec_error_callback`. Each frame contains the execution object's address, current line number, and signature, with qualified names for package members. + +C formatting skips the diagnostic function's own first frame, places the remaining frames after the fixed `PL/SQL Call Stack` header, and labels all of them `function`. No caller frames to display produces `NULL`. This path reads the current execution stack, separately from the exception path saved in `cur_error->context`. + +=== Displayed identifiers + +Backtraces and call stacks pass names through `apply_identifier_case_switch()`. With case switching disabled, or mode `normal`, the text is unchanged. `lowercase` downcases all-uppercase text. `interchange` swaps all-uppercase and all-lowercase text, leaving mixed-case text unchanged. + +This processing applies to the retrieved name text, so displayed results depend on session configuration. Call-stack handles are runtime addresses rather than persistent object identifiers. + +== Verification + +The `dbms_utility` regression group covers basic exceptions, nested calls, functions and anonymous blocks, rethrowing, package members and schema-qualified names, nested exception context restoration, error-number mapping, no-exception results, call-stack structure, and combined use of all three functions. + +Current master cases also verify qualified package member names in a non-`public` schema, checking for a missing package name or duplicate `PUBLIC` prefix. Because stack addresses vary, tests check headers, names, and frame counts rather than fixed addresses. + +From a configured, built, and installed current master source tree, run `make -C contrib/ivorysql_ora oracle-installcheck ORA_REGRESS=dbms_utility`. The target instance needs matching versions of PL/iSQL and `ivorysql_ora`. diff --git a/EN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc b/EN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc new file mode 100644 index 00000000..8e1b2308 --- /dev/null +++ b/EN/modules/ROOT/pages/master/oracle_compatibility/compat_dbms_utility.adoc @@ -0,0 +1,140 @@ +:sectnums: +:sectnumlevels: 5 + += DBMS_UTILITY Package + +== Purpose + +`DBMS_UTILITY` formats error messages, exception locations, and the current call stack to help PL/iSQL programs record diagnostics and locate failures. + +== Functionality + +All three functions take no arguments and return `text`: + +[cols="3,4",options="header"] +|=== +|Function |Purpose +|`FORMAT_ERROR_BACKTRACE` |Returns the origin and propagation path of the exception being handled, formatted as `ORA-06512` lines +|`FORMAT_ERROR_STACK` |Returns a mapped `ORA-XXXXX` error number and the original error message +|`FORMAT_CALL_STACK` |Returns the PL/iSQL call stack that is still executing at the time of the call; no exception is required +|=== + +In PL/iSQL, assign the function results directly to variables: + +[source,sql] +---- +v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; +v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; +v_call_stack := DBMS_UTILITY.FORMAT_CALL_STACK; +---- + +=== Exception locations and error messages + +`FORMAT_ERROR_BACKTRACE` describes the call path at the exception's origin. `FORMAT_ERROR_STACK` describes the current exception's error number and message; the current implementation does not automatically append a location backtrace to that message. Capture both when logging an exception. + +Without an exception being handled, both error diagnostic functions return `NULL`. If an outer handler calls a subprogram with its own exception handler, the outer exception context is restored when that subprogram returns normally, allowing the original diagnostics to be read again. + +=== Current call stack + +`FORMAT_CALL_STACK` returns the PL/iSQL stack at the time of the call, including a fixed header, object handles, line numbers, and names. When called after an error, it shows the stack still executing, rather than the exception's path through routines that have already exited. Use `FORMAT_ERROR_BACKTRACE` for the exception path. + +Object handles are runtime addresses and vary by environment. The current output labels all frames as `function`, including procedures. The result is `NULL` if there are no caller frames to display. + +== Examples + +Run these examples in Oracle compatibility mode with package execution privileges. To make displayed names easier to compare, the examples enable the following case settings and reset them to defaults at the end. + +[source,sql] +---- +SET ivorysql.enable_case_switch = true; +SET ivorysql.identifier_case_switch = interchange; +---- + +=== Record the error message and exception location + +[source,sql] +---- +CREATE OR REPLACE PROCEDURE public.utility_error_demo AS + v_error_stack text; + v_backtrace text; +BEGIN + RAISE EXCEPTION 'demo error'; +EXCEPTION + WHEN OTHERS THEN + v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; + v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; + RAISE INFO 'Error stack: %', v_error_stack; + RAISE INFO 'Backtrace: %', v_backtrace; +END; +/ + +CALL public.utility_error_demo(); +---- + +The main output is shown below. Line numbers depend on statement positions in the procedure body: + +[source,text] +---- +INFO: Error stack: ORA-06510: demo error +INFO: Backtrace: ORA-06512: at "PUBLIC.UTILITY_ERROR_DEMO", line 4 +---- + +The functions return diagnostic text without automatically rethrowing the caught exception. To propagate it, execute `RAISE;` after recording the diagnostics. + +=== Inspect nested calls + +[source,sql] +---- +CREATE OR REPLACE PROCEDURE public.utility_stack_inner AS + v_stack text; +BEGIN + v_stack := DBMS_UTILITY.FORMAT_CALL_STACK; + RAISE INFO 'Call stack: %', v_stack; +END; +/ + +CREATE OR REPLACE PROCEDURE public.utility_stack_outer AS +BEGIN + public.utility_stack_inner(); +END; +/ + +CALL public.utility_stack_outer(); +---- + +The result includes the `----- PL/SQL Call Stack -----` header and caller frames for `UTILITY_STACK_INNER` and `UTILITY_STACK_OUTER`. The diagnostic function's own first frame is omitted. + +=== No exception being handled + +[source,sql] +---- +DO $$ +DECLARE + v_backtrace text; + v_error_stack text; +BEGIN + v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE; + v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK; + RAISE INFO 'Backtrace: [%], error stack: [%]', v_backtrace, v_error_stack; +END; +$$; +---- + +Both results are `NULL`, displayed as `` in psql's INFO output. + +[source,sql] +---- +DROP PROCEDURE public.utility_error_demo; +DROP PROCEDURE public.utility_stack_outer; +DROP PROCEDURE public.utility_stack_inner; +RESET ivorysql.identifier_case_switch; +RESET ivorysql.enable_case_switch; +---- + +== Output considerations + +* Error numbers use a partial SQLSTATE mapping. Division by zero maps to `ORA-01476`, and `RAISE EXCEPTION` maps to `ORA-06510`. Other errors can use the generic `ORA-06502`; examine the original message and location backtrace as well. +* Displayed object names depend on `ivorysql.enable_case_switch` and `ivorysql.identifier_case_switch`. Do not assume they are always uppercase. +* Package frames include the schema, package, and subprogram. Already schema-qualified names do not acquire an extra `PUBLIC` prefix. Unqualified standalone routines can still receive a `public` backtrace prefix, which alone does not establish the actual owning schema. +* `FORMAT_CALL_STACK` collects PL/iSQL frames. In mixed-language calls, it does not represent the complete runtime stack for every language. +* Results can span multiple lines. Use `text` to retain complete diagnostics without an undersized receiving variable. Functions can also return `NULL` when the PL/iSQL context APIs are unavailable.