Skip to content

Commit 720a0b8

Browse files
committed
refactor: move DBMS_UTILITY to ivorysql_ora per upstream convention
Move DBMS_UTILITY package from plisql to contrib/ivorysql_ora following the IvorySQL built-in package convention. Changes: - Add dbms_utility.c and dbms_utility--1.0.sql to contrib/ivorysql_ora/src/builtin_packages/dbms_utility/ - Use dynamic symbol lookup (dlsym) to call plisql API, avoiding link-time dependency between extensions - Keep minimal API in plisql: plisql_get_current_exception_context() exports exception context for cross-module access - Update Makefile and ivorysql_ora_merge_sqls for new package - Move regression tests to contrib/ivorysql_ora/sql and expected/ - Remove DBMS_UTILITY package definition from plisql--1.0.sql The dynamic lookup approach allows ivorysql_ora.so and plisql.so to be loaded in any order, resolving the symbol at runtime when the function is actually called from within a PL/iSQL exception handler.
1 parent 784ff3e commit 720a0b8

File tree

11 files changed

+257
-154
lines changed

11 files changed

+257
-154
lines changed

contrib/ivorysql_ora/Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ OBJS = \
2727
src/builtin_functions/misc_functions.o \
2828
src/merge/ora_merge.o \
2929
src/sysview/sysview_functions.o \
30-
src/xml_functions/ora_xml_functions.o
30+
src/xml_functions/ora_xml_functions.o \
31+
src/builtin_packages/dbms_utility/dbms_utility.o
3132

3233
EXTENSION = ivorysql_ora
3334

@@ -69,10 +70,14 @@ ORA_REGRESS = \
6970
datatype_and_func_bugs \
7071
ora_sysview \
7172
ora_like_operator \
72-
ora_xml_functions
73+
ora_xml_functions \
74+
dbms_utility
7375

7476
SHLIB_LINK += -lxml2
7577

78+
# Include path for plisql.h (needed by dbms_utility)
79+
PG_CPPFLAGS += -I$(top_srcdir)/src/pl/plisql/src
80+
7681
ifdef USE_PGXS
7782
PG_CONFIG = pg_config
7883
PGXS := $(shell $(PG_CONFIG) --pgxs)
File renamed without changes.

contrib/ivorysql_ora/ivorysql_ora_merge_sqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ src/datatype/datatype
22
src/builtin_functions/builtin_functions
33
src/sysview/sysview
44
src/xml_functions/xml_functions
5+
src/builtin_packages/dbms_utility/dbms_utility
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/***************************************************************
2+
*
3+
* DBMS_UTILITY Package
4+
*
5+
* Oracle-compatible utility functions.
6+
*
7+
***************************************************************/
8+
9+
-- C function wrapper for FORMAT_ERROR_BACKTRACE
10+
CREATE FUNCTION sys.ora_format_error_backtrace() RETURNS TEXT
11+
AS 'MODULE_PATHNAME', 'ora_format_error_backtrace'
12+
LANGUAGE C VOLATILE;
13+
14+
COMMENT ON FUNCTION sys.ora_format_error_backtrace() IS 'Internal function for DBMS_UTILITY.FORMAT_ERROR_BACKTRACE';
15+
16+
-- DBMS_UTILITY Package Definition
17+
CREATE OR REPLACE PACKAGE dbms_utility IS
18+
FUNCTION FORMAT_ERROR_BACKTRACE RETURN TEXT;
19+
END dbms_utility;
20+
21+
CREATE OR REPLACE PACKAGE BODY dbms_utility IS
22+
FUNCTION FORMAT_ERROR_BACKTRACE RETURN TEXT IS
23+
BEGIN
24+
RETURN sys.ora_format_error_backtrace();
25+
END;
26+
END dbms_utility;

