Why Oracle says 'SET TRANSACTION' is one means of transaction beginning - oracle

In Oracle document http://docs.oracle.com/cd/E11882_01/server.112/e25789/transact.htm#CNCPT88952, it says:
A transaction begins when the first executable SQL statement is
encountered. An executable SQL statement is a SQL statement that
generates calls to a database instance, including DML and DDL
statements and the SET TRANSACTION statement.
When a transaction begins, Oracle Database assigns the transaction to
an available undo data segment to record the undo entries for the new
transaction.
My understandings of the preceding statements are,
SET TRANSACTION is one way to beginning a transaction;
a mile stone of the transaction beginning is available undo data segment is assigned;
transaction id is part of available undo data segment, therefore will be assigned after running the SET TRANSACTION.
But my following test doesn't satisfy my theory,
SQL> -- I suppose there's no other session is using this database
SQL> show rel;
release 1102000200
SQL> set transaction name 't1';
Transaction set.
SQL> SELECT XID, XIDUSN, XIDSLOT, STATUS from v$transaction;
no rows selected
It surprise me that there's no rows in transaction view. What's wrong with my hypothesis?

You skipped the last, most important sentence from the quoted text:
A transaction begins when the first executable SQL statement is encountered. An executable SQL statement is a SQL statement that generates calls to a database instance, including DML and DDL statements and the SET TRANSACTION statement.
When a transaction begins, Oracle Database assigns the transaction to an available undo data segment to record the undo entries for the new transaction. A transaction ID is not allocated until an undo segment and transaction table slot are allocated, which occurs during the first DML statement. A transaction ID is unique to a transaction and represents the undo segment number, slot, and sequence number.
SET TRANSACTION is not a DML (Data Manipulation Language), it is a transaction control statement, see this link for details: http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_1001.htm
You need to run DML statement in order to see the transaction is V$Transactions view.
======= EDIT - SET TRANSACTION syntax (parameters) ===============
See this link for details: http://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_10005.htm#SQLRF01705

Related

What is meant by PRAGMA AUTONOMOUS_TRANSACTION?

I need a clarity about pragma autonomous transaction. I have used a overlap select query in trigger and insert query in procedure. If I import two records in single file, the 2nd record is same as 1st record so the second record should be shown as overlap error. Now the select query in trigger executes but the error is not thrown by using the pragma autonomous transaction.
AUTONMOUS_TRANSACTION is a nested transaction. It executes DML independently of the calling transaction. So a query issued in an autonomous transaction won't see any uncommitted changes in the outer transaction. This is why you don't see your error message: the invalid state only exists in the uncommitted changes of the transaction.
Obviously you are using AUTONMOUS_TRANSACTION to avoid a mutating table error. However, a better solution would be to use a COMPOUND DML trigger: use a FOR EACH ROW to store the changes in an array, then verify them for no overlaps in the AFTER statement stage. Find out more.

Database read locking

