alter table constraint on unlock oracle - oracle

I am trying to write a script to be executed by a client who has no real knowledge of pl/sql,
I do a bunch of transactions in PL/sql to clean up their landscape and then have to add in some constraints to keep this from happening again, I commit automatically after the cleaning, and would like to finish my PL block with adding these constraints
only issue is I have no guarantee that the tables will not be locked when trying to add constraints to them, is there a wait until unlock type of command in oracle?
thank you, new to oracle and cannot seem to find this, I have combed through a bit of API but am at the point of time sensitivity and is proving very difficult to find when i think it seems like it would be a pretty regular issue with DB management

in your pl/sql block before execute any DDL you should call via execute immedaite :
LOCK TABLE <table_name> IN EXCLUSIVE MODE WAIT <n>;
where is number of seconds to wait.
But bear in mind even if you get lock after your first ddl it will be released because DDL commits automatically. So there is no way to guarantee that list of DDL commands will be executed one-by-one as in one transaction.

The command would be:
LOCK TABLE tab IN EXCLUSIVE MODE;
This will wait indefinitely if there is another session with a similar lock (which there should not be).
You can lock a table explicitly (with "WAIT") prior to a DDL, but that won't necessarily solve all of your problems if you are worried about another session holding an exclusive lock. Sounds more like you may have experienced an ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired due to a DML transaction in another session. This will eventually finish, so issuing the above lock command prior to ALTER TABLE will help you here. Just be aware that if there is a hung session elsewhere the LOCK TABLE command can wait indefinitely unless you have set a timeout.
So lets do an example:
In session A I start a transaction on tab
SQL> INSERT INTO tab VALUES(...);
In session B I attempt to add a constraint to the table.
SQL> alter table tab add constraint uk_name unique(name);
alter table tab add constraint uk_name unique(name)
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expire
So I'd have to try again. But instead if you precede your DDL with a LOCK ... EXCLUSIVE (WAIT)
SQL> lock table tab in exclusive mode;
-- indefinite period while the session A transaction blocks session B
Then session A commits or rolls back
SQL> commit;
Session B immediately continues when we see the lock command return Table(s) locked
Table(s) Locked.
SQL> alter table tab add constraint uk_name unique(name);
Table altered. (**comment Lock is released by the implicit commit issued the DDL statement)
SQL>
Since the DDL statement releases the lock, each DDL will need to be preceded with a fresh LOCK statement. If you want to use this, I recommend adding a timeout (as suggested in comments by Justin). Lets wait up to a minute before giving up.
SQL> lock table tab in exclusive mode WAIT 60;
Although if this is an unattended script on a busy database, I'd probably go with something more than 60 seconds. Just log everything to a spool file, and check the log later for errors.
Anything more than this sort of maintenance probably requires that you quiesce your database first, else just deal with the contention on a case by case basis.

Related

Oracle Lock , How do they differ?

What is the difference between the below two erros.as far as i understood, they happen in case of a Lock. But do you know the differene in scenarios where one might occure.
ORA-04021: timeout occurred while waiting to lock object
and
ORA-00054: resource busy and acquire with NOWAIT specified
Example for ORA-04021 might be this: there's a package in your schema. It contains a procedure which does some job that takes 15 minutes to finish. Someone runs that procedure. Meanwhile, you'd want to fix something in that package, so you edit its code and want to compile it. Well, you can't - it is being used so you'll have to wait until it is released. Oracle tells you that timeout occurred while you're waiting to lock the package and compile it.
Example for ORA-00054: there's a table. You update some values in it, but didn't commit (nor rollback) as you have to do something else as well. In another session, another user wants to alter one of table's columns (for example, enlarge its size). ALTER will then raise 0RA-00054 which says that table is busy (you're updating it in another session, right?) so you'll have to wait until transaction commits (or rollbacks).

Select * from table#dblink in PL/SQL Developer

