PRAGMA AUTONOMOUS_TRANSACTION runs a block of code as an independent transaction, completely separate from the calling (parent) transaction. The autonomous transaction has its own commit and rollback logic—its outcome does not affect, and is not affected by, the parent transaction. Use it for audit logging and error logging, where you need to persist a record even if the main operation rolls back.
How it works
When a program unit marked with PRAGMA AUTONOMOUS_TRANSACTION is called, the database suspends the parent transaction and starts a new, independent sub-transaction. The sub-transaction runs to completion—committing or rolling back on its own—and then the parent transaction resumes.
Autonomous transactions are not nested transactions. Key differences:
No dependency: If the parent transaction rolls back, the autonomous transaction's committed changes are not rolled back.
Independent isolation: An autonomous transaction cannot see uncommitted changes from the parent transaction, and vice versa.
Separate connection: Each autonomous transaction creates a dedicated database connection, which is released when the transaction ends.
Declare the directive
Place PRAGMA AUTONOMOUS_TRANSACTION in the declaration section of the following Structured Process Language (SPL) program units:
Standalone stored procedures and functions
Anonymous blocks
Stored procedures and functions declared in packages
Triggers
Object type methods
Follow these rules when using PRAGMA AUTONOMOUS_TRANSACTION:
Place the directive at the beginning of the declaration section. The examples below show the correct position.
Autonomous transactions in triggers are not supported in Oracle syntax compatibility version 2.0. The trigger syntax below applies to Oracle compatibility 1.0 only.
Always end an autonomous transaction with an explicit
COMMITorROLLBACK. In PolarDB for PostgreSQL (Compatible with Oracle), omitting both causes an implicit commit—the transaction commits automatically when the block exits normally. Oracle throws anORA-06519error in the same situation. This behavioral difference is a high-risk migration concern: data may be committed unexpectedly, which violates transactional atomicity. See Compatibility notes for details.
Correct syntax for a stored procedure:
CREATE OR REPLACE PROCEDURE procedure_name IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- Business logic
COMMIT;
END;Correct syntax for a trigger (Oracle compatibility 1.0 only):
CREATE OR REPLACE TRIGGER trigger_name
AFTER INSERT OR UPDATE OR DELETE ON table_name
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- Business logic
COMMIT;
END;Use cases
Record audit logs with an autonomous anonymous block
This example shows how audit logs are preserved even when the parent transaction rolls back.
Create the
emptable for employee records and theempauditlogtable for audit entries.CREATE TABLE emp ( emp_id INT, emp_name VARCHAR(50), job VARCHAR(50) ); CREATE TABLE empauditlog ( audit_date DATE, audit_user VARCHAR2(20), audit_desc VARCHAR2(100) );Start a transaction, insert an employee record, write an audit log in an autonomous block, then roll back the parent transaction.
BEGIN; INSERT INTO emp (emp_id, emp_name, job) VALUES (101, 'John Doe', 'Engineer'); DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO empauditlog VALUES (SYSDATE, USER, 'Added employee(s)'); COMMIT; -- Commits only the autonomous transaction END; ROLLBACK; -- Rolls back only the parent transactionVerify the results. The
empinsert is rolled back, but the audit log entry is preserved.-- Returns no rows: the parent transaction was rolled back SELECT * FROM emp WHERE emp_id = 101; -- Returns one row: the autonomous transaction committed independently SELECT * FROM empauditlog;
Log failed order attempts with an autonomous stored procedure
This example creates an order if stock is sufficient. If stock runs out, the order is rolled back, but the failure is still logged.
Create the required tables and seed the inventory with 5 units.
-- Order table CREATE TABLE orders ( order_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_id NUMBER, product_id NUMBER, qty NUMBER, order_time TIMESTAMP DEFAULT SYSDATE ); -- Inventory table CREATE TABLE inventory ( product_id NUMBER PRIMARY KEY, stock NUMBER ); -- Operation log (written by the autonomous transaction) CREATE TABLE operation_log ( log_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_id NUMBER, product_id NUMBER, action VARCHAR2(50), -- e.g., 'ORDER_ATTEMPT' status VARCHAR2(20), -- 'SUCCESS' or 'FAILED' reason VARCHAR2(200), log_time TIMESTAMP DEFAULT SYSDATE ); -- Seed: product 1001 has 5 units in stock INSERT INTO inventory (product_id, stock) VALUES (1001, 5);Create an autonomous stored procedure that writes to
operation_logindependently of the caller's transaction.CREATE OR REPLACE PROCEDURE log_order_attempt( p_user_id IN NUMBER, p_product_id IN NUMBER, p_reason IN VARCHAR2 ) IS PRAGMA AUTONOMOUS_TRANSACTION; -- Declared at the start of the declaration section BEGIN INSERT INTO operation_log ( user_id, product_id, action, status, reason, log_time ) VALUES ( p_user_id, p_product_id, 'ORDER_ATTEMPT', 'FAILED', p_reason, SYSDATE ); COMMIT; -- Commits independently; not affected by the caller's rollback END;Attempt to order 10 units. Because stock is only 5, the order fails, but the autonomous transaction logs the attempt.
DECLARE v_user_id NUMBER := 123; v_product_id NUMBER := 1001; v_order_qty NUMBER := 10; v_stock NUMBER; BEGIN SELECT stock INTO v_stock FROM inventory WHERE product_id = v_product_id FOR UPDATE; IF v_stock < v_order_qty THEN log_order_attempt( p_user_id => v_user_id, p_product_id => v_product_id, p_reason => 'Insufficient stock: need ' || v_order_qty || ', available ' || v_stock ); RAISE_APPLICATION_ERROR(-20001, 'Insufficient stock, order creation failed'); END IF; INSERT INTO orders (user_id, product_id, qty) VALUES (v_user_id, v_product_id, v_order_qty); UPDATE inventory SET stock = stock - v_order_qty WHERE product_id = v_product_id; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END;Verify the results. The order and inventory changes are rolled back, but the log entry is committed.
SELECT * FROM orders; -- Empty: parent transaction rolled back SELECT * FROM inventory; -- Unchanged: stock still 5 SELECT * FROM operation_log; -- One row: autonomous transaction committed
Best practices
Avoid connection exhaustion
Each autonomous transaction consumes a separate database connection for its entire duration. In high-concurrency environments, this can exhaust the max_connections limit and prevent new connections from being established.
High-risk patterns:
Calling autonomous transactions inside a loop
Calling autonomous transactions from a frequently invoked function
Nesting risk: Autonomous transactions can be nested. Each level of nesting uses an additional connection. For example, three levels of nested autonomous transactions consume four connections in total—one for the main session and one per autonomous transaction.
Before deploying autonomous transactions in production:
Estimate the additional connection demand.
Adjust the
max_connectionsparameter based on that estimate.Run stress tests to validate the configuration.
Keep autonomous transactions short
An autonomous transaction should complete quickly. Use it only for fast, simple operations such as inserting a single log entry. Avoid complex queries or long-running operations inside an autonomous block.
Use autonomous transactions only for auxiliary logic
Reserve autonomous transactions for tasks that must be independent of the main transaction outcome—auditing, failure logging, and statistics updates. Do not use them to implement core business logic.
Always commit or roll back explicitly
End every autonomous transaction block with COMMIT or ROLLBACK. Relying on implicit behavior leads to different outcomes on Oracle and PolarDB for PostgreSQL (Compatible with Oracle). See Compatibility notes.
Compatibility notes
PolarDB for PostgreSQL (Compatible with Oracle) and Oracle Database behave differently when an autonomous transaction block ends without an explicit COMMIT or ROLLBACK.
| Scenario | Oracle behavior | PolarDB for PostgreSQL (Compatible with Oracle) behavior | Risk |
|---|---|---|---|
No explicit COMMIT or ROLLBACK at block exit | Throws ORA-06519 | Implicit commit — the transaction commits automatically | High. Data may be committed unexpectedly, violating transactional atomicity. Code that relies on Oracle's error-forcing behavior will silently commit on PolarDB. |
To keep code portable and safe across environments, always end autonomous transaction blocks with an explicit COMMIT or ROLLBACK.
FAQ
How do I troubleshoot "sorry, too many clients already"?
This error means active connections have reached the max_connections limit. To check whether autonomous transactions are the cause:
Check the current connection count and the configured limit.
-- Total active connections SELECT count(*) FROM pg_stat_activity; -- Configured limit SHOW max_connections;Find long-running autonomous transactions (running longer than five minutes).
SELECT pid, query_start, now() - query_start AS duration, query FROM pg_stat_activity WHERE state = 'active' AND query ILIKE '%PRAGMA AUTONOMOUS_TRANSACTION%' AND now() - query_start > interval '5 minutes';Based on the results, take one or more of the following actions:
Optimize the code: Look for loops or frequently called functions that trigger autonomous transactions. Refactor to reduce call frequency.
Increase
max_connections: If the current usage is legitimate but connections are still exhausted, raisemax_connectionsafter evaluating the system-wide memory impact.Set a query timeout: Configure
statement_timeoutfor the session to automatically terminate queries that run too long, preventing connections from being held open indefinitely.