Skip to content

Commit 33bab6f

Browse files
authored
feat: implement PRAGMA AUTONOMOUS_TRANSACTION via dblink (#7)
* feat: implement PRAGMA AUTONOMOUS_TRANSACTION via dblink Implement Oracle-compatible autonomous transactions for PL/iSQL using dblink to execute procedures in separate database sessions. Implementation approach: - Calls procedures by name via dblink (not SQL parsing) - Uses MyProcPort->database_name for dynamic database detection - Sets plisql.inside_autonomous_transaction flag to prevent recursion - Parameters work with original names (no name translation needed) What works: - Procedures without parameters - Procedures with parameters (all types) - Transaction isolation (commits independent of caller) - Works in any database (dynamic database name resolution) Known limitations: - Procedures must be committed before calling (dblink can't see uncommitted catalog changes) - Requires dblink extension - Return values not yet captured for functions
1 parent c51d958 commit 33bab6f

11 files changed

Lines changed: 981 additions & 29 deletions

src/pl/plisql/src/Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ rpath =
3939

4040
OBJS = \
4141
$(WIN32RES) \
42+
pl_autonomous.o \
4243
pl_comp.o \
4344
pl_exec.o \
4445
pl_funcs.o \
@@ -51,12 +52,17 @@ OBJS = \
5152

5253
DATA = plisql.control plisql--1.0.sql
5354

55+
# dblink extension is required for autonomous transactions
56+
# Install dblink into the test instance
57+
EXTRA_INSTALL = contrib/dblink
58+
5459
ORACLE_REGRESS_OPTS = --dbname=$(PL_TESTDB)
5560

5661
REGRESS = plisql_array plisql_call plisql_control plisql_copy plisql_domain \
5762
plisql_record plisql_cache plisql_simple plisql_transaction \
5863
plisql_trap plisql_trigger plisql_varprops plisql_nested_subproc \
59-
plisql_nested_subproc2 plisql_out_parameter plisql_type_rowtype
64+
plisql_nested_subproc2 plisql_out_parameter plisql_type_rowtype \
65+
plisql_autonomous
6066

6167
# where to find ora_gen_keywordlist.pl and subsidiary files
6268
TOOLSDIR = $(top_srcdir)/src/tools
@@ -85,6 +91,7 @@ install-headers: installdirs
8591
$(INSTALL_DATA) '$(srcdir)/plisql.h' '$(DESTDIR)$(includedir_server)'
8692
$(INSTALL_DATA) '$(srcdir)/pl_subproc_function.h' '$(DESTDIR)$(includedir_server)'
8793
$(INSTALL_DATA) '$(srcdir)/pl_package.h' '$(DESTDIR)$(includedir_server)'
94+
$(INSTALL_DATA) '$(srcdir)/pl_autonomous.h' '$(DESTDIR)$(includedir_server)'
8895

8996
uninstall-data:
9097
rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
@@ -93,12 +100,13 @@ uninstall-headers:
93100
rm -f '$(DESTDIR)$(includedir_server)/plisql.h'
94101
rm -f '$(DESTDIR)$(includedir_server)/pl_subproc_function.h'
95102
rm -f '$(DESTDIR)$(includedir_server)/pl_package.h'
103+
rm -f '$(DESTDIR)$(includedir_server)/pl_autonomous.h'
96104

97105
.PHONY: install-data install-headers uninstall-data uninstall-headers
98106

99107

100108
# Force these dependencies to be known even without dependency info built:
101-
pl_gram.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o pl_scanner.o: plisql.h pl_gram.h plerrcodes.h pl_subproc_function.h pl_package.h
109+
pl_gram.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o pl_scanner.o pl_autonomous.o: plisql.h pl_gram.h plerrcodes.h pl_subproc_function.h pl_package.h pl_autonomous.h
102110
pl_scanner.o: pl_reserved_kwlist_d.h pl_unreserved_kwlist_d.h
103111

104112
# See notes in src/backend/parser/Makefile about the following two rules
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
--
2+
-- Test PRAGMA AUTONOMOUS_TRANSACTION
3+
--
4+
-- This test verifies autonomous transaction functionality using dblink
5+
--
6+
-- Setup: Enable Oracle mode and install dblink
7+
CREATE EXTENSION IF NOT EXISTS dblink;
8+
-- Create test table
9+
CREATE TABLE autonomous_test (
10+
id INT,
11+
msg TEXT,
12+
tx_state TEXT DEFAULT 'unknown'
13+
);
14+
--
15+
-- Test 1: Basic autonomous transaction (no parameters)
16+
--
17+
CREATE OR REPLACE PROCEDURE test_basic AS $$
18+
PRAGMA AUTONOMOUS_TRANSACTION;
19+
BEGIN
20+
INSERT INTO autonomous_test VALUES (1, 'basic test', 'committed');
21+
END;
22+
$$ LANGUAGE plisql;
23+
/
24+
-- Must commit procedure before calling it
25+
COMMIT;
26+
WARNING: there is no transaction in progress
27+
CALL test_basic();
28+
SELECT id, msg, tx_state FROM autonomous_test WHERE id = 1;
29+
id | msg | tx_state
30+
----+------------+-----------
31+
1 | basic test | committed
32+
(1 row)
33+
34+
--
35+
-- Test 2: Autonomous transaction with parameters
36+
--
37+
CREATE OR REPLACE PROCEDURE test_with_params(p_id INT, p_msg TEXT) AS $$
38+
PRAGMA AUTONOMOUS_TRANSACTION;
39+
BEGIN
40+
INSERT INTO autonomous_test VALUES (p_id, p_msg, 'committed');
41+
END;
42+
$$ LANGUAGE plisql;
43+
/
44+
COMMIT;
45+
WARNING: there is no transaction in progress
46+
CALL test_with_params(2, 'with params');
47+
SELECT id, msg, tx_state FROM autonomous_test WHERE id = 2;
48+
id | msg | tx_state
49+
----+-------------+-----------
50+
2 | with params | committed
51+
(1 row)
52+
53+
--
54+
-- Test 3: Transaction isolation - autonomous commit survives outer rollback
55+
--
56+
CREATE OR REPLACE PROCEDURE test_isolation(p_id INT) AS $$
57+
PRAGMA AUTONOMOUS_TRANSACTION;
58+
BEGIN
59+
INSERT INTO autonomous_test VALUES (p_id, 'autonomous', 'committed');
60+
END;
61+
$$ LANGUAGE plisql;
62+
/
63+
COMMIT;
64+
WARNING: there is no transaction in progress
65+
-- Start a transaction and rollback
66+
BEGIN;
67+
INSERT INTO autonomous_test VALUES (100, 'before autonomous', 'rolled back');
68+
CALL test_isolation(200);
69+
INSERT INTO autonomous_test VALUES (300, 'after autonomous', 'rolled back');
70+
ROLLBACK;
71+
-- Verify: Only id=200 should exist (100 and 300 rolled back)
72+
SELECT id, msg, tx_state FROM autonomous_test WHERE id >= 100 ORDER BY id;
73+
id | msg | tx_state
74+
-----+------------+-----------
75+
200 | autonomous | committed
76+
(1 row)
77+
78+
--
79+
-- Test 4: Multiple parameters with different types
80+
--
81+
CREATE OR REPLACE PROCEDURE test_multi_types(
82+
p_int INT,
83+
p_text TEXT,
84+
p_bool BOOLEAN
85+
) AS $$
86+
PRAGMA AUTONOMOUS_TRANSACTION;
87+
BEGIN
88+
INSERT INTO autonomous_test VALUES (
89+
p_int,
90+
p_text || ' (bool=' || p_bool::TEXT || ')',
91+
'committed'
92+
);
93+
END;
94+
$$ LANGUAGE plisql;
95+
/
96+
COMMIT;
97+
WARNING: there is no transaction in progress
98+
CALL test_multi_types(4, 'multi-type', true);
99+
SELECT id, msg FROM autonomous_test WHERE id = 4;
100+
id | msg
101+
----+------------------------
102+
4 | multi-type (bool=true)
103+
(1 row)
104+
105+
--
106+
-- Test 5: NULL parameter handling
107+
--
108+
CREATE OR REPLACE PROCEDURE test_nulls(p_id INT, p_msg TEXT) AS $$
109+
PRAGMA AUTONOMOUS_TRANSACTION;
110+
BEGIN
111+
INSERT INTO autonomous_test VALUES (p_id, COALESCE(p_msg, 'NULL msg'), 'committed');
112+
END;
113+
$$ LANGUAGE plisql;
114+
/
115+
COMMIT;
116+
WARNING: there is no transaction in progress
117+
CALL test_nulls(5, NULL);
118+
SELECT id, msg FROM autonomous_test WHERE id = 5;
119+
id | msg
120+
----+----------
121+
5 | NULL msg
122+
(1 row)
123+
124+
--
125+
-- Test 6: Multiple sequential autonomous calls
126+
--
127+
CREATE OR REPLACE PROCEDURE test_sequential(p_id INT) AS $$
128+
PRAGMA AUTONOMOUS_TRANSACTION;
129+
BEGIN
130+
INSERT INTO autonomous_test VALUES (p_id, 'sequential', 'committed');
131+
END;
132+
$$ LANGUAGE plisql;
133+
/
134+
COMMIT;
135+
WARNING: there is no transaction in progress
136+
CALL test_sequential(6);
137+
CALL test_sequential(7);
138+
CALL test_sequential(8);
139+
SELECT id, msg FROM autonomous_test WHERE id IN (6, 7, 8) ORDER BY id;
140+
id | msg
141+
----+------------
142+
6 | sequential
143+
7 | sequential
144+
8 | sequential
145+
(3 rows)
146+
147+
--
148+
-- Test 7: Error handling - missing dblink should give clear error
149+
--
150+
-- Note: We can't actually test this because dblink is already installed,
151+
-- but the error message would be:
152+
-- ERROR: dblink_exec function not found
153+
-- HINT: Install dblink extension: CREATE EXTENSION dblink
154+
--
155+
-- Test 8: Verify transaction isolation - autonomous changes persist
156+
--
157+
TRUNCATE autonomous_test;
158+
CREATE OR REPLACE PROCEDURE test_persist(p_id INT) AS $$
159+
PRAGMA AUTONOMOUS_TRANSACTION;
160+
BEGIN
161+
INSERT INTO autonomous_test VALUES (p_id, 'should persist', 'committed');
162+
END;
163+
$$ LANGUAGE plisql;
164+
/
165+
COMMIT;
166+
WARNING: there is no transaction in progress
167+
-- Outer transaction that will rollback
168+
BEGIN;
169+
INSERT INTO autonomous_test VALUES (1000, 'will rollback', 'rolled back');
170+
CALL test_persist(2000);
171+
INSERT INTO autonomous_test VALUES (3000, 'will rollback', 'rolled back');
172+
-- Check within transaction - both should be visible
173+
SELECT COUNT(*) AS count_in_tx FROM autonomous_test;
174+
count_in_tx
175+
-------------
176+
3
177+
(1 row)
178+
179+
ROLLBACK;
180+
-- After rollback - only autonomous insert should remain
181+
SELECT id, msg, tx_state FROM autonomous_test ORDER BY id;
182+
id | msg | tx_state
183+
------+----------------+-----------
184+
2000 | should persist | committed
185+
(1 row)
186+
187+
--
188+
-- Test 9: Extension drop/recreate - verify OID invalidation works
189+
--
190+
CREATE OR REPLACE PROCEDURE test_oid_invalidation(p_id INT) AS $$
191+
PRAGMA AUTONOMOUS_TRANSACTION;
192+
BEGIN
193+
INSERT INTO autonomous_test VALUES (p_id, 'oid test', 'committed');
194+
END;
195+
$$ LANGUAGE plisql;
196+
/
197+
COMMIT;
198+
WARNING: there is no transaction in progress
199+
-- Call once to cache the OID
200+
CALL test_oid_invalidation(9);
201+
-- Drop and recreate dblink extension (OID will change)
202+
DROP EXTENSION dblink CASCADE;
203+
CREATE EXTENSION dblink;
204+
-- Call again - should work with new OID (tests invalidation callback)
205+
CALL test_oid_invalidation(10);
206+
-- Verify both calls succeeded
207+
SELECT id, msg FROM autonomous_test WHERE id IN (9, 10) ORDER BY id;
208+
id | msg
209+
----+----------
210+
9 | oid test
211+
10 | oid test
212+
(2 rows)
213+
214+
--
215+
-- Summary: Show all test results
216+
--
217+
SELECT 'All autonomous transaction tests completed' AS status;
218+
status
219+
--------------------------------------------
220+
All autonomous transaction tests completed
221+
(1 row)
222+
223+
-- Cleanup
224+
DROP PROCEDURE test_basic();
225+
DROP PROCEDURE test_with_params(INT, TEXT);
226+
DROP PROCEDURE test_isolation(INT);
227+
DROP PROCEDURE test_multi_types(INT, TEXT, BOOLEAN);
228+
DROP PROCEDURE test_nulls(INT, TEXT);
229+
DROP PROCEDURE test_sequential(INT);
230+
DROP PROCEDURE test_persist(INT);
231+
DROP PROCEDURE test_oid_invalidation(INT);
232+
DROP TABLE autonomous_test;

src/pl/plisql/src/expected/plisql_nested_subproc2.out

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,11 +3996,7 @@ begin
39963996
end;
39973997
/
39983998
INFO: var1 = 2
3999-
ERROR: schema "dbms_output" does not exist
4000-
LINE 1: CALL dbms_output.put_line('xiexie')
4001-
^
4002-
QUERY: CALL dbms_output.put_line('xiexie')
4003-
CONTEXT: PL/iSQL function inline_code_block line 5 at CALL
3999+
INFO: xiexie
40044000
create or replace function test.test_f(id integer) return integer is
40054001
var1 integer;
40064002
function test_f(id integer) return integer;

0 commit comments

Comments
 (0)