where xid identifies the transaction's local or global transaction ID. To find these transaction IDs, query the data dictionary view DBA_2PC_PENDING. Any decisions to force in−doubt transactions should be made after consulting with the database administrator(s) at the remote database location(s). If the decision is made to locally force any transactions, the database administrator should either commit or roll back such transactions (as was done by nodes that successfully resolved the transactions). Otherwise, the administrator should query the DBA_2PC_PENDING view's ADVICE and TRAN_COMMENT columns for further insight. For more information on this topic, see "Manually Overriding In−Doubt Transactions" in the Oracle Corporation document Oracle8 Server Distributed Systems. 4.2.4.3 The DBMS_TRANSACTION.SAVEPOINT procedure The SAVEPOINT procedure is equivalent to the SAVEPOINT command, which is already implemented as part of PL/SQL. The header for this procedure is, PROCEDURE DBMS_TRANSACTION.SAVEPOINT (savept IN VARCHAR2); where savept specifies the savepoint. Why would you use this procedure and not simply rely on the SAVEPOINT command? When you use SAVEPOINT, you must use an "undeclared identifier" for the savepoint: BEGIN SAVEPOINT right_here; do_my_stuff; EXCEPTION WHEN OTHERS THEN ROLLBACK TO right_here; END; The "right_here" identifier is not declared anywhere; it is simply hard−coded into your application. With the DBMS_TRANSACTION programs, you can soft code these savepoint names, as you can see from the following block: DECLARE v_sp VARCHAR2(30) := 'right_here'; BEGIN DBMS_TRANSACTION.SAVEPOINT (v_sp); do_my_stuff; EXCEPTION WHEN OTHERS THEN /* Soft−coded rollback to, as well! */ DBMS_TRANSACTION.ROLLBACK_SAVEPOINT (v_sp); END; / 4.2.4.4 The DBMS_TRANSACTION.ROLLBACK_SAVEPOINT procedure The ROLLBACK_SAVEPOINT procedure is equivalent to the ROLLBACK TO command in PL/SQL. The header for this procedure is, PROCEDURE DBMS_TRANSACTION.ROLLBACK_SAVEPOINT (savept IN VARCHAR2); where savept specifies the savepoint. [Appendix A] What's on the Companion Disk? 4.2.4 Rolling Back Changes 226 You should use this program in coordination with the SAVEPOINT procedure, as illustrated in the example in the previous section. With this program, you can roll back to a savepoint that is not hard−coded into your application. 4.2.4.5 The DBMS_TRANSACTION.USE_ROLLBACK_SEGMENT procedure The USE_ROLLBACK_SEGMENT procedure assigns the current transaction to the specified rollback segment. This option also establishes the transaction as a read−write transaction. Here's the specification for the procedure: PROCEDURE DBMS_TRANSACTION.USE_ROLLBACK_SEGMENT (rb_name IN VARCHAR2); The specified rollback segment (rb_name) must be online. Often, the rollback specified is a large one that is kept offline during the day and is specifically enabled at night for large batch jobs. You cannot use both the DBMS_TRANSACTION.READ_ONLY (see next section) and the USE_ROLLBACK_SEGMENT procedures within the same transaction. Read−only transactions do not generate rollback information and thus cannot be assigned rollback segments. In the following example, we have modified our data warehousing extract promotion program to reference the staging fact table remotely in order to load our local fact table −− and to do so utilizing the big rollback segment. This version of the code has the advantage that it could be run without change from each remote database (assuming that each remote database had a big rollback segment named BIG_RBS). BEGIN DBMS_TRANSACTION.USE_ROLLBACK_SEGMENT('BIG_RBS'); INSERT INTO fact_table SELECT * FROM fact_table@staging; COMMIT; END; / 4.2.5 Setting Transaction Characteristics DBMS_TRANSACTION offers several programs that set various characteristics of the transaction for your session. 4.2.5.1 The DBMS_TRANSACTION.READ_ONLY procedure The READ_ONLY procedure establishes transaction−level read consistency (i.e., repeatable reads). Here's the header for this program: PROCEDURE DBMS_TRANSACTION.READ_ONLY; Once a transaction is designated as read−only, all queries within that transaction can see only changes committed prior to that transaction's start. Hence, read−only transactions permit you to issue two or more queries against tables that may be undergoing concurrent inserts or updates, and yet return results consistent as of the transaction's start. The READ_ONLY procedure is quite useful for reports. Long−running read−only transactions can receive a "snapshot too old" error (ORA−01555). If this occurs, increase rollback segment sizes. In the following example, we can separately query the order and item tables successfully, regardless of other transactions. Even if someone deletes the item we are interested in while we query the order, a read−consistent image will be maintained in the rollback segments. So we will always be able to see the items for any order we look at. [Appendix A] What's on the Companion Disk? 4.2.4 Rolling Back Changes 227 DECLARE lv_order_count INTEGER := 0; lv_item_count INTEGER := 0; BEGIN DBMS_TRANSACTION.READ_ONLY; SELECT COUNT(*) INTO lv_order_count FROM order WHERE order_number = 12345; SELECT COUNT(*) INTO lv_item_count FROM item WHERE order_number = 12345; END; / 4.2.5.2 The DBMS_TRANSACTION.READ_WRITE procedure The READ_WRITE procedure establishes the current transaction as a read−write transaction. As this is the default transaction mode, you will not often need to use this procedure. The header for this program follows: PROCEDURE DBMS_TRANSACTION.READ_WRITE; The following example demonstrates using READ_WRITE in a transaction where we want to delete an order and its associated items. However, this example would execute in exactly the same way, even if the READ_WRITE procedure call were commented out. BEGIN DBMS_TRANSACTION.READ_WRITE; DELETE FROM item WHERE order_number = 12345; DELETE FROM order WHERE order_number = 12345; END; / 4.2.5.3 The DBMS_TRANSACTION.BEGIN_DISCRETE_TRANSACTION procedure The BEGIN_DISCRETE_TRANSACTION procedure streamlines transaction processing so that short transactions can execute more rapidly. The header for this program follows: PROCEDURE DBMS_TRANSACTION.BEGIN_DISCRETE_TRANSACTION; During discrete transactions, normal redo information is generated, although it is stored in a separate location in memory. When the discrete transaction commits, the redo information is written to the redo log file and data block changes are applied directly. As a result, there is no need for undo information in rollback segments. The block is then written to the database file in the usual manner. The call to this procedure is effective only until the transaction is committed or rolled back; the next transaction is processed as a standard transaction. 4.2.5.3.1 Restrictions Although discrete transactions offer improved performance, there are numerous restrictions: • The database initialization parameter DISCRETE_TRANSACTIONS_ENABLED must be set to TRUE; otherwise, calls to this procedure are ignored and transactions function normally. • Discrete transactions cannot be distributed transactions. • Discrete transactions can change each database block only once. • Discrete transactions cannot see their own changes (since there are no rollback segments). • [Appendix A] What's on the Companion Disk? 4.2.5 Setting Transaction Characteristics 228 Discrete transactions cannot perform inserts or updates on both tables involved in a referential integrity constraint. • Discrete transactions cannot modify tables containing any LONG values. 4.2.5.3.2 Exceptions This procedure may raise either of the following exceptions: DBMS_TRANSACTION.DISCRETE_TRANSACTION_FAILED DBMS_TRANSACTION.CONSISTENT_READ_FAILURE 4.2.5.3.3 Example In the following example, we have modified the last version of our data warehousing extract promotion program to utilize discrete transactions. The code is written in such a way that it ensures that the transaction is attempted again in the event of a discrete transaction failure. This coding practice should be followed strictly. BEGIN FOR fact_rec in (SELECT * FROM fact table@staging) LOOP DBMS_TRANSACTION. BEGIN_DISCRETE_TRANSACTION; FOR I in 1 2 LOOP BEGIN INSERT INTO fact_table VALUES (fact_rec.product_id, fact_rec.location_id, fact_record.period_id, fact_rec.numeric_value1, fact_rec.numeric_value2); COMMIT; EXIT; EXCEPTION WHEN DBMS_TRANSACTION.DISCRETE_TRANSACTION_FAILED THEN ROLLBACK; END; END LOOP; END LOOP; END; / For more information on this topic, see "Using Discrete Transactions" in the Oracle Corporation document Oracle8 Server Tuning. 4.2.6 Cleaning Up Transaction Details When performing two−phase commits, it is possible to "strand" information about recovery or resolution steps. The two procedures PURGE_MIXED and PURGE_LOST_DB_ENTRY are provided by Oracle to clean up that information. 4.2.6.1 The DBMS_TRANSACTION.PURGE_MIXED procedure The PURGE_MIXED procedure deletes information about a given in−doubt, distributed transaction that has had mixed outcomes as the consequence of transaction resolution mismatch. This occurs when an in−doubt, distributed transaction is forced to commit or roll back on one node, and other nodes do the opposite. For example, we may force commit on node 1 a distributed transaction that rolled back on node 2. Oracle cannot [Appendix A] What's on the Companion Disk? 4.2.5 Setting Transaction Characteristics 229 automatically resolve such inconsistencies, but it does flag entries in the DBA_2PC_PENDING view by setting the MIXED column to "yes." When the database administrator is sure that any inconsistencies for a transaction have been resolved, he or she can call PURGE_MIXED procedure. The specification for the procedure is, PROCEDURE DBMS_TRANSACTION.PURGE_MIXED (xid IN VARCHAR2); where xid identifies the mixed transaction's local transaction ID, which can be found in the LOCAL_TRAN_ID column of the data dictionary view DBA_2PC_PENDING. For more information on this topic, see "Manually Overriding In−Doubt Transactions" in the Oracle Corporation document Oracle8 Server Distributed Systems. 4.2.6.2 The DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY procedure The PURGE_LOST_DB_ENTRY procedure deletes information about a given in−doubt, distributed transaction that has had mixed outcomes as the consequence of a lost database. This occurs when an in−doubt, distributed transaction is able to commit or roll back on one node and other nodes have either destroyed or recreated their databases. For example, we may successfully commit on node 1 a distributed transaction that is no longer represented in the recreation of the database now on node 2. Oracle cannot automatically resolve such inconsistencies. The information in DBA_2PC_PENDING will never be deleted, and Oracle will try periodically to recover (even though it can't). When the database administrator is sure that any inconsistencies for a transaction have been resolved, he or she can call the PURGE_MIXED procedure. The specification for the procedure is, PROCEDURE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY (xid IN VARCHAR2); where xid identifies the mixed transaction's local transaction ID, which can be found in the LOCAL_TRAN_ID column of the data dictionary view DBA_2PC_PENDING. Oracle's dbmsutil.sql file, which contains the definition of the DBMS_TRANSACTION package, offers some insights into when and how you would use this program. According to that file, the DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY procedure should only be used when the other database is lost or has been recreated. Any other use may leave that other database in an unrecoverable or inconsistent state. Before you run automatic recovery, the transaction appears in the DBA_2PC_PENDING view in the state "collecting," "committed," or "prepared." If the DBA has forced an in−doubt transaction to have a particular result by using the COMMIT FORCE or ROLLBACK FORCE options, then the additional states FORCED COMMIT or FORCED ROLLBACK may also appear. Automatic recovery will normally delete entries that are in any of these states. The only exception occurs when the recovery process finds a forced transaction that is in a state inconsistent with other sites in the transaction. In this case, the entry will be left in the table and the MIXED column will be given a value of "yes." Under certain conditions, it may not be possible for an automatic recovery to execute without errors. For example, a remote database may have been permanently lost. In this case, even if it is recreated, it will be assigned a new database ID. As a result, recovery cannot identify it. (A possible symptom for this situation is when the ORA−02062 error is raised.) [Appendix A] What's on the Companion Disk? 4.2.6 Cleaning Up Transaction Details 230 . information on this topic, see "Manually Overriding In−Doubt Transactions" in the Oracle Corporation document Oracle8 Server Distributed Systems. 4.2.4.3 The DBMS_TRANSACTION.SAVEPOINT procedure The. more information on this topic, see "Using Discrete Transactions" in the Oracle Corporation document Oracle8 Server Tuning. 4.2.6 Cleaning Up Transaction Details When performing two−phase. information on this topic, see "Manually Overriding In−Doubt Transactions" in the Oracle Corporation document Oracle8 Server Distributed Systems. 4.2.6.2 The DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY