Is it possible/make sense to have COMMIT statement in SQL functions?
Technically, the answer ist yes. You can do the following:
create or replace function committest return number as
begin
update my_table set col = 'x';
commit;
return 1;
end;
/
declare
number n;
begin
n := committest();
end;
/
However, you can't do the following:
select committest() from dual;
this would be a commit during a query and thus result in a
ORA-14552: Cannot Perform a DDL Commit or Rollback Inside a Query or DML
Yes, you can do that if you make the function an autonomous transaction. That way it will not be part of the current transaction anymore.
create or replace function doIt
as
pragma autonomous_transaction;
begin
... code ...
commit;
end;
/
More documentation
No, it's not possible, see the documentation:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm
Restrictions on User-Defined Functions
.... In addition, when a function is called from within a query or DML
statement, the function cannot: ....
Commit or roll back the current transaction, create a savepoint or roll back to a savepoint, or alter the session or the system. DDL
statements implicitly commit the current transaction, so a
user-defined function cannot execute any DDL statements.
Related
I am trying to create a postgres before trigger to check the amount of records that are going to be deleted before it actually does. For example to not delete more than 5 records
You could achieve that with an AFTER DELETE statement-level trigger. Inside the trigger function you can count the number of affected rows and throw an exception if the count is too high. The exception will force a rollback of the transaction that initiated the delete.
create function prevent_delete()
returns trigger
as
$BODY$
declare
l_count integer;
begin
select count(*)
into l_count
from old_table;
if l_count > 5 then
raise exception 'Too many rows would be deleted';
end if;
return null;
end;
$BODY$
LANGUAGE plpgsql;
And then create the trigger:
create trigger prevent_mass_delete
after delete on the_table
referencing old table as old_table
for each statement
execute procedure prevent_delete();
Tables are updating in loop, but if error come in one of table than transaction failed and all the tables data updated is gone so provide me the solution in which each time any table is update that its progress can save.
d0
$$
declare g record;
declare tablename varchar(50);
BEGIN
--fetching tablename from catalog.table
for g in execute formate ('select table_name from catalog.table');
loop
tablename= lower(g.tablename);
--passing tablename to function for some execution
execute'select function('''||tablename||''')';
end loop;
end;
$$
The transaction won't fail if you trap the error.
BEGIN
execute your query
EXCEPTION WHEN unique_violation OR foreign_key_violation OR ... THEN
END;
When a function or codeblock is executed there is always already a transaction either created explicitly with a BEGIN or automatically. The BEGIN of the exception block acts as a SAVEPOINT in the transaction. When the error is trapped by the EXCEPTION part only the work after the BEGIN is lost because it rollsback to the savepoint.
When you let an error escape from the function a rollback of the whole transaction is done.
For details see the manual.
BTW. postgresql 9.1 is not being maintained you should consider upgrading.
I've an SQL file which has some PL/SQL scripts and some DML scripts, but I'm not able to run normal DML commands just after a PL/SQL block IN SQL Developer. For e.g.
BEGIN
-- Some Statements
END;
UPDATE TABLE TABLE_NAME SET FLD_NAME = SOMETHING;
Do I need to change anything here so that I can run these commands.
PS: I don't want to put everything in BEGIN ... END block.
try put / after anonymous block:
BEGIN
-- Some Statements
END;
/
UPDATE TABLE TABLE_NAME SET FLD_NAME = SOMETHING;
Is it possible to execute some sql commands within one EXECUTE IMMEDIATE block?
What is wrong with this syntax:
declare
pragma autonomous_transaction;
begin
execute immediate
'begin
COMMENT ON TABLE t1 IS ''description1'';
COMMENT ON TABLE t2 IS ''description2'';
end;';
end;
For one SQL command it works fine:
declare
pragma autonomous_transaction;
begin
execute immediate ' COMMENT ON TABLE t1 IS ''description1'' ';
end;
The begin end within the string to execute immediate is going to be treated as a PL/SQL anonymous block. DDL, such as COMMENT is not allowed in PL/SQL. If it were you wouldn't need to use execute immediate. Oracle essentially works with either a block of PL/SQL statement or a single SQL statement at a time. Though there are APIs to batch SQL statements too.
So, to run COMMENT within a PL/SQL block or procedure, you will need to execute immediate statements.
Without more context I can not intelligently comment on whether that is the right approach, or if just having the two comment statements stand alone would be better.
Well, you could do this:
begin
execute immediate
'begin
execute immediate ''COMMENT ON TABLE t1 IS ''''description1'''' '';
execute immediate ''COMMENT ON TABLE t2 IS ''''description2'''' '';
end;';
end;
But there's not much point.
I cannot convince why I can't add DML operation inside Oracle Function especially inside cursor loop. I feel Oracle don't support DML operation inside cursor loop.
How can I do If I need to insert into table inside cursor loop? Create new store procedure inside it or something else?
Error Message : cannot perform DML operation inside a query
Here is my function,
CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
V_MESSAGE VARCHAR2(30);
CURSOR C_PERSON (V_ID VARCHAR2) IS
SELECT NAME_UPPER
FROM TBL_PERSON
WHERE NAME_UPPER = V_ID;
BEGIN
FOR C_PERSON_CURSOR IN C_PERSON(U_ID)
LOOP
INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
END LOOP;
RETURN V_MESSAGE;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
You can use DML inside a PL/SQL function - no problem. However, the function can only be called from PL/SQL, not from SQL - i.e. it can be called like this:
declare
l_message varchar2(30);
begin
l_message := test_func('123');
end;
... but not like this:
select test_func(empno) from emp;
That leads to the error message you posted.
Many people (including me) don't like functions that have "side effects" like this, but that is a matter of best practice and standards, not a technical issue.
You can perform DML operations inside an Oracle PL/SQL function and, although this is generally not a good practice, call it from SQL. The function has to be marked with a pragma AUTONOMOUS_TRANSACTION and the transaction has to be committed or rolled back before exiting the function (see AUTONOMOUS_TRANSACTION Pragma).
You should be aware that this kind of function called from SQL can dramatically degrade your queries performances. I recommend you use it only for audit purposes.
Here is an example script starting from your function:
CREATE TABLE TBL_PERSON (NAME_UPPER VARCHAR2(30));
CREATE TABLE TMP_PERSON (NAME VARCHAR2(30));
INSERT INTO TBL_PERSON (NAME_UPPER) VALUES ('KING');
CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION; -- Needed to be called from SQL
V_MESSAGE VARCHAR2(2000);
CURSOR C_PERSON (V_ID VARCHAR2) IS
SELECT NAME_UPPER
FROM TBL_PERSON
WHERE NAME_UPPER = V_ID;
BEGIN
FOR C_PERSON_CURSOR IN C_PERSON(U_ID)
LOOP
INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
V_MESSAGE := SQL%ROWCOUNT
|| ' Person record successfully inserted into TMP_PERSON table';
END LOOP;
COMMIT; -- The current autonomous transaction need to be commited
-- before exiting the function.
RETURN V_MESSAGE;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
/
PROMPT Call the TEST_FUNC function and insert a new record into TMP_PERSON table
SELECT TEST_FUNC('KING') FROM DUAL;
PROMPT Content of the TMP_PERSON table
COL NAME FOR A30
SELECT * FROM TMP_PERSON;
When running the previous script we get the following output:
Table created.
Table created.
1 row created.
Function created.
Calling the TEST_FUNC function and insert a new record into TMP_PERSON table
TEST_FUNC('KING')
------------------------------------------------------------
1 Person record successfully inserted into TMP_PERSON table
Content of the TMP_PERSON table
NAME
------------------------------
KING