I have a use case where I need to do the following things in one transaction:
start the transaction
INSERT an item into a table
SELECT all the items in the table
dump the selected items into a file (this file is versioned and another program always uses the latest version)
If all the above things succeed, commit the transaction, if not, rollback.
If two transactions begin almost simultaneously, it is possible that before the first transaction A commits what it has inserted into the table (step 4), the second transaction B has already performed the SELECT operation(step 2) whose result doesn't contain yet the inserted item by the first transaction(as it is not yet committed by A, so not visible to B). In this case, when A finishes, it will have correctly dumped a file File1 containing its inserted item. Later, B finishes, it will have dumped another file File2 containing only its inserted item but not the one inserted by A. Since File2 is more recent, we will use File2. The problem is that File2 doesn't contain the item inserted by A even though this item is well in the DB.
I would like to know if it is feasible to solve this problem by locking the read(SELECT) of the table when a transaction inserts something into the table until its commit or rollback and if yes, how this locking can be implemented in Spring with Oracle as DB.
You need some sort of synchronization between the transactions:
start the transaction
Obtain a lock to prevent the transaction in another session to proceed or wait until the transaction in the other session finishes
INSERT an item into a table
SELECT ......
......
Commit and release the lock
The easiest way is to use LOCK TABLE command, at least in SHARE mode (SHARE ROW EXCLUSIVE or EXCLUSIVE modes can also be used, but they are too restrictve for this case).
The advantage of this approach is that the lock is automatically released at commit or rollback.
The disadvantage is a fact, that this lock can interfere with other transactions in the system that update this table at the same time, and could reduce an overall performance.
Another approach is to use DBMS_LOCK package. This lock doesn't affect other transactions that don't explicitely use that lock. The drawaback is that this package is difficult to use, the lock is not released on commit nor rollback, you must explicitelly release the lock at the end of the transaction, and thus all exceptions must be carefully handled, othervise a deadlock easily could occur.
One more solution is to create a "dummy" table with a single row in it, for example:
CREATE TABLE my_special_lock_table(
int x
);
INSERT INTO my_special_lock_table VALUES(1);
COMMIT:
and then use SELECT x FROM my_special_lock_table FOR UPDATE
or - even easier - simple UPDATE my_special_lock_table SET x=x in your transaction.
This will place an exclusive lock on a row in this table and synchronize only this one transaction.
A drawback is that another "dummy" table must be created.
But this solution doesn't affect the other transactions in the system, the lock is automatically released upon commit or rollback, and it is portable - it should work in all other databases, not only in Oracle.
Use spring's REPEATABLE_READ or SERIALIZABLE isolation levels:
REPEATABLE_READ A constant indicating that dirty reads and
non-repeatable reads are prevented; phantom reads can occur. This
level prohibits a transaction from reading a row with uncommitted
changes in it, and it also prohibits the situation where one
transaction reads a row, a second transaction alters the row, and the
first transaction rereads the row, getting different values the second
time (a "non-repeatable read").
SERIALIZABLE A constant indicating that dirty reads, non-repeatable
reads and phantom reads are prevented. This level includes the
prohibitions in ISOLATION_REPEATABLE_READ and further prohibits the
situation where one transaction reads all rows that satisfy a WHERE
condition, a second transaction inserts a row that satisfies that
WHERE condition, and the first transaction rereads for the same
condition, retrieving the additional "phantom" row in the second read.
with serializable or repeatable read, the group will be protected from non-repeatable reads:
connection 1: connection 2:
set transaction isolation level
repeatable read
begin transaction
select name from users where id = 1
update user set name = 'Bill' where id = 1
select name from users where id = 1 |
commit transaction |
|--> executed here
In this scenario, the update will block until the first transaction is complete.
Higher isolation levels are rarely used because they lower the number of people that can work in the database at the same time. At the highest level, serializable, a reporting query halts any update activity.
I think you need to serialize the whole transaction. While a SELECT ... FOR UPDATE could work, it does not really buy you anything, since you would be selecting all rows. You may as well just take and release a lock, using DBMS_LOCK()

Can I create a trigger in which it creates a new user?

