Why am I getting a Warning: Trigger created with compilation errors? - triggers

This is my first time messing with Triggers in SQL*Plus, and I want to not have more than the max capacity of a movie theater's showing room.
The showtimes gives the max_occupancy. This is what I have:
CREATE OR REPLACE TRIGGER over_capacity
BEFORE INSERT OR UPDATE ON tickets
FOR EACH ROW
WHEN (NEW.SHOWID=OLD.SHOWID)
DECLARE
nope EXCEPTION;
ti SYS_REFCURSOR;
sh SYS_REFCURSOR;
total INT;
max_oc INT;
BEGIN
OPEN sh FOR
SELECT s.max_occupancy FROM showtimes s WHERE NEW.showid=s.showid;
FETCH sh INTO max_oc;
OPEN ti FOR
SELECT COUNT(t.userid) FROM tickets t WHERE t.showid=NEW.showid;
FETCH ti INTO total;
IF total=max_oc THEN
RAISE nope;
END IF;
EXCEPTION
WHEN nope THEN
raise_application_error (-20500, 'AT CAPACITY');
END;
/

You can use just select statements like this:
CREATE OR REPLACE TRIGGER over_capacity
BEFORE INSERT OR UPDATE ON tickets FOR EACH ROW
WHEN (NEW.SHOWID=OLD.SHOWID)
DECLARE
nope EXCEPTION;
total INT;
max_oc INT;
BEGIN
SELECT max_occupancy INTO total FROM showtimes WHERE showid = :new.showid;
SELECT COUNT(userid) INTO max_oc FROM tickets WHERE showid=:new.showid;
IF total=max_oc THEN
RAISE nope;
END IF;
EXCEPTION
WHEN nope THEN
raise_application_error (-20500, 'AT CAPACITY');
END;
/

Related

What is the equivalent of PL/SQL %ISOPEN in PL/pgSQL?

I'm migrating an Oracle PLSQL SP to be compatible with Postgres plpgsql (version PostgreSQL 13.6 on x86_64-pc-linux-gnu, compiled by x86_64-pc-linux-gnu-gcc (GCC) 7.4.0, 64-bit).
The exception block of the PLSQL SP has the below code:
exception
when others then
if CURR1%isopen then
close SPV_RECON_INFO;
end if;
open CURR1 for execute select sysdate from dual;
END;
How can %isopen be implemented in Postgres?
That is simple. You have to assign a name to the cursor variable, then you can search for that cursor in pg_cursors. If there is a row with that name, the cursor is open.
Here is a self-contained example:
DO
$$DECLARE
c refcursor;
BEGIN
c := 'mycursor';
/* cursor is not open, EXIST returns FALSE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
OPEN c FOR SELECT * FROM pg_class;
/* cursor is open, EXIST returns TRUE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
END;$$;
NOTICE: f
NOTICE: t
If you do not assign a name, PostgreSQL will generate a name (but you don't know what the name is).
PostgreSQL cursors do not support %ISOPEN or %NOTFOUND. To address this problem %ISOPEN can be replaced by a boolean variable declared internally in the procedure and is updated manually when the cursor is opened or closed.
http://wiki.openbravo.com/wiki/PL-SQL_code_rules_to_write_Oracle_and_Postgresql_code
I often found it convenient in cases like this to create a function that emulates Oracle. In his case something like:
create or replace function cursor_isopen(cur text)
returns boolean
language sql
as $$
select exists (select null
from pg_cursors
where name = cur
) ;
$$;
Then your code becomes something like:
exception
when others then
if cursor_isopen(cur_name::text) then
close SPV_RECON_INFO;
end if;
Of course you need to have preset the cursor name as Laurenz Albe has pointed out. Sample test case.
do $$
declare
cur1 cursor for select table_name from information_schema.tables;
cur2 cursor for select table_name from information_schema.tables;
table_name text;
begin
cur1 := 'closed-cursor';
cur2 := 'open-cursor';
open cur2;
if cursor_isopen(cur1::text)
then
fetch cur1 into table_name;
raise notice 'First table name: %', table_name;
close cur1;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
if cursor_isopen(cur2::text)
then
fetch cur2 into table_name;
raise notice 'First table name: %', table_name;
close cur2;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
end;
$$;
results:
cursor_isopen('closed-cursor') returned f
cursor_isopen('open-cursor') returned t. First table name: task_states

How do i use cursor to print new things i added to table in postgresql?

I want to write SQL trigger in postgresql.
create trigger add_car on samochod
after insert
as
begin
declare k_inserted cursor
for select car_model, mileage from inserted
declare #car_model(30), #mileage varchar(30)
open k_inserted
fetch next from k_inserted into #car_model(30), #mileage varchar(30)
while ##FETCH_STATUS = 0
begin
print 'Added car: '
print 'Model: ' + #car_model
print 'Mileage: ' + #mileage
fetch next from k_inserted into #car_model(30), #mileage varchar(30)
end
close k_inserted
deallocate k_inserted
end
INSERT INTO car(car_model, mileage)
VALUES ('Audi', 15011646);
That trigger actually works for me. Then i tried in postgresql.
create function add_car()
RETURNS TRIGGER
as
$$
declare
car_model varchar(30);
mileage varchar(30);
k_inserted cursor for select new.car_model, new.mileage from car;
begin
open k_inserted;
fetch next from k_inserted into car_model, mileage;
while (FOUND)
LOOP
RAISE NOTICE 'Added car: ';
RAISE NOTICE 'Marka: %', car_model;
RAISE NOTICE 'Mileage: %', mileage;
fetch next from k_inserted into car_model, mileage;
end LOOP;
close k_inserted;
return new;
end;
$$ LANGUAGE plpgsql;
CREATE TRIGGER add_car
after INSERT on car
for each row EXECUTE PROCEDURE add_car();
It works but it prints me 6 times the same thing (i have 6 cars in my table)
How can I fix it so that it prints as many times as the cars I added
You declared your trigger as for each row so there is no need for a loop or a select "from inserted" (something that SQL Server doesn't have). The trigger will be called once for each row you insert, so just print the values from the new record
create function add_car()
RETURNS TRIGGER
as
$$
begin
RAISE NOTICE 'Added car: ';
RAISE NOTICE 'Marka: %', new.car_model;
RAISE NOTICE 'Mileage: %', new.mileage;
return new;
end;
$$ LANGUAGE plpgsql;
If you do want a statement level trigger to mimic SQL Server's behaviour, you can do the following:
A trigger function using the inserted transition table:
create function add_car()
RETURNS TRIGGER
as
$$
declare
l_row record;
begin
for l_row in select * from inserted
LOOP
RAISE NOTICE 'Added car: ';
RAISE NOTICE 'Marka: %', l_row.car_model;
RAISE NOTICE 'Mileage: %', l_row.mileage;
end LOOP;
return new;
end;
$$ LANGUAGE plpgsql;
And the corresponding definition of a statement level trigger:
CREATE TRIGGER add_car
after INSERT on car
REFERENCING NEW TABLE AS inserted
for each statement EXECUTE PROCEDURE add_car();

stored procedure - pass multiple values for IN clause

I'm writing a stored procedure for Postgres, just do select * from table where id in (value1, value2, ...).
These values will be getting from the variable.
My code:
CREATE OR REPLACE PROCEDURE record_example(v_name varchar(100), v_id int)
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN select id, updated from mytable where names in (v_name) and id=v_id
LOOP
RAISE INFO 'id = % and updated = %', rec.id, rec.updated;
END LOOP;
END;
$$;
This actually works if I use single value for v_name.
Ex:
call record_example('myname',101);
But if I do multiple values, its not working.
HELP 1 :
call record_example('myname, your_name',101);
It just returned CALL, that's it. Nothing happened.
HELP 2:
Sometimes the v_id variable is optional, so that time the FOR loop should inclue the id=v_id
Ex:
FOR rec IN select id, updated from mytable where names in (v_name)
LOOP
RAISE INFO 'id = % and updated = %', rec.id, rec.updated;
END LOOP;

function not returning any value and not raising exception

The below function neither returning the value nor raising the exception.
create or replace function get_custid(p_customerNum varchar2)
RETURNS text AS $$
DECLARE
cust_id customer.customer_num%TYPE;
begin
raise notice '%', message_text;
select customer_num into cust_id
from customer
where customer_num = p_customerNum;
return cust_id;
exception
when OTHERS then
raise notice '%', message_text;
raise;
end $$ language plpgsql;
select get_custid('Ab12345') from dual;
-- the customer number is existed but not returning any rows.
select get_custid('DDDDDDD') from dual;
-- the customer number is not existed but not going to exception block
I think that is you really use postgresql this code is more likely what you need (or at list it's running...)
create table customer (cust_id int, customer_num varchar);
insert into customer values (1, 'Ab12345');
drop function get_custid(varchar);
create or replace function get_custid(p_customerNum varchar)
RETURNS int AS $$
DECLARE
out_cust_id int;
begin
--raise notice '%', message_text;
select cust_id into out_cust_id
from customer
where customer_num = p_customerNum;
if out_cust_id is null
then raise exception 'your exception';
end if;
return out_cust_id;
end $$ language plpgsql;
select get_custid('Ab12345');
In PL/pgSQL, SELECT INTO only throws an exception on the wrong number of rows if STRICT is specified.
create or replace function get_custid(p_customerNum varchar)
RETURNS text AS $$
DECLARE
cust_id customer.customer_num%TYPE;
begin
raise notice '%', 'message_text';
select customer_num into strict cust_id
from customer
where customer_num = p_customerNum;
return cust_id;
exception
when OTHERS then
raise notice '%', 'message_text';
raise;
end $$ language plpgsql;

commit can not be excueted successfully

I have the following code: The code is to do a map from demoimage table to fitsheader table. I can run the code without complaining if I comment out the commit; line. But demoimage table doesn't change which make me confused these hours. I search a lot on the problem on google but got no result.
CREATE OR REPLACE FUNCTION public.linkjpeg(
)
RETURNS TABLE(matchtext text)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
DECLARE
v_jpeghref demoImage.href%type;
v_fitsHREF fitsheader."HREF"%type;
v_uid varchar(500);
v_count bigint;
c_jpeg cursor for SELECT href from demoImage;
c_fitsHREF cursor for select t."HREF" from fitsheader t;
array_matches text[];
array_fitsHREF text[];
array_imgHref text[];
v_arrayDim bigint;
fileBone varchar(500);
fileBoneFits char(500);
c_checkUID cursor for select v_uid=any(array_matches);
c_checkfitsHREF cursor for select v_fitsHREF = any(array_fitsHREF);
c_matchrow cursor for select t.id_fitsheader from fitsheader t where t."HREF" like '%'||fileBoneFits||'%';
v_idfits bigint;
v_matchedFitsHREF fitsheader."HREF"%type;
i bigint;
_sql text;
BEGIN
i := 0;
open c_jpeg;
loop
fetch c_jpeg into v_jpegHref;
v_uid := substring(v_jpegHref from '/member.uid.*');
if(v_uid is not null) then
array_imgHref := string_to_array(v_uid,'.');
v_arrayDim := cardinality(array_imgHref);
fileBone := array_to_string(array_imgHref[1:v_arrayDim-3],'.');
-- replace xxxxx-x-xxxxxS. with null
fileBoneFits := regexp_replace(filebone,'\d\d\d\d.\d.\d\d\d\d\d.S-','');
open c_matchrow;
begin
fetch c_matchrow into v_idfits;
exception
when others then
raise notice '%','not found v_idfits';
end;
raise notice '%','v_idfits'||v_idfits;
begin
execute 'update demoImage set id_fitsheader='|| v_idfits ||' where href ='||quote_literal(v_jpegHref);
**commit;**
exception
when others then
raise notice '%','commit failed'||fileBoneFits;
end;
i := i+1;
raise notice '%', i;
close c_matchrow;
end if;
end loop;
close c_jpeg;
END
The problem is that I can never make the commit executed successfully. I can run the sql above the commit successfully in psql window separately. Can anyone help me figure out where I am wrong? Thanks in advance!
You cannot have transaction statements like COMMIT and ROLLBACK in a PL/pgSQL function; that feature will become available in v11.