src/pl/plisql/src/pl_dbms_utility.c renamed to contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility.c

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,77 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*
16-
* pl_dbms_utility.c
16+
* dbms_utility.c
1717
*
18-
* This file contains the implementation of Oracle's DBMS_UTILITY package
19-
* functions. These functions are part of the PL/iSQL language runtime
20-
* because they need access to PL/iSQL internals (exception context, etc.)
18+
* Implementation of Oracle's DBMS_UTILITY package functions.
19+
* This module is part of ivorysql_ora extension but calls the PL/iSQL
20+
* API to access exception context information.
2121
*
2222
* Portions Copyright (c) 2025, IvorySQL Global Development Team
2323
*
24-
* src/pl/plisql/src/pl_dbms_utility.c
24+
* contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility.c
2525
*
2626
*-------------------------------------------------------------------------
2727
*/
2828

2929
#include "postgres.h"
3030
#include "fmgr.h"
31-
#include "funcapi.h"
32-
#include "catalog/pg_collation.h"
33-
#include "catalog/pg_proc.h"
34-
#include "catalog/pg_namespace.h"
3531
#include "utils/builtins.h"
36-
#include "utils/elog.h"
37-
#include "utils/formatting.h"
38-
#include "utils/lsyscache.h"
39-
#include "utils/syscache.h"
4032
#include "mb/pg_wchar.h"
41-
#include "plisql.h"
33+
34+
#ifndef WIN32
35+
#include <dlfcn.h>
36+
#endif
4237

4338
PG_FUNCTION_INFO_V1(ora_format_error_backtrace);
4439

40+
/*
41+
* Function pointer type for plisql_get_current_exception_context.
42+
* We use dynamic lookup to avoid link-time dependency on plisql.so.
43+
*/
44+
typedef const char *(*plisql_get_context_fn)(void);
45+
46+
/*
47+
* Cached function pointer for plisql_get_current_exception_context.
48+
* Looked up once on first use.
49+
*/
50+
static plisql_get_context_fn get_exception_context_fn = NULL;
51+
static bool lookup_attempted = false;
52+
53+
/*
54+
* Look up the plisql_get_current_exception_context function dynamically.
55+
* Returns the function pointer, or NULL if not found.
56+
*/
57+
static plisql_get_context_fn
58+
lookup_plisql_get_exception_context(void)
59+
{
60+
void *fn;
61+
62+
if (lookup_attempted)
63+
return get_exception_context_fn;
64+
65+
lookup_attempted = true;
66+
67+
#ifndef WIN32
68+
/*
69+
* Use RTLD_DEFAULT to search all loaded shared objects.
70+
* plisql.so should already be loaded when this function is called
71+
* from within a PL/iSQL exception handler.
72+
*/
73+
fn = dlsym(RTLD_DEFAULT, "plisql_get_current_exception_context");
74+
if (fn != NULL)
75+
get_exception_context_fn = (plisql_get_context_fn) fn;
76+
#else
77+
/*
78+
* On Windows, we'd need to use GetProcAddress with the module handle.
79+
* For now, just return NULL - this feature requires plisql.
80+
*/
81+
fn = NULL;
82+
#endif
83+
84+
return get_exception_context_fn;
85+
}
86+
4587
/*
4688
* Transform a single line from PostgreSQL error context format to Oracle format.
4789
*
@@ -152,8 +194,7 @@ transform_and_append_line(StringInfo result, const char *line)
152194
pfree(func_name);
153195
pfree(func_upper);
154196
pfree(schema_upper);
155-
if (schema_name)
156-
pfree(schema_name);
197+
pfree(schema_name);
157198

158199
return true;
159200
}
@@ -164,9 +205,8 @@ transform_and_append_line(StringInfo result, const char *line)
164205
* Returns formatted error backtrace string in Oracle format.
165206
* Returns NULL if not in exception handler context.
166207
*
167-
* This Oracle-compatible function automatically retrieves the exception
168-
* context from PL/iSQL's session storage, which is set when entering
169-
* an exception handler.
208+
* This Oracle-compatible function retrieves the exception context from
209+
* PL/iSQL via dynamic lookup of plisql_get_current_exception_context().
170210
*/
171211
Datum
172212
ora_format_error_backtrace(PG_FUNCTION_ARGS)
@@ -176,9 +216,18 @@ ora_format_error_backtrace(PG_FUNCTION_ARGS)
176216
char *line;
177217
char *saveptr;
178218
StringInfoData result;
219+
plisql_get_context_fn get_context;
220+
221+
/* Look up the PL/iSQL function dynamically */
222+
get_context = lookup_plisql_get_exception_context();
223+
if (get_context == NULL)
224+
{
225+
/* plisql not loaded or function not found */
226+
PG_RETURN_NULL();
227+
}
179228