When I run query select * from table#dblink in PL/SQL Developer,transaction commit/rollback icons are activated, but then if I use Fetch last page these icons are disabled. Why is this happening?
Querying over a db_link flips the 'we have a transaction' switch in the data dictionary
In most tools, you'll get a prompt for COMMIT or an indicator of an open transaction whenever you query against a DB_LINK.
That's because you're doing 'something' that's not clear to us in a different database. Your 'SELECT' could have side effects which require a COMMIT/ROLLBACK, or as Tom would say
'If you are distributed, you would want to commit to finish off anything that was implicitly started on the remote site.'
I think that PL/SQL is trying to remove useless transactions to help avoid session errors. It seems that whenever you press the "Fetch last page" button, PL/SQL Developer runs commit write batch if the statement contains a database link, if there are no transactions currently open in the session, and if the statement does not include FOR UPDATE.
Those are a lot of weird conditions, but they seem to ensure that the program won't commit when it shouldn't. I assume PL/SQL Developer is using commit write batch to use less resources than a normal commit. That guess is based on the number returned by this query increasing when I hit the button. (There's another statistic for user commits, and that number does not increase.)
select value
from v$mystat
join v$statname on v$mystat.statistic# = v$statname.statistic#
where lower(display_name) = 'commit batch performed';
This behavior is a little odd, but it could help prevent some errors in the session. For example, if you later try to run alter session enable parallel dml the session would throw the error ORA-12841: Cannot alter the session parallel DML state within a transaction. By committing the (worthless) transaction, you avoid some of those errors.

Set timeout value for ALTER TABLE ADD COLUMN in Oracle (DDL_LOCK_TIMEOUT not works)

Question
How I can set a timeout value for nonblocking DDL (ALTER TABLE add column) in oracle so that if any DML lock the table for long time (several hours), my DDL can fast-fail instead of waiting for hours. (we expect oracle raise error like ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired to interrupt our DDL)
P.S: DDL_LOCK_TIMEOUT is not working (refer 'What I tried' below)
Background
I'm working on a big oracle database (Oracle Database 19c). There are legacy application every hour will do aggregation job to calculate the data in past hour, like AVG, SUM of the counters. The production has 40 CPUs and 200GB+ memory, normally the aggregation job will run around 30 minutes, but in some case, like due to maintenance break the aggregation jobs are delayed, more data need to be handle in next aggregation job cause the job running for few hours.
Those legacy applications are out of my control. It's not possible to change the aggregation job.
Edition-Based Redefinition is not used.
My work is update database table (due to new counter added). We use ALTER TABLE to add new column to the existing tables. But in some case, the aggregation job lock the table for hours make my script hang there for hours. It make customer unhappy. So I want to make my script fast-fail.
What I tried
By google a long time, seems DDL_LOCK_TIMEOUT is the simplest solution.
However, based on the test, we notice that DDL_LOCK_TIMEOUT is not works in our case. By a long time google again, we found Oracle document here clearly mentioned:
The DDL_LOCK_TIMEOUT parameter affects blocking DDL statements (but not nonblocking DDL statements)
ALTER TABLE add column is exactly 'nonblocking DDL' as listed in List of Nonblocking DDLs
Expectation
When a DML lock the table for 1 hours, like SELECT * FROM MY_TABLE FOR UPDATE and commit after 1 hours. I want my DDL like ALTER TABLE MY_TABLE ADD (COL_A number) can get timeout after 10 minutes instead of wait for 1 hour.
Other Solutions
1
There have one solution in my mind that we can first issue a lock table MY_TABLE IN EXCLUSIVE MODE wait 600 to get the lock fist. But before we go with this solution, I want to seek is there any simple solution just like DDL_LOCK_TIMEOUT to set only one parameter.
2
Based on oracle doc, enable Supplemental Logging able to downgrade the nonblocking DDL to blocking way. But Supplemental Logging is DB level configuration. I do not have the permission to do such change.

What is the reason for ORA-00054 error?

From Oracle's documentation:-
ORA-00054 resource busy and acquire with NOWAIT specified
Cause: Resource interested is busy.
Action: Retry if necessary.
In our code we issue a SELECT FOR UPDATE NOWAIT command to lock the row we are about to update.
Right now the logic is if it returns SQL error 54 then it is assumed that another user is trying to update that same record. Is this logic valid?
From Oracle's documentation it looks more like if the DB is overwhelmed then this might also cause this error to be thrown.
What are the possible reasons for this error, when we are only using the above SQL command?
The SELECT ... FOR UPDATE attempts to acquire an RS (Row Share) lock on the table and an X (eXclusive) lock on the row. If another session has an exclusive lock on the table (eg creating an index) or an exclusive lock on the row (update, delete, or select for update) then the query will wait for the other transaction to release the lock (commit or rollback generally) unless you have specified NOWAIT.
So one possibility is to not specify NOWAIT.
I don't recognise the situation where the database might throw this error due to being "overwhelmed".

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

Why am I getting this database error when I update a table?
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
Your table is already locked by some query. For example, you may have executed "select for update" and have not yet committed/rollbacked and fired another select query. Do a commit/rollback before executing your query.
from here ORA-00054: resource busy and acquire with NOWAIT specified
You can also look up the sql,username,machine,port information and get to the actual process which holds the connection
SELECT O.OBJECT_NAME, S.SID, S.SERIAL#, P.SPID, S.PROGRAM,S.USERNAME,
S.MACHINE,S.PORT , S.LOGON_TIME,SQ.SQL_FULLTEXT
FROM V$LOCKED_OBJECT L, DBA_OBJECTS O, V$SESSION S,
V$PROCESS P, V$SQL SQ
WHERE L.OBJECT_ID = O.OBJECT_ID
AND L.SESSION_ID = S.SID AND S.PADDR = P.ADDR
AND S.SQL_ADDRESS = SQ.ADDRESS;
Please Kill Oracle Session
Use below query to check active session info
SELECT
O.OBJECT_NAME,
S.SID,
S.SERIAL#,
P.SPID,
S.PROGRAM,
SQ.SQL_FULLTEXT,
S.LOGON_TIME
FROM
V$LOCKED_OBJECT L,
DBA_OBJECTS O,
V$SESSION S,
V$PROCESS P,
V$SQL SQ
WHERE
L.OBJECT_ID = O.OBJECT_ID
AND L.SESSION_ID = S.SID
AND S.PADDR = P.ADDR
AND S.SQL_ADDRESS = SQ.ADDRESS;
kill like
alter system kill session 'SID,SERIAL#';
(For example, alter system kill session '13,36543';)
Reference
http://abeytom.blogspot.com/2012/08/finding-and-fixing-ora-00054-resource.html
There is a very easy work around for this problem.
If you run a 10046 trace on your session (google this... too much to explain). You will see that before any DDL operation Oracle does the following:
LOCK TABLE 'TABLE_NAME' NO WAIT
So if another session has an open transaction you get an error. So the fix is... drum roll please. Issue your own lock before the DDL and leave out the 'NO WAIT'.
Special Note:
if you are doing splitting/dropping partitions oracle just locks the partition.
-- so yo can just lock the partition subpartition.
So...
The following steps fix the problem.
LOCK TABLE 'TABLE NAME'; -- you will 'wait' (developers call this hanging). until the session with the open transaction, commits. This is a queue. so there may be several sessions ahead of you. but you will NOT error out.
Execute DDL. Your DDL will then run a lock with the NO WAIT. However, your session has aquired the lock. So you are good.
DDL auto-commits. This frees the locks.
DML statements will 'wait' or as developers call it 'hang' while the table is locked.
I use this in code that runs from a job to drop partitions. It works fine. It is in a database that is constantly inserting at a rate of several hundred inserts/second. No errors.
if you are wondering. Doing this in 11g. I have done this in 10g before as well in the past.
This error happens when the resource is busy. Check if you have any referential constraints in the query. Or even the tables that you have mentioned in the query may be busy. They might be engaged with some other job which will be definitely listed in the following query results:
SELECT * FROM V$SESSION WHERE STATUS = 'ACTIVE'
Find the SID,
SELECT * FROM V$OPEN_CURSOR WHERE SID = --the id
In my case, I was quite sure it was one of my own sessions which was blocking. Therefore, it was safe to do the following:
I found the offending session with:
SELECT * FROM V$SESSION WHERE OSUSER='my_local_username';
The session was inactive, but it still held the lock somehow. Note, that you may need to use some other WHERE condition in your case (e.g. try USERNAME or MACHINE fields).
Killed the session using the ID and SERIAL# acquired above:
alter system kill session '<id>, <serial#>';
Edited by #thermz: If none of the previous open-session queries work try this one. This query can help you to avoid syntax errors while killing sessions:
SELECT 'ALTER SYSTEM KILL SESSION '''||SID||','||SERIAL#||''' immediate;' FROM V$SESSION WHERE OSUSER='my_local_username_on_OS'
This happens when a session other than the one used to alter a table is holding a lock likely because of a DML (update/delete/insert). If you are developing a new system, it is likely that you or someone in your team issues the update statement and you could kill the session without much consequence. Or you could commit from that session once you know who has the session open.
If you have access to a SQL admin system use it to find the offending session. And perhaps kill it.
You could use v$session and v$lock and others but I suggest you google how to find that session and then how to kill it.
In a production system, it really depends. For oracle 10g and older, you could execute
LOCK TABLE mytable in exclusive mode;
alter table mytable modify mycolumn varchar2(5);
In a separate session but have the following ready in case it takes too long.
alter system kill session '....
It depends on what system do you have, older systems are more likely to not commit every single time. That is a problem since there may be long standing locks. So your lock would prevent any new locks and wait for a lock that who knows when will be released. That is why you have the other statement ready. Or you could look for PLSQL scripts out there that do similar things automatically.
In version 11g there is a new environment variable that sets a wait time. I think it likely does something similar to what I described. Mind you that locking issues don't go away.
ALTER SYSTEM SET ddl_lock_timeout=20;
alter table mytable modify mycolumn varchar2(5);
Finally it may be best to wait until there are few users in the system to do this kind of maintenance.
select
c.owner,
c.object_name,
c.object_type,
b.sid,
b.serial#,
b.status,
b.osuser,
b.machine
from
v$locked_object a,
v$session b,
dba_objects c
where
b.sid = a.session_id
and
a.object_id = c.object_id;
ALTER SYSTEM KILL SESSION 'sid,serial#';
As mentioned in other answers, this error is caused by concurrent DML operations running in other sessions. This causes Oracle to fail to lock the table for DDL with the default NOWAIT option.
For those without admin permissions in the database or who cannot kill/interrupt the other sessions, you can also precede your DDL operation with:
alter session set DDL_LOCK_TIMEOUT = 30;
--Run your DDL command, e.g.: alter table, etc.
I was receiving this error repeatedly in a database with background jobs doing large insert/update operations, and altering this parameter in the session allowed the DDL to continue after a few seconds of waiting for the lock.
For further information, see the comment from rshdev on this answer, this entry on oracle-base or the official docs on DDL_LOCK_TIMEOUT.
Just check for process holding the session and Kill it. Its back to normal.
Below SQL will find your process
SELECT s.inst_id,
s.sid,
s.serial#,
p.spid,
s.username,
s.program FROM gv$session s
JOIN gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id;
Then kill it
ALTER SYSTEM KILL SESSION 'sid,serial#'
OR
some example I found online seems to need the instance id as well
alter system kill session '130,620,#1';
I had this error happen when I had 2 scripts I was running. I had:
A SQL*Plus session connected directly using a schema user account (account #1)
Another SQL*Plus session connected using a different schema user account (account #2), but connecting across a database link as the first account
I ran a table drop, then table creation as account #1.
I ran a table update on account #2's session. Did not commit changes.
Re-ran table drop/creation script as account #1. Got error on the drop table x command.
I solved it by running COMMIT; in the SQL*Plus session of account #2.
Your problem looks like you are mixing DML & DDL operations. See this URL which explains this issue:
http://www.orafaq.com/forum/t/54714/2/
I managed to hit this error when simply creating a table! There was obviously no contention problem on a table that didn't yet exist. The CREATE TABLE statement contained a CONSTRAINT fk_name FOREIGN KEY clause referencing a well-populated table. I had to:
Remove the FOREIGN KEY clause from the CREATE TABLE statement
Create an INDEX on the FK column
Create the FK
I solved this problem by closing one of my IDE tabs.
PL/SQL Developer
Version 10.0.5.1710
I also face the similar Issue. Nothing programmer has to do to resolve this error. I informed to my oracle DBA team. They kill the session and worked like a charm.
Solution given by Shashi's link is the best... no needs to contact dba or someone else
make a backup
create table xxxx_backup as select * from xxxx;
delete all rows
delete from xxxx;
commit;
insert your backup.
insert into xxxx (select * from xxxx_backup);
commit;

Resources