if it is possible... how can do that? i'm new in databases , please help me!
i'm using oracle and I want when create a new user when I insert a new row in a table "users"
As JGreenwell has mentioned, yes, you can do it, but is is not a good idea. The main problem is not the password in clear, is the transactional side effects.
Let's see the solution to password in clear:
It is easy to solve the problem of the password (do not store password in clear, encrypt it or hash it). If you have installed DMBS_CRYPTO and have privileges on it then you can create a procedure (I assume you have a table USERS with to columns: USERNAME and USERPASS):
CREATE OR REPLACE PROCEDURE MYCREATEUSER( NAME VARCHAR, PASS VARCHAR )
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'CREATE USER ' || NAME || ' IDENTIFIED BY "' || PASS || '"';
END;
and a trigger:
CREATE OR REPLACE TRIGGER TUSERS BEFORE INSERT ON USERS FOR EACH ROW
BEGIN
MYCREATEUSER(:NEW.USERNAME, :NEW.USERPASS);
:NEW.USERPASS := DBMS_CRYPTO.HASH(:NEW.USERPASS, DBMS_CRYPTO.HASH_SH1);
END;
Your insert statement uses the username and the password, the trigger creates the user, but before inserting the row it replaces the password with its SHA1 hash value. You must remember this when you query the table.
One example:
INSERT INTO USERS VALUES('Mary', 'Mary123');
To check:
SELECT COUNT(*)
FROM USERS
WHERE USERNAME = 'Mary' AND
USERPASS = DBMS_CRYPTO.HASH('Mary123', DBMS_CRYPTO.HASH_SH1);
If count = 1 then the user and password exists, but if count = 0 the user/password does not exists.
This example only works if: you have installed DBMS_CRYPTO and has enough privileges, and you have CREATE TABLE privileges (remember that when a procedure executes statements it only has explicit granted privileges, privileges that came from ROLES are disabled - so, it is not enough to have the RESOURCE privilege, you need the CREATE TABLE explicit privilege).
But as I have mentioned it is not a good idea, because you can be affected by transactional side effects. Let's see the main problem:
To maintain ACID principles (Atomicity, Consistency, Isolation and Durability) Oracle needs to ensure that one statement is viewed as an Atomic operation. If I execute INSERT INTO USERS SELECT * FROM OTHER_USER_TABLES this statement must be viewed as an atomic unit. The atomic unit starts when you send the statement and ends when oracle notifies you the error/ok code.
But trigger is executed inside the statement (inside its temporal scope). The trigger controls the insert statement (if it fails all the statement fails, if it says ok, the statement is ok). Note that the statement ends after the triggers finalization. This means that it is not possible to commit or rollback the current transaction before the statement ends (because in this case the Atomicity is not ensured). So, a trigger cannot COMMIT/ROLLBACK the current transaction (it cannot commit and it cannot call a procedure to commit it, nobody can do it before statement ends).
CREATE TABLE is an autocommit statement (if forces a commit) so it is not possible to CREATE a table inside the trigger scope.
But we say "you can" and we have a working example. How?
The answer is the PRAGMA AUTONOMOUS_TRANSACTION.
This pragma forces oracle to execute the affected PL/SQL block in a new different transaction. So, in our example, oracle executes MYCREATEUSER procedure in a new transaction.
Oracle does not have a nested transaction model, so the inner AUTONOMOUS_TRANSACTION does not depend on the outer trigger's transaction. They are both flat transactions. This means that if the procedures ends (the user is created) and the insert finally fails (due to any reason) the row is not in the table but the new user exists.
Let's see an example:
INSERT INTO USERS VALUES ('Anne', 'An232131');
INSERT INTO USERS VALUES ('Mike', "ABC123');
ROLLBACK;
This examples inserts two rows (it creates two users) and rollback the transaction. So, finally the inserts are canceled and the rows (Anne an Mike) are not in users table. But Anne and Mike Oracle's users exists (the rollback) does not affect the user creation because they have been created by a different transaction that finally commited (the autonomous transaction).
This problem is not easy to solve without a nested transactional model (in a nested transactional model a inner transaction only finally commits when its outer transaction does).

exception in stored procedure

I have following procedure:
procedure mayFailProc() as
begin
insert into t1 (id, val) values (1, '123');
insert into t1 (id, val) values (2, '123');
insert into t1 (id, val) values (3, '123'); //fails, i.e. due to pk uniqueness error
end;
this exception thrown in mayFailProc is a normal thing and it is handled by its caller. So the transaction is not rolled back and execution continues as if there was no exception in mayFailProc. I wonder what will happen to first two successfully executed inserts? Will they be retained or not?
See here for Oracle's explanation. You can jump to the How Oracle Does Implicit Rollbacks section to start.
Before executing an INSERT, UPDATE, or DELETE statement, Oracle marks
an implicit savepoint (unavailable to you). If the statement fails,
Oracle rolls back to the savepoint. Usually, just the failed SQL
statement is rolled back, not the whole transaction. If the statement
raises an unhandled exception, the host environment determines what is
rolled back.
More:
You should explicitly commit or roll back every transaction. Whether
you issue the commit or rollback in your PL/SQL program or from a
client program depends on the application logic. If you do not commit
or roll back a transaction explicitly, the client environment
determines its final state.
For example, in the SQL*Plus environment, if your PL/SQL block does
not include a COMMIT or ROLLBACK statement, the final state of your
transaction depends on what you do after running the block. If you
execute a data definition, data control, or COMMIT statement or if you
issue the EXIT, DISCONNECT, or QUIT command, Oracle commits the
transaction. If you execute a ROLLBACK statement or abort the SQL*Plus
session, Oracle rolls back the transaction.
Just done a quick test... the rows in my test were not retained when the third insert caused a "unique constraint ... violated" error

triggers statement level atomicity

What does Oracle mean by "statement level atomicity"?
Let's cite a couple of chapters from the Concepts:
Statement-Level Read Consistency
Oracle always enforces statement-level read consistency. This guarantees that all the data returned by a single query comes from a single point in time--the time that the query began. Therefore, a query never sees dirty data nor any of the changes made by transactions that commit during query execution. As query execution proceeds, only data committed before the query began is visible to the query. The query does not see changes committed after statement execution begins.
Statement-Level Rollback
If at any time during execution a SQL statement causes an error, all effects of the statement are rolled back. The effect of the rollback is as if that statement had never been run. This operation is a statement-level rollback.
A SQL statement that fails causes the loss only of any work it would have performed itself. It does not cause the loss of any work that preceded it in the current transaction.
It means that any single SQL statement you run is atomic in nature - it will either succeed completely or fail completely. If your SQL statement fails, and triggers that would have run as a result of that SQL statement will fail as well.
This is a statement about the nature of transactions: a Unit Of Work either succeeds or completes in its entirety. If your transaction comprises two inserts plus an update, and the update statement fails, the database will rollback all three statements. Any recursive SQL or other SQL such as that included in triggers is included in the atomic scope of the transaction. Find out more.
Atomicity means the A in the ACID principles of transaction management.

Resources