why not postgresql transaction block raise an error? - sql

In Postgesql db, I created a table like below zzz_sil6. column b is date datatype.
create table zzz_sil6
(
b date
)
if i execute sql as below with only insert statement, it raise an error due to data type problem. This is the case we expected.
insert into zzz_sil6
select 1;
However, when i executed the insert statement in begin end transaction block, it didnt raise an error due to data type problem.
begin
insert into zzz_sil6
select 1;
commit;
end;
Am i missing about transaction block? I m new to postgresql.

Related

After Insert Trigger not working as Expected

I have a scenario
to Insert a record in second table after only when inserting a record in first table on certain conditions.
I have my trigger logic like
CREATE OR REPLACE TRIGGER trgname_TR AFTER INSERT OR UPDATE ON <table1>
DECLARE
v_value NUMBER(12,0);
BEGIN
select br.value into v_value
from table3 BR
where br.value =:NEW.value
and br.category IN (839,23,30,843,1414)
and br.efctv_to_date is null;
IF INSERTING OR UPDATING THEN
INSERT INTO table1(col1,col2,created_date,updated_date)
VALUES
(1,v_value,SYSDATE,:NEW.LAST_UPDATE_DATE);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001,'NO SUCH SOURCES EXISTS');
END;
The problem is when I try inserting record in first table on condition IN (839,23,30,843,1414)
insert working fine and record gets inserted in second table
on when trying insert other than this condition IN (839,23,30,843,1414)
ORA-20001: NO SUCH SOURCES EXISTS
ORA-06512: at "table", line 23
ORA-04088: error during execution of trigger 'table'
There is a Select into command that, when IN list changed, does not fetch any row. Consequence is NO_DATA_FOUND exception.
Check your data and fix the code:
select br.value into v_value
from table3 BR
where br.value =:NEW.value
and br.category IN (839,23,30,843,1414)
and br.efctv_to_date is null;
This command says that you need a SINGLE row from table3 where table3.value is equal to the new value of the trigger table AND table3.efctv_to_date is Null AND table3.category is in the list (this one or changed).
Oracle will return a message and say that there is no such row.
Be careful with this as there could be more than 1 row (because of IN() function) resulting with TOO_MANY_ROWS exception.

How to use variables in PostgreSQL transaction

