Skip to content

Commit 96b9ad9

Browse files
committed
feat: add FORMAT_ERROR_STACK and FORMAT_CALL_STACK to DBMS_UTILITY
Implement two additional Oracle-compatible functions in the DBMS_UTILITY package: - FORMAT_ERROR_STACK: Returns the error message with Oracle error code (e.g., ORA-01476 for division by zero, ORA-06510 for user exceptions) - FORMAT_CALL_STACK: Returns the current PL/iSQL call stack in Oracle format with header and function entries New PL/iSQL API functions in pl_exec.c: - plisql_get_current_exception_message() - returns error message - plisql_get_current_exception_sqlerrcode() - returns SQLSTATE code - plisql_get_call_stack() - traverses error_context_stack for call stack All three DBMS_UTILITY functions now work correctly: - Return appropriate values when in exception handler - Return NULL when not in exception context (except CALL_STACK) - Output matches Oracle format conventions
1 parent 720a0b8 commit 96b9ad9

File tree

6 files changed

+667
-25
lines changed

6 files changed

+667
-25
lines changed

contrib/ivorysql_ora/expected/dbms_utility.out

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,153 @@ INFO: Outer backtrace after: ORA-06512: at "PUBLIC.TEST_NESTED_OUTER", line 4
264264
INFO: SUCCESS: Outer backtrace preserved
265265
DROP PROCEDURE test_nested_outer;
266266
DROP PROCEDURE test_nested_inner;
267+
-- ============================================================
268+
-- Tests for FORMAT_ERROR_STACK
269+
-- ============================================================
270+
-- Test 11: FORMAT_ERROR_STACK - Basic exception
271+
CREATE OR REPLACE PROCEDURE test_error_stack_basic AS
272+
v_stack VARCHAR2(4000);
273+
BEGIN
274+
RAISE EXCEPTION 'Test error message';
275+
EXCEPTION
276+
WHEN OTHERS THEN
277+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
278+
RAISE INFO 'Error stack: %', v_stack;
279+
END;
280+
/
281+
CALL test_error_stack_basic();
282+
INFO: Error stack: ORA-06510: Test error message
283+
284+
DROP PROCEDURE test_error_stack_basic;
285+
-- Test 12: FORMAT_ERROR_STACK - Division by zero
286+
CREATE OR REPLACE PROCEDURE test_error_stack_divzero AS
287+
v_stack VARCHAR2(4000);
288+
v_num NUMBER;
289+
BEGIN
290+
v_num := 1 / 0;
291+
EXCEPTION
292+
WHEN OTHERS THEN
293+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
294+
RAISE INFO 'Division error stack: %', v_stack;
295+
END;
296+
/
297+
CALL test_error_stack_divzero();
298+
INFO: Division error stack: ORA-01476: division by zero
299+
300+
DROP PROCEDURE test_error_stack_divzero;
301+
-- Test 13: FORMAT_ERROR_STACK - No exception (should return NULL)
302+
CREATE OR REPLACE PROCEDURE test_error_stack_no_error AS
303+
v_stack VARCHAR2(4000);
304+
BEGIN
305+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
306+
RAISE INFO 'No error - stack: [%]', v_stack;
307+
END;
308+
/
309+
CALL test_error_stack_no_error();
310+
INFO: No error - stack: [<NULL>]
311+
DROP PROCEDURE test_error_stack_no_error;
312+
-- ============================================================
313+
-- Tests for FORMAT_CALL_STACK
314+
-- ============================================================
315+
-- Test 14: FORMAT_CALL_STACK - Basic single procedure (verify structure)
316+
-- Note: Addresses vary between runs, so we just verify the stack is not null
317+
-- and contains the expected function name pattern
318+
CREATE OR REPLACE PROCEDURE test_call_stack_basic AS
319+
v_stack VARCHAR2(4000);
320+
BEGIN
321+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
322+
IF v_stack IS NOT NULL AND v_stack LIKE '%----- PL/SQL Call Stack -----%' THEN
323+
-- Extract just the function name part for verification
324+
IF v_stack LIKE '%TEST_CALL_STACK_BASIC%' THEN
325+
RAISE INFO 'Call stack contains expected function';
326+
END IF;
327+
END IF;
328+
END;
329+
/
330+
CALL test_call_stack_basic();
331+
INFO: Call stack contains expected function
332+
DROP PROCEDURE test_call_stack_basic;
333+
-- Test 15: FORMAT_CALL_STACK - Nested procedure calls (verify count)
334+
CREATE OR REPLACE PROCEDURE test_call_stack_level3 AS
335+
v_stack VARCHAR2(4000);
336+
v_count INTEGER;
337+
BEGIN
338+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
339+
-- Count the number of function entries (look for 'function ' pattern)
340+
v_count := (LENGTH(v_stack) - LENGTH(REPLACE(v_stack, 'function ', ''))) / 9;
341+
RAISE INFO 'Call stack has % function entries', v_count;
342+
END;
343+
/
344+
CREATE OR REPLACE PROCEDURE test_call_stack_level2 AS
345+
BEGIN
346+
test_call_stack_level3();
347+
END;
348+
/
349+
CREATE OR REPLACE PROCEDURE test_call_stack_level1 AS
350+
BEGIN
351+
test_call_stack_level2();
352+
END;
353+
/
354+
CALL test_call_stack_level1();
355+
INFO: Call stack has 3 function entries
356+
DROP PROCEDURE test_call_stack_level1;
357+
DROP PROCEDURE test_call_stack_level2;
358+
DROP PROCEDURE test_call_stack_level3;
359+
-- Test 16: FORMAT_CALL_STACK - In exception handler
360+
CREATE OR REPLACE PROCEDURE test_call_stack_exception AS
361+
v_stack VARCHAR2(4000);
362+
BEGIN
363+
RAISE EXCEPTION 'Test error';
364+
EXCEPTION
365+
WHEN OTHERS THEN
366+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
367+
IF v_stack IS NOT NULL AND v_stack LIKE '%TEST_CALL_STACK_EXCEPTION%' THEN
368+
RAISE INFO 'Call stack in exception handler: OK';
369+
END IF;
370+
END;
371+
/
372+
CALL test_call_stack_exception();
373+
INFO: Call stack in exception handler: OK
374+
DROP PROCEDURE test_call_stack_exception;
375+
-- Test 17: All three functions together (verify they return expected content)
376+
CREATE OR REPLACE PROCEDURE test_all_functions_inner AS
377+
BEGIN
378+
RAISE EXCEPTION 'Inner error for all functions test';
379+
END;
380+
/
381+
CREATE OR REPLACE PROCEDURE test_all_functions_outer AS
382+
v_backtrace VARCHAR2(4000);
383+
v_error_stack VARCHAR2(4000);
384+
v_call_stack VARCHAR2(4000);
385+
v_all_ok BOOLEAN := TRUE;
386+
BEGIN
387+
test_all_functions_inner();
388+
EXCEPTION
389+
WHEN OTHERS THEN
390+
v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
391+
v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
392+
v_call_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
393+
-- Verify FORMAT_ERROR_BACKTRACE
394+
IF v_backtrace IS NULL OR v_backtrace NOT LIKE '%ORA-06512%' THEN
395+
v_all_ok := FALSE;
396+
RAISE INFO 'FORMAT_ERROR_BACKTRACE: FAILED';
397+
END IF;
398+
-- Verify FORMAT_ERROR_STACK
399+
IF v_error_stack IS NULL OR v_error_stack NOT LIKE '%ORA-%' THEN
400+
v_all_ok := FALSE;
401+
RAISE INFO 'FORMAT_ERROR_STACK: FAILED';
402+
END IF;
403+
-- Verify FORMAT_CALL_STACK
404+
IF v_call_stack IS NULL OR v_call_stack NOT LIKE '%----- PL/SQL Call Stack -----%' THEN
405+
v_all_ok := FALSE;
406+
RAISE INFO 'FORMAT_CALL_STACK: FAILED';
407+
END IF;
408+
IF v_all_ok THEN
409+
RAISE INFO 'All three DBMS_UTILITY functions: OK';
410+
END IF;
411+
END;
412+
/
413+
CALL test_all_functions_outer();
414+
INFO: All three DBMS_UTILITY functions: OK
415+
DROP PROCEDURE test_all_functions_outer;
416+
DROP PROCEDURE test_all_functions_inner;