180-
/* Get the current exception context from PL/iSQL session storage */
181-
pg_context = plisql_get_current_exception_context();
229+
/* Get the current exception context from PL/iSQL */
230+
pg_context = get_context();
182231

183232
/* If no context available (not in exception handler), return NULL */
184233
if (pg_context == NULL || pg_context[0] == '\0')
@@ -202,8 +251,5 @@ ora_format_error_backtrace(PG_FUNCTION_ARGS)
202251

203252
pfree(context_copy);
204253

205-
/* Oracle always ends with a newline - don't remove it */
206-
/* The transform_and_append_line function already adds newlines */
207-
208254
PG_RETURN_TEXT_P(cstring_to_text(result.data));
209255
}

design/dbms_utility/PROPOSAL.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# DBMS_UTILITY Implementation Proposal
2+
3+
## Summary
4+
5+
This proposal describes an implementation approach for `DBMS_UTILITY.FORMAT_ERROR_BACKTRACE` that follows IvorySQL's built-in package convention while addressing the cross-module dependency challenge.
6+
7+
## Background
8+
9+
Oracle's `DBMS_UTILITY.FORMAT_ERROR_BACKTRACE` returns the call stack at the point where an exception was raised. This requires access to exception context information that is internal to PL/iSQL's execution state.
10+
11+
## The Challenge
12+
13+
IvorySQL has two independent modules:
14+
15+
```
16+
src/pl/plisql/src/ → plisql.so (PL/iSQL language runtime)
17+
contrib/ivorysql_ora/ → ivorysql_ora.so (Oracle compatibility extension)
18+
```
19+
20+
The built-in package convention places packages in `contrib/ivorysql_ora/src/builtin_packages/`. However, `FORMAT_ERROR_BACKTRACE` needs access to PL/iSQL's exception context (`edata->context`), which is only available inside PL/iSQL's `exec_stmt_block()` during exception handling.
21+
22+
## Proposed Solution
23+
24+
Split the implementation between modules with a minimal API boundary:
25+
26+
### 1. Minimal API in `plisql` (5-10 lines)
27+
28+
Add a single accessor function to expose the exception context:
29+
30+
```c
31+
// src/pl/plisql/src/pl_exec.c
32+
33+
static PLiSQL_execstate *exception_handling_estate = NULL;
34+
35+
const char *
36+
plisql_get_current_exception_context(void)
37+
{
38+
if (exception_handling_estate != NULL &&
39+
exception_handling_estate->cur_error != NULL)
40+
return exception_handling_estate->cur_error->context;
41+
return NULL;
42+
}
43+
```
44+
45+
Track `exception_handling_estate` when entering/exiting exception handlers:
46+
47+
```c
48+
// In exec_stmt_block(), when entering exception handler:
49+
exception_handling_estate = estate;
50+
rc = exec_stmts(estate, exception->action);
51+
exception_handling_estate = save_exception_handling_estate;
52+
```
53+
54+
Export in header:
55+
56+
```c
57+
// src/pl/plisql/src/plisql.h
58+
extern PGDLLEXPORT const char *plisql_get_current_exception_context(void);
59+
```
60+
61+
### 2. DBMS_UTILITY Package in `ivorysql_ora`
62+
63+
```
64+
contrib/ivorysql_ora/
65+
├── src/builtin_packages/
66+
│ └── dbms_utility/
67+
│ ├── dbms_utility.c ← C code (format transformation)
68+
│ └── dbms_utility--1.0.sql ← Package definition
69+
├── sql/dbms_utility.sql ← Regression tests
70+
├── expected/dbms_utility.out
71+
├── Makefile ← Add dbms_utility.o
72+
└── ivorysql_ora_merge_sqls ← Add entry
73+
```
74+
75+
The C code calls the plisql API and transforms the output:
76+
77+
```c
78+
// contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility.c
79+
80+
#include "plisql.h" // For plisql_get_current_exception_context()
81+
82+
Datum
83+
ora_format_error_backtrace(PG_FUNCTION_ARGS)
84+
{
85+
const char *pg_context = plisql_get_current_exception_context();
86+
if (pg_context == NULL)
87+
PG_RETURN_NULL();
88+
89+
// Transform PostgreSQL format to Oracle format:
90+
// "PL/iSQL function foo() line 3 at RAISE"
91+
// → "ORA-06512: at \"PUBLIC.FOO\", line 3"
92+
...
93+
}
94+
```
95+
96+
## File Changes Summary
97+
98+
### `plisql` Changes (Minimal)
99+
100+
| File | Change |
101+
|------|--------|
102+
| `src/pl/plisql/src/pl_exec.c` | Add `exception_handling_estate` tracking + API function (~20 lines) |
103+
| `src/pl/plisql/src/plisql.h` | Add API declaration (1 line) |
104+
105+
### `ivorysql_ora` Changes (Main Implementation)
106+
107+
| File | Change |
108+
|------|--------|
109+
| `contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility.c` | New - C implementation |
110+
| `contrib/ivorysql_ora/src/builtin_packages/dbms_utility/dbms_utility--1.0.sql` | New - Package definition |
111+
| `contrib/ivorysql_ora/sql/dbms_utility.sql` | New - Regression tests |
112+
| `contrib/ivorysql_ora/expected/dbms_utility.out` | New - Expected output |
113+
| `contrib/ivorysql_ora/Makefile` | Add `dbms_utility.o` |
114+
| `contrib/ivorysql_ora/ivorysql_ora_merge_sqls` | Add entry |
115+
116+
## Why This Approach?
117+
118+
1. **Follows convention**: DBMS_UTILITY lives in `contrib/ivorysql_ora/src/builtin_packages/`
119+
2. **Minimal plisql changes**: Only a small API (~20 lines), no package code
120+
3. **Clean API boundary**: `plisql_get_current_exception_context()` is a stable interface
121+
4. **Uses existing data**: Leverages `estate->cur_error` which PL/iSQL already maintains
122+
5. **No new struct fields**: Uses existing `ErrorData.context` field
123+
124+
## Alternative Considered
125+
126+
Implement everything in `plisql`:
127+
- Simpler (no cross-module coordination)
128+
- But doesn't follow built-in package convention
129+
- Future DBMS packages would be inconsistent
130+
131+
## Questions for IvorySQL Team
132+
133+
1. Is adding a minimal API to `plisql` acceptable for packages that need PL/iSQL internals?
134+
2. Should this API pattern be documented as the standard approach for such packages?
135+
3. Are there any concerns about the `PGDLLEXPORT` approach for cross-module calls?
136+
137+
## References
138+
139+
- [Oracle DBMS_UTILITY.FORMAT_ERROR_BACKTRACE](https://docs.oracle.com/en/database/oracle/oracle-database/23/arpls/DBMS_UTILITY.html#GUID-D72B928F-C353-461D-B098-83865F295C55)
140+
- Existing IvorySQL built-in packages: `contrib/ivorysql_ora/src/builtin_functions/`
141+
142+
---
143+
144+
**Author:** [Your Name]
145+
**Date:** 2025-12-02
146+
**Status:** Proposal

src/pl/plisql/src/Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ OBJS = \
4141
$(WIN32RES) \
4242
pl_autonomous.o \
4343
pl_comp.o \
44-
pl_dbms_utility.o \
4544
pl_exec.o \
4645
pl_funcs.o \
4746
pl_gram.o \

0 commit comments

Comments
 (0)