|
| 1 | +<!-- doc/src/sgml/atx.sgml --> |
| 2 | + |
| 3 | +<chapter id="atx"> |
| 4 | + <title>Autonomous transactions.</title> |
| 5 | + |
| 6 | +<sect1 id="atx-overview"> |
| 7 | + <title>Overview of autonomous transactions</title> |
| 8 | + |
| 9 | +<para> |
| 10 | +Postgres supports nested transactions: them are rarely explicitly used by programmer and mostly used for error handling and stored |
| 11 | +procedures. It is possible to rollback subtransaction without affecting parent transaction. But commit of subtraction is delayed until |
| 12 | +commit of parent transaction. |
| 13 | +</para> |
| 14 | +<para> |
| 15 | +But in some cases application needs to run several independent transactions in one session. |
| 16 | +"Autonomous Subtransactions" (in short AST) denotes the capability of a single session to run multiple independent transactions, as if multiple different sessions were executing each transaction. |
| 17 | +</para> |
| 18 | +<para> |
| 19 | +Autonomous transactions are needed mostly for implementing audits, when the fact of performing audit should be reported regardless |
| 20 | +status of audit itself: whether it was successfully completed or not. |
| 21 | +Autonomous transactions are widely used in Oracle PL-SQL, at porting such procedures to Postgres is problematic without autonomous transaction support. |
| 22 | +</para> |
| 23 | + |
| 24 | +</sect1> |
| 25 | + |
| 26 | +<sect1 id="behaviour"><title>Behavior</title> |
| 27 | +<para> |
| 28 | +An AST can happen only inside another transaction. |
| 29 | +Inside an existing transaction (call it T0), the user can decide to start a subtransaction. Then T0 is paused and pushed in an AST stack, and a new transaction (call it T1) is started. |
| 30 | +</para> |
| 31 | +<para> |
| 32 | +At some point in the future the user can commit the subtransaction; after T1 is committed then T0 is popped from the AST stack and resumed. |
| 33 | +The user can also decide to COMMIT the parent transaction T0, in which case T1 is committed, then T0 is popped from the AST stack and then committed. |
| 34 | +</para> |
| 35 | +<para> |
| 36 | +All the transactions happen synchronously; at any time only one transaction can be active, while in the stack there are zero (or more) paused transactions in the stack. |
| 37 | +All the possible combinations of COMMIT / ROLLBACK for T0 and T1 can happen; for instance, it is possible to COMMIT T1 and ROLLBACK T0. |
| 38 | +It is possible to nest subtransactions, up to a global resource limit (e.g. the AST stack size) which can be set on the server. |
| 39 | +</para> |
| 40 | + |
| 41 | +</sect1> |
| 42 | + |
| 43 | +<sect1 id="example"><title>Example</title> |
| 44 | + |
| 45 | +<para> |
| 46 | +The following figure describes an example where a transaction executes a subtransaction. A continuous line denotes an active transaction, while a dotted line denotes a transaction which has been paused and pushed in the AST stack. Time flows downwards. |
| 47 | + |
| 48 | +<programlisting> |
| 49 | +BEGIN; -- start ordinary tx T0 |
| 50 | +| |
| 51 | +INSERT INTO t VALUES (1); |
| 52 | +:\ |
| 53 | +: BEGIN AUTONOMOUS SUBTRANSACTION; -- start AST tx T1, pushes T0 into stack |
| 54 | +: | |
| 55 | +: INSERT INTO t VALUES (2); |
| 56 | +: | |
| 57 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; -- ends tx T1, pops tx T0 from stack |
| 58 | +:/ |
| 59 | +COMMIT / ROLLBACK; -- ends tx T0 |
| 60 | +</programlisting> |
| 61 | + |
| 62 | +<para> |
| 63 | +Depending on the two choices between COMMIT and ROLLBACK we can get 4 different outputs from |
| 64 | +</para> |
| 65 | + |
| 66 | +<programlisting> |
| 67 | +SELECT sum(x) from t; |
| 68 | +</programlisting> |
| 69 | + |
| 70 | +</sect1> |
| 71 | +<sect1 id="example2"><title>Example 2 (more than one subtransaction)</title> |
| 72 | + |
| 73 | +<para> |
| 74 | +The parent transaction can have more than one subtransaction, just by repeating the application of the push/pop cycle. |
| 75 | +</para> |
| 76 | + |
| 77 | +<programlisting> |
| 78 | +BEGIN; -- start ordinary tx T0 |
| 79 | +| |
| 80 | +INSERT INTO t VALUES (1); |
| 81 | +:\ |
| 82 | +: BEGIN AUTONOMOUS TRANSACTION; -- start AST tx T1, pushes T0 into stack |
| 83 | +: | |
| 84 | +: INSERT INTO t VALUES (2); |
| 85 | +: | |
| 86 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; -- ends tx T1, pops tx T0 from stack |
| 87 | +:/ |
| 88 | +| |
| 89 | +:\ |
| 90 | +: BEGIN AUTONOMOUS TRANSACTION; -- start AST tx T2, pushes T0 into stack |
| 91 | +: | |
| 92 | +: INSERT INTO t VALUES (4); |
| 93 | +: | |
| 94 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; -- ends tx T2, pops tx T0 from stack |
| 95 | +:/ |
| 96 | +COMMIT / ROLLBACK; -- ends tx T0 |
| 97 | +</programlisting> |
| 98 | + |
| 99 | +</sect1> |
| 100 | + |
| 101 | +<sect1 id="visibility"><title>Visibility</title> |
| 102 | + |
| 103 | +<para> |
| 104 | +Visibility rules work as in the case of independent transactions executed via dblink. T1 does not see the effects of T0, because the latter has not been committed yet. T0 might see the effects of T1, depending on its own transaction isolation mode. |
| 105 | +In case of read committed isolation level parent transaction will see changes made by autonomous subtransaction. |
| 106 | +But in case of repeatable read isolation level parent transaction will not see changes made by autonomous subtransaction. |
| 107 | +</para> |
| 108 | + |
| 109 | +<para> |
| 110 | +Now single-session deadlocks become possible, because an AST can become entangled with one of the paused transactions in its session. |
| 111 | +Autonomous transaction T1 is assumed to depend on parent transaction T1 and if it attempt to obtain any resource locked by T0, then |
| 112 | +deadlock is reported. |
| 113 | +</para> |
| 114 | + |
| 115 | +</sect1> |
| 116 | + |
| 117 | +<sect1 id="sql-grammar"><title>SQL grammar extension for autonomous transactions</title> |
| 118 | + |
| 119 | +<para> |
| 120 | +Postgres <literal>BEGIN</literal>/<literal>END</literal> transaction statements are extended by by optional keyword <literal>autonomous</literal>: |
| 121 | +</para> |
| 122 | + |
| 123 | +<programlisting> |
| 124 | + BEGIN [AUTONOMOUS] [TRANSACTION] [isolation-level] |
| 125 | + END [AUTONOMOUS] [TRANSACTION] |
| 126 | +</programlisting> |
| 127 | + |
| 128 | +<para> |
| 129 | +Specifying autonomous keyword in end transaction clause is optional. |
| 130 | +It is possible to have several nesting levels of autonomous transactions, but top level transaction can not be autonomous. |
| 131 | +</para> |
| 132 | + |
| 133 | +</sect1> |
| 134 | + |
| 135 | +<sect1 id="plpgsql-grammar"><title>PL/pgSQL grammar extension for autonomous transactions</title> |
| 136 | + |
| 137 | +<para> |
| 138 | +Block construction in PL/pgSQL is extended by optional autonomous keyword. |
| 139 | +It is possible to treat all function body as autonomous transaction: |
| 140 | +</para> |
| 141 | + |
| 142 | +<programlisting> |
| 143 | +create function foo(x integer) returns integer as $$ |
| 144 | +begin autonomous |
| 145 | + return x; |
| 146 | +end; |
| 147 | +$$ language plpgsql; |
| 148 | +</programlisting> |
| 149 | + |
| 150 | +<para> |
| 151 | +or create separate begin-end block: |
| 152 | +</para> |
| 153 | + |
| 154 | +<programlisting> |
| 155 | +create or replace function myaudit() returns boolean as $$ |
| 156 | +begin autonomous |
| 157 | + begin autonomous |
| 158 | + insert into audit_schedule values ('new audit',now()); |
| 159 | + end; |
| 160 | + ... -- do audit itself |
| 161 | + return true; |
| 162 | +end; |
| 163 | +$$ language plpgsql; |
| 164 | +</programlisting> |
| 165 | + |
| 166 | +<para> |
| 167 | +When exception is raised inside BEGIN AUTONOMOUS block, this autonomous transaction is aborted and standard |
| 168 | +exception handling procedure is started, unwinding stack and executing exception handlers until exception is caught. |
| 169 | +So exception handling is done i the same way as with normal Postgres subtransactions. |
| 170 | +</para> |
| 171 | +<para> |
| 172 | +When an error is caught by an EXCEPTION clause, the local variables of the PL/pgSQL function remain as they were when the error occurred, but all changes to persistent database state within the block are rolled back. |
| 173 | +</para> |
| 174 | + |
| 175 | +</sect1> |
| 176 | + |
| 177 | +<sect1 id="plpython-grammar"><title>PL/python extension for autonomous transactions</title> |
| 178 | + |
| 179 | +<para> |
| 180 | +In addition to <varname>subtransaction</varname> method, Plpython module provides new <varname>autonomous</varname> method |
| 181 | +which can be used in <varname>>WITH</varname> clause to start autonomous transaction: |
| 182 | +</para> |
| 183 | + |
| 184 | +<programlisting> |
| 185 | +create or replace function pythonomous() returns void as $$ |
| 186 | + plpy.execute("insert into atx_test values ('asd', 123)") |
| 187 | + |
| 188 | + try: |
| 189 | + with plpy.autonomous(): |
| 190 | + plpy.execute("insert into atx_test values ('bsd', 456)") |
| 191 | + except plpy.SPIError, e: |
| 192 | + print("error: %s" % e.args) |
| 193 | + |
| 194 | + plpy.execute("insert into atx_test values ('csd', 'csd')") |
| 195 | +$$ language plpythonu; |
| 196 | +</programlisting> |
| 197 | + |
| 198 | +<para> |
| 199 | +Exception handling for autonomous transaction in plpython is done in the same way as for subtransactions. |
| 200 | +</para> |
| 201 | + |
| 202 | +</sect1> |
| 203 | +</chapter> |
0 commit comments