contrib/ivorysql_ora/sql/dbms_utility.sql

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,174 @@ CALL test_nested_outer();
268268

269269
DROP PROCEDURE test_nested_outer;
270270
DROP PROCEDURE test_nested_inner;
271+
272+
-- ============================================================
273+
-- Tests for FORMAT_ERROR_STACK
274+
-- ============================================================
275+
276+
-- Test 11: FORMAT_ERROR_STACK - Basic exception
277+
CREATE OR REPLACE PROCEDURE test_error_stack_basic AS
278+
v_stack VARCHAR2(4000);
279+
BEGIN
280+
RAISE EXCEPTION 'Test error message';
281+
EXCEPTION
282+
WHEN OTHERS THEN
283+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
284+
RAISE INFO 'Error stack: %', v_stack;
285+
END;
286+
/
287+
288+
CALL test_error_stack_basic();
289+
290+
DROP PROCEDURE test_error_stack_basic;
291+
292+
-- Test 12: FORMAT_ERROR_STACK - Division by zero
293+
CREATE OR REPLACE PROCEDURE test_error_stack_divzero AS
294+
v_stack VARCHAR2(4000);
295+
v_num NUMBER;
296+
BEGIN
297+
v_num := 1 / 0;
298+
EXCEPTION
299+
WHEN OTHERS THEN
300+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
301+
RAISE INFO 'Division error stack: %', v_stack;
302+
END;
303+
/
304+
305+
CALL test_error_stack_divzero();
306+
307+
DROP PROCEDURE test_error_stack_divzero;
308+
309+
-- Test 13: FORMAT_ERROR_STACK - No exception (should return NULL)
310+
CREATE OR REPLACE PROCEDURE test_error_stack_no_error AS
311+
v_stack VARCHAR2(4000);
312+
BEGIN
313+
v_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
314+
RAISE INFO 'No error - stack: [%]', v_stack;
315+
END;
316+
/
317+
318+
CALL test_error_stack_no_error();
319+
320+
DROP PROCEDURE test_error_stack_no_error;
321+
322+
-- ============================================================
323+
-- Tests for FORMAT_CALL_STACK
324+
-- ============================================================
325+
326+
-- Test 14: FORMAT_CALL_STACK - Basic single procedure (verify structure)
327+
-- Note: Addresses vary between runs, so we just verify the stack is not null
328+
-- and contains the expected function name pattern
329+
CREATE OR REPLACE PROCEDURE test_call_stack_basic AS
330+
v_stack VARCHAR2(4000);
331+
BEGIN
332+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
333+
IF v_stack IS NOT NULL AND v_stack LIKE '%----- PL/SQL Call Stack -----%' THEN
334+
-- Extract just the function name part for verification
335+
IF v_stack LIKE '%TEST_CALL_STACK_BASIC%' THEN
336+
RAISE INFO 'Call stack contains expected function';
337+
END IF;
338+
END IF;
339+
END;
340+
/
341+
342+
CALL test_call_stack_basic();
343+
344+
DROP PROCEDURE test_call_stack_basic;
345+
346+
-- Test 15: FORMAT_CALL_STACK - Nested procedure calls (verify count)
347+
CREATE OR REPLACE PROCEDURE test_call_stack_level3 AS
348+
v_stack VARCHAR2(4000);
349+
v_count INTEGER;
350+
BEGIN
351+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
352+
-- Count the number of function entries (look for 'function ' pattern)
353+
v_count := (LENGTH(v_stack) - LENGTH(REPLACE(v_stack, 'function ', ''))) / 9;
354+
RAISE INFO 'Call stack has % function entries', v_count;
355+
END;
356+
/
357+
358+
CREATE OR REPLACE PROCEDURE test_call_stack_level2 AS
359+
BEGIN
360+
test_call_stack_level3();
361+
END;
362+
/
363+
364+
CREATE OR REPLACE PROCEDURE test_call_stack_level1 AS
365+
BEGIN
366+
test_call_stack_level2();
367+
END;
368+
/
369+
370+
CALL test_call_stack_level1();
371+
372+
DROP PROCEDURE test_call_stack_level1;
373+
DROP PROCEDURE test_call_stack_level2;
374+
DROP PROCEDURE test_call_stack_level3;
375+
376+
-- Test 16: FORMAT_CALL_STACK - In exception handler
377+
CREATE OR REPLACE PROCEDURE test_call_stack_exception AS
378+
v_stack VARCHAR2(4000);
379+
BEGIN
380+
RAISE EXCEPTION 'Test error';
381+
EXCEPTION
382+
WHEN OTHERS THEN
383+
v_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
384+
IF v_stack IS NOT NULL AND v_stack LIKE '%TEST_CALL_STACK_EXCEPTION%' THEN
385+
RAISE INFO 'Call stack in exception handler: OK';
386+
END IF;
387+
END;
388+
/
389+
390+
CALL test_call_stack_exception();
391+
392+
DROP PROCEDURE test_call_stack_exception;
393+
394+
-- Test 17: All three functions together (verify they return expected content)
395+
CREATE OR REPLACE PROCEDURE test_all_functions_inner AS
396+
BEGIN
397+
RAISE EXCEPTION 'Inner error for all functions test';
398+
END;
399+
/
400+
401+
CREATE OR REPLACE PROCEDURE test_all_functions_outer AS
402+
v_backtrace VARCHAR2(4000);
403+
v_error_stack VARCHAR2(4000);
404+
v_call_stack VARCHAR2(4000);
405+
v_all_ok BOOLEAN := TRUE;
406+
BEGIN
407+
test_all_functions_inner();
408+
EXCEPTION
409+
WHEN OTHERS THEN
410+
v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
411+
v_error_stack := DBMS_UTILITY.FORMAT_ERROR_STACK;
412+
v_call_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
413+
414+
-- Verify FORMAT_ERROR_BACKTRACE
415+
IF v_backtrace IS NULL OR v_backtrace NOT LIKE '%ORA-06512%' THEN
416+
v_all_ok := FALSE;
417+
RAISE INFO 'FORMAT_ERROR_BACKTRACE: FAILED';
418+
END IF;
419+
420+
-- Verify FORMAT_ERROR_STACK
421+
IF v_error_stack IS NULL OR v_error_stack NOT LIKE '%ORA-%' THEN
422+
v_all_ok := FALSE;
423+
RAISE INFO 'FORMAT_ERROR_STACK: FAILED';
424+
END IF;
425+
426+
-- Verify FORMAT_CALL_STACK
427+
IF v_call_stack IS NULL OR v_call_stack NOT LIKE '%----- PL/SQL Call Stack -----%' THEN
428+
v_all_ok := FALSE;
429+
RAISE INFO 'FORMAT_CALL_STACK: FAILED';
430+
END IF;
431+
432+
IF v_all_ok THEN
433+
RAISE INFO 'All three DBMS_UTILITY functions: OK';
434+
END IF;
435+
END;
436+
/
437+
438+
CALL test_all_functions_outer();
439+
440+
DROP PROCEDURE test_all_functions_outer;
441+
DROP PROCEDURE test_all_functions_inner;

contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility--1.0.sql

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,43 @@
66
*
77
***************************************************************/
88

9-
-- C function wrapper for FORMAT_ERROR_BACKTRACE
9+
-- C function wrappers
1010
CREATE FUNCTION sys.ora_format_error_backtrace() RETURNS TEXT
1111
AS 'MODULE_PATHNAME', 'ora_format_error_backtrace'
1212
LANGUAGE C VOLATILE;
1313

14+
CREATE FUNCTION sys.ora_format_error_stack() RETURNS TEXT
15+
AS 'MODULE_PATHNAME', 'ora_format_error_stack'
16+
LANGUAGE C VOLATILE;
17+
18+
CREATE FUNCTION sys.ora_format_call_stack() RETURNS TEXT
19+
AS 'MODULE_PATHNAME', 'ora_format_call_stack'
20+
LANGUAGE C VOLATILE;
21+
1422
COMMENT ON FUNCTION sys.ora_format_error_backtrace() IS 'Internal function for DBMS_UTILITY.FORMAT_ERROR_BACKTRACE';
23+
COMMENT ON FUNCTION sys.ora_format_error_stack() IS 'Internal function for DBMS_UTILITY.FORMAT_ERROR_STACK';
24+
COMMENT ON FUNCTION sys.ora_format_call_stack() IS 'Internal function for DBMS_UTILITY.FORMAT_CALL_STACK';
1525

1626
-- DBMS_UTILITY Package Definition
1727
CREATE OR REPLACE PACKAGE dbms_utility IS
1828
FUNCTION FORMAT_ERROR_BACKTRACE RETURN TEXT;
29+
FUNCTION FORMAT_ERROR_STACK RETURN TEXT;
30+
FUNCTION FORMAT_CALL_STACK RETURN TEXT;
1931
END dbms_utility;
2032

2133
CREATE OR REPLACE PACKAGE BODY dbms_utility IS
2234
FUNCTION FORMAT_ERROR_BACKTRACE RETURN TEXT IS
2335
BEGIN
2436
RETURN sys.ora_format_error_backtrace();
2537
END;
38+
39+
FUNCTION FORMAT_ERROR_STACK RETURN TEXT IS
40+
BEGIN
41+
RETURN sys.ora_format_error_stack();
42+
END;
43+
44+
FUNCTION FORMAT_CALL_STACK RETURN TEXT IS
45+
BEGIN
46+
RETURN sys.ora_format_call_stack();
47+
END;
2648
END dbms_utility;

0 commit comments

Comments
 (0)