How in Postgresql inside a transaction to get values ​​into a variable, and if SELECT did not return anything, throw an error, and if SELECT returned data, then use them in the transaction?
Like this:
BEGIN;
#activeRounds = SELECT * FROM "rounds" WHERE status = 'active';
if(!#activeRounds) {
RAISE ERROR "Has no Active rounds"
};
INSERT INTO "bet"
(user_id, round_id)
VALUES
(100, #activeRound[0].id)
COMMIT;
how to do something similar in one request within a transaction?
You can adapt following code to your table structure:
begin;
do
$$
declare
v int;
begin
select c1 into v from t1 where k1=1;
if not found
then
raise exception 'no row found';
else
insert into t2(c2) values(v);
end if;
end;
$$;
commit;
Note the difference bewteen begin; to start a transaction and begin to start a pl/pgSQL block.
Write a DO statement in PL/pgSQL.
Like every SQL statement, DO runs in a single transaction. PL/pgSQL is a procedural language that has variables and conditional processing.

Issue with trigger for archiving

Trying to make a trigger that puts data into an archive table when a column called COMPLETION_STATUS goes from incomplete to complete, the dbms is a placeholder for the insert but I'm getting the following errors in the if statement
Error(6,1): PLS-00103: Encountered the symbol enter code here"SELECT" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior The symbol "begin" was substituted for "SELECT" to continue.
Error(9,1): PLS-00103: Encountered the symbol "IF" when expecting one of the following: * & - + ; / at for mod remainder rem and or group having intersect minus order start union where connect || multiset The symbol ";" was substituted for "IF" to continue.
Error(13,4): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Code:
create or replace TRIGGER ARCHIVING_TRIG
BEFORE UPDATE OF COMPLETION_STATUS ON PROJECT_DATA
BEGIN
DECLARE COMPLETION_STATUS1 VARCHAR2(9);
SELECT COMPLETION_STATUS into COMPLETION_STATUS1
FROM PROJECT_DATA WHERE COMPLETION_STATUS = 'complete'
IF COMPLETION_STATUS1 = 'complete'
THEN
DBMS.output('123');
END IF;
END;
The DECLARE block should be before the BEGIN block.
The SELECT ... statement needs to be terminated with a semicolon (;).
It's dbms_output.put_line() not dbms.output();
You're trying to assign the result of a query that potentially can return more than one row to a scalar variable.
The rows selected from project_data have no relation to the one(s) that triggered the trigger.
I suggest you use something like:
CREATE TRIGGER archiving_trig
AFTER UPDATE
ON project_data
FOR EACH ROW
WHEN (old.completion_status <> 'complete'
AND new.completion_status = 'complete')
BEGIN
dbms_output.put_line('Trigger fired for ID ' || :new.id);
END;
db<>fiddle
I think maybe AFTER is the better time, because you want to archive the row after the status was successfully changed.
Because of the WHEN the trigger will only fire if completion_status has been changed from something other than 'complete' to 'complete'. But you maybe also need to have a method of removing entries from the archive when the status changes from 'complete' to something else. That isn't covered here.
Declaring it as FOR EACH ROW let's you access the values of the updated row via :new. That way you don't need a query to select that nor a variable to select into.
I guess you need this:
create table PROJECT_DATA_NEW as select * from PROJECT_DATA where 1=2;
CREATE OR REPLACE TRIGGER ARCHIVING_TRIG
AFTER UPDATE
ON PROJECT_DATA
FOR EACH ROW
DECLARE
status number;
BEGIN
status:=0;
select 1 into status from PROJECT_DATA where
:new.COMPLETION_STATUS='complete' and
:old.COMPLETION_STATUS='incomplete'
if (status=1) then
insert into PROJECT_DATA_NEW values(:old.column1,
:old.column2,
:old.column3,
:old.column4,
:old.column5,....etc);
end if;
END;
/

Why inside of a trigger the code before raise_application_error isn't executed?

IF I create this trigger, then the error is raised when drop or truncate is used on tables, but there is nothing inserted into logTable, but if I delete RAISE_APPLICATION_ERROR... then the values are inserted into logTable, but the drop/truncate are executed too. Why? How can I avoid drop/truncate on Schema (If I use instead of trigger, it is fired only if owner of the schema is dropping/truncating something).
CREATE OR REPLACE TRIGGER trigger_name
BEFORE DROP OR TRUNCATE ON DATABASE
DECLARE
username varchar2(100);
BEGIN
IF ora_dict_obj_owner = 'MySchema' THEN
select user INTO username from dual;
INSERT INTO logTable VALUES(username , SYSDATE);
RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
END IF;
END;
According to the documentation:
Statement-Level Atomicity
Oracle Database supports statement-level atomicity, which means that a SQL statement is an atomic unit of work
and either completely succeeds or completely fails.
A successful statement is different from a committed transaction. A
single SQL statement executes successfully if the database parses and
runs it without error as an atomic unit, as when all rows are changed
in a multirow update.
If a SQL statement causes an error during execution, then it is not
successful and so all effects of the statement are rolled back. This
operation is a statement-level rollback.
The procedure is a PL/SQL statement, it is atomic, if you raise an error within the procedure, then the whole procedure fails and Oracle performs a rollback of all the changes done by this procedure.
But you can create a procedure with AUTONOMOUS_TRANSACTION Pragma in order to bypass this behaviour, in this way:
CREATE TABLE logtable(
username varchar2(200),
log_date date
);
CREATE OR REPLACE PROCEDURE log_message( username varchar2 ) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO logtable( username, log_date ) VALUES ( username, sysdate );
COMMIT;
END;
/
CREATE OR REPLACE TRIGGER trigger_name
BEFORE DROP OR TRUNCATE ON DATABASE
DECLARE
username varchar2(100);
BEGIN
IF ora_dict_obj_owner = 'TEST' THEN
log_message( user );
RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
END IF;
END;
And now:
drop table table1;
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: ERROR, YOU CAN NOT DELETE THIS!!
ORA-06512: at line 6
00604. 00000 - "error occurred at recursive SQL level %s"
*Cause: An error occurred while processing a recursive SQL statement
(a statement applying to internal dictionary tables).
*Action: If the situation described in the next error on the stack
can be corrected, do so; otherwise contact Oracle Support.
select * from logtable;
USERNAME LOG_DATE
-------- -------------------
TEST 2018-04-27 00:16:34

How to insert a record into multiple tables using a trigger?

I have two Tables.
I want to insert the same record on both tables at the same time.
I.e., while I insert a record for the first table, this same record also is inserted in the second table using a trigger.
Do you have any experience/advice in this process ?
if you're using stored procedures you can easily manage this
CREATE PROCEDURE sp_Insert
#Value varchar(10)
AS
insert into table1 (...,...) values (#value,...)
insert into table2 (...,...) values (#value,...)
I would suggest using Erik's method over a trigger. Triggers tend to cause performance issues, and a lot of times, you forget that the trigger exists, and get unexpected behavior. If you do want to use a trigger however, it will work. here is an example:
CREATE TRIGGER trgTest ON Test
FOR INSERT
AS
INSERT Test2
(Id, value)
SELECT Id, Value
FROM Inserted
Can Use Cursor Concept!
CREATE OR REPLACE TRIGGER bi_order
BEFORE INSERT
ON ord
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
WHEN (NEW.payment_type = 'CREDIT')
DECLARE
CURSOR cur_check_customer IS
SELECT 'x'
FROM customer
WHERE customer_id = :NEW.customer_id
AND credit_rating = 'POOR';
lv_temp_txt VARCHAR2(1);
lv_poor_credit_excep EXCEPTION;
BEGIN
OPEN cur_check_customer;
FETCH cur_check_customer INTO lv_temp_txt;
IF (cur_check_customer%FOUND) THEN
CLOSE cur_check_customer;
RAISE lv_poor_credit_excep;
ELSE
CLOSE cur_check_customer;
END IF;
EXCEPTION
WHEN lv_poor_credit_excep THEN
RAISE_APPLICATION_ERROR(-20111, 'Cannot process CREDIT ' ||
'order for a customer with a POOR credit rating.');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20122, 'Unhandled error occurred in' ||
' BI_ORDER trigger for order#:' || TO_CHAR(:NEW.ORDER_ID));
END bi_order;