Yet another bad bind variable in triggers - variables

getting a bad bind variable "old.seatsremain" and "new.seatsremain". Trying to make this trigger automatically decrease the number of seats for the offering if the seats are available for the particular offering. Do I have to declare all the variables with the : in front of it???? my two tables are:
enrollments
{sid number,
offerno number;)
and
offering
{offerno number,
courseno varchar2(10),
instructor varchar2(10),
seatsremain number;}
UPDATED CODE TESTING: OK so I incorporated the coding Fumble gave me and it cleared the errors for ones that originally popped up however I tested out this new code and I still have some remaining error because of the EXCEPTION clause which I have no idea why because the syntax I double checked should be right. any ideas??
create or replace trigger enroll_bef_ins_row
before insert on enrollments
for each row
declare
originstruct offering.instructor%type;
origcourseno offering.courseno%type;
original offering.seatsremain%type;
seatsremain_already_full exception;
begin
Select seatsremain, instructor, courseno into original, originstruct, origcourseno from offering where offerno= :new.offerno;
if original > 0 then
update offering set seatsremain= seatsremain - 1;
dbms_output.put_line ('Seats available in offering'||offerno||'have decreased from'||original|| 'to' ||(seatsremain));
else if original = 0 then
raise seatsremain_already_full;
dbms_output.put_line ('Offering'||offerno||'is already full!');
else
update offering set offerno = :old.offerno;
update offering set courseno = origcourseno;
update offering set instructor = originstruct;
update offering set seatsremain = original;
end if;
exception
when seatsremain_already_full then
raise_application_error (-20001, 'Cannot allow insertion');
commit;
end;
/
THIS ERROR SHOWS UP NOW:
PLS-00103: Encountered the symbol "EXCEPTION" when expecting one
of the following:
begin case declare end exit for goto if loop mod null pragma
raise return select update while with
<<
close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe
PLS-00103: Encountered the symbol "end-of-file" when expecting
one of the following:
end not pragma final instantiable order overriding static

You are using correlation names from a table other than the one your trigger is created for. Try declaring oldSeatsRemain and NewSeatsRemain as variables within your trigger.
Try this (note: this sample has not been executed). It includes the edits I described in my comments.
create or replace trigger enroll_bef_ins_row
before insert on enrollments
for each row
declare
offerrow offering%rowtype;
seatsremain_already_full exception;
pragma autonomus_transaction;
begin
Select seatsremain into offerrow from offering where offerno= :new.offerno;
if offerrow.seatsremain > 0 then
update offering set seatsremain= offerrow.seatsremain - 1;
dmbs_output.put_line ('Seats available in offering ' |offerno| ' have decreased from ' |offerrow.seatsremain| ' to ' |offerrow.seatsremain-1|);
else if original = 0 then
dbms_output.put_line ('Offering ' |offerno| ' is already full!');
raise seatsremain_already_full;
else
insert into offering
values(offering.offerno,offering.courseno,offering.instructor,offering.seatsremain);
end if;
commit;
exception
when seatsremain_already_full
raise_application_error (-20001, 'Cannot allow insertion');
end
/

Related

how to fix this trigger error in PostgreSQL [duplicate]

This question already has answers here:
syntax Error in PostgreSQL when I try to create Trigger
(2 answers)
Closed last year.
I am getting a syntax error for my code which I can't understand why
am I missing something?
also, I read this I did not get my answer
syntax Error in PostgreSQL when I try to create Trigger
CREATE TRIGGER MyExampleName AFTER INSERT ON baskets
FOR EACH ROW BEGIN
UPDATE customers
SET customers.credit=customers.credit - NEW.amount
WHERE customers.id = NEW.customer_id;
END;
and tried it like this as well:
CREATE TRIGGER MyExampleName AFTER INSERT ON baskets
FOR EACH ROW AS $$ BEGIN
UPDATE customers
SET customers.credit=customers.credit - NEW.amount
WHERE customers.id = NEW.customer_id;
END;
$$ LANGUAGE plpgsql;
Error:
ERROR: syntax error at or near "BEGIN"
LINE 2: FOR EACH ROW BEGIN
^
SQL state: 42601
Character: 67
I'd say the first comment on your question pretty much covers it all. You cannot put the trigger code in the trigger body, you must first create a separate function and include the function call inside the trigger body.
This example comes directly from the Postgres docs:
-- 1. Create the function that does what you need
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when they must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
-- 2. Create the trigger with the 'EXECUTE FUNCTION function_name' part
-- replacing the actual function name from step 1.
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE FUNCTION emp_stamp();

Procedure to check if authorized

So I have this function:
create or replace function get_authorization(
p_pnr in bankcustomer.pnr%type,
p_knr in account.cnr%type)
return number
as
v_authorization bankcustomer.pnr%type;
begin
select count(*)
into v_authorization
from bankcustomer,account
where pnr = p_pnr
and cnr = p_cnr;
return v_authorization;
exception
when no_data_found then
return -1;
end;
This returns 1 or 0 if allowed. I need to do a procedure which adds a row in a table called withdraw and that procedure needs to call get_authorization to check if the customer is allowed. This is what i have so far:
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_belopp in withdraw.belopp%type)
as
declare
authorization exception;
begin
insert into uttag(pnr,amount)
values((select pnr from account),p_amount); /*pnr is foreign key in withdraw*/
if user not in (select get_amount(p_pnr) from dual) then
raise authorization;
end if;
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
commit;
end;
I get alot of error messages and specifically on declare. I really cant wrap my head around this :(
You have few problems with your code -
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_belopp in withdraw.belopp%type)
as
-- declare -- Declare keyword is not used in procedure
authorization exception;
begin
insert into uttag(pnr,amount)
values((select pnr from account), -- This might return multiple rows, So you have to add a where clause in this query.
p_amount); /*pnr is foreign key in withdraw*/
if user <> get_authorization(p_pnr) then -- You are checking current user with amount which should be corrected.
raise authorization;
end if;
commit; -- Commit should be last sattement of procedure
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
Rollback; -- In exception you should use Rollabck instead of Commit;
end;
So ive tried what you said and think i know what you mean:
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_amount in withdraw.amount%type)
as
-- declare -- Declare keyword is not used in procedure
authorization exception;
begin
insert into withdraw(pnr,amount)
values((select pnr from account where pnr = p_pnr), -- This might return multiple rows, So you have to add a where clause in this query.
p_amount); /*pnr is foreign key in withdraw*/
if user not in (select get_authorization(p_pnr)) then -- You are checking current user with amount which should be corrected.
raise authorization;
end if;
commit; -- Commit should be last sattement of procedure
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
Rollback; -- In exception you should use Rollabck instead of Commit;
end;
Now i get error: Line/Col: 11/51 PLS-00103: Encountered the symbol ")" when expecting one of the following:. ( , * % & - + /

PL/SQL trigger: can't create apex user

I am working on a database and one of the final tasks left is to create user accounts. For some reason I can't seem to get it to work. In fact, none of the commented code works when uncommented. Our primary concern is being able to automate the creation of user accounts rather than creating them manually. I am hoping someone can shed some light into the errors of my ways so my code will compile.
create or replace TRIGGER trg_Students
BEFORE INSERT OR UPDATE OF SRN, Surname, Forename, Username, DOB, Date_Cv_Submitted, Date_cv_approved, same_address, home_phone_no, home_postcode ON Students
FOR EACH ROW
BEGIN
IF INSERTING THEN
:NEW.SRN := seq_SRN.nextval;
CREATE USER :new.USERNAME
IDENTIFIED BY PASSWORD
PROFILE app_user
PASSWORD EXPIRE;
--IF (ACTIVE_ACCOUNT = 'Y' AND CV_APPROVED = NULL) THEN
-- RAISE_APPLICATION_ERROR(-20000, 'Cannot create an account that is active before the cv is approved!');
--END IF;
END IF;
--IF UPDATING THEN
--IF (DATE_CV_APPROVED != NULL) THEN
--:new.Active_Account := 'Y';
--END IF;
--END IF;
:NEW.forename := INITCAP(:NEW.forename);
:NEW.surname := INITCAP(:NEW.surname);
:NEW.home_postcode := UPPER(:NEW.home_postcode);
:NEW.home_phone_no := REGEXP_REPLACE(:NEW.home_phone_no, '[^[:digit:]]', '');
:NEW.home_phone_no := REGEXP_REPLACE(:NEW.home_phone_no,
'([[:digit:]]{5})([[:digit:]]{6})', '(\1) \2');
IF :NEW.same_address = 'Y' THEN
:NEW.term_no := :NEW.home_no;
:NEW.term_postcode := :NEW.home_postcode;
:NEW.term_phone_no := :NEW.home_phone_no;
ELSE
:NEW.term_postcode := UPPER(:NEW.term_postcode);
:NEW.term_phone_no := REGEXP_REPLACE(:NEW.term_phone_no, '[^[:digit:]]', '');
:NEW.term_phone_no := REGEXP_REPLACE(:NEW.term_phone_no,
'([[:digit:]]{5})([[:digit:]]{6})', '(\1) \2');
END IF;
IF (:NEW.DOB + NUMTOYMINTERVAL(18,'YEAR') > SYSDATE) THEN
RAISE_APPLICATION_ERROR(-20000, 'Client must be at least 18 years of age!');
END IF;
IF (:NEW.Date_cv_approved < :NEW.date_cv_submitted) THEN
RAISE_APPLICATION_ERROR(-20000, 'Cannot approve a cv before it is submitted!');
END IF;
END;
the error is
Compilation failed, line 6 (13:19:44) The line numbers associated with
compilation errors are relative to the first BEGIN statement. This
only affects the compilation of database triggers. PLS-00103:
Encountered the symbol "CREATE" when expecting one of the following: (
begin case declare else elsif end 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.
I have changed my method to:
APEX_UTIL.CREATE_USER(
p_user_name => :new.USERNAME,
P_web_password => 'Password123');
and it now produces this error:
An API call has been prohibited. Contact your administrator. Details
about this incident are available via debug id "46046".
Contact your application administrator.
Seems funny that i am answering my own question but i solved the issue. The code i used to create apex users is the following.
APEX_UTIL.CREATE_USER(
p_user_name => :new.USERNAME,
P_web_password => 'Password123',
p_change_password_on_first_use => 'Y');
The error above was solved by changing the security settings from within the application builder to allow the api to work this is found by the following.
Application Builder -> (Your Application) -> Shared Components -> Security Attributes and finally tick the boxes next to runtime API Usage at the bottom of the page, i ticked all 3 as i needed to.
You cannot execute create statements directly from PLSQL.
change this to:
IF INSERTING THEN
:NEW.SRN := seq_SRN.nextval;
execute immediate 'CREATE USER '||:new.USERNAME ||'
IDENTIFIED BY PASSWORD
PROFILE app_user
PASSWORD EXPIRE';
You have to set your trigger for an autonomous transaction in order to commit within a trigger.
CREATE TRIGGER your_trigger
BEFORE INSERT ON your_table FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
...
HTH

PL SQL trigger using raise_application_error thows error.

I have a few things of code I need help debugging but I feel that if I can get one of them running i'll be able to get the rest(oh how i hope).
create or replace
trigger minimumwage
before insert or update on Employee
for each row
begin
if :new.Wage < 7.25
then raise_application_error('-20000,Pay is below Texas minimum wage!');
end if;
end;
/
I'm trying to do this on a table ran on my school's server through sqlplus if that helps.
When you're getting an error, it's always helpful to specify what error. There is a syntax error in the raise_application_error call in your trigger. That procedure takes two arguments, a number and a string. You are passing in a single argument that is one long string.
create or replace trigger minimumwage
before insert or update on Employee
for each row
begin
if :new.Wage < 7.25
then
raise_application_error(-20000,'Pay is below Texas minimum wage!');
end if;
end;
should be valid assuming there is a WAGE column in your EMPLOYEE table.
create or replace trigger deny_dec_pu before update of PU on ARTICLE
for each row
declare
erreur_pu exception;
begin
*insert into erreur values ('Operation de MAJ',sysdate);*
-- this intruction is never executec why ?
if (:new.pu < :old.pu) then
raise erreur_pu ;
end if;
exception
when erreur_pu then
Raise_application_error(-20100, 'rrrrrrrr', FALSE);
end;
/

Why is the exception NO_DATA_FOUND not being triggered?

So the problem i am having is that if i execute the following procedure and the cursor doesnt find the parameter being passed, it continues to execute the block (insert statement) but instead of throwing the NO_DATA_FOUND exception error it throws a parent/foreign key error.
CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE);
END ASSIGNMENT3;
/
CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
CURSOR ADCOST_CUR IS
SELECT ACTUALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
V_TOTALCOST NUMBER;
BEGIN
V_TOTALCOST := 0;
FOR INVOICE_REC IN ADCOST_CUR
LOOP
V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST;
END LOOP;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.');
COMMIT;
END END_CAMPAIGN;
END ASSIGNMENT3;
/
SET SERVEROUTPUT ON
EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV');
While the parent foreign key error is correct, i dont want the block to execeute if the cursor doesnt return a row. Why is this happening?
Also, in terms of placing the COMMIT, where exactly do i tell it to COMMIT? Before the exception or after?
This is for a uni assignment.
When you loop over a cursor like that, if the cursor finds no matching rows, the loop simply doesn't execute at all. A NO_DATA_FOUND exception would only be raised if you had a SELECT ... INTO ... statement inside the BEGIN/END block that did not return any rows.
Where you have the COMMIT placed now, it is part of the EXCEPTION block -- but your indentation implies that you want it to execute whether the exception occurred or not. In this case, I would just put the COMMIT immediately after the INSERT, since it only matters if the INSERT is successful.
"So is there no way to have the NODATAFOUND exception trigger when
using a cursor, if the CTITLE parameter isnt found in the table"
What you could do is test the value of V_TOTAL_COST. If it is zero raise an exception, like this:
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
CURSOR ADCOST_CUR IS
SELECT ACTUALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
V_TOTALCOST NUMBER;
BEGIN
V_TOTALCOST := 0;
FOR INVOICE_REC IN ADCOST_CUR
LOOP
V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST;
END LOOP;
if v_total_cost = 0 then
raise no_data_found;
end if;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
COMMIT;
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.');
END END_CAMPAIGN;
This assumes you have a business rule that ACTUAL_COST cannot be zero.
Alternatively, there is the clunkier workaround of incrementing a counter in the loop and testing whether it is zero after the loop.
As for where to place the commit I would say the answer is not inside the procedure. The client (sqlplus in this case) should determine if the transaction will commit or rollback as the call to end the campaign may be just a part of a wider process. Also assuming that a campaign can exist without any advertisements then I would have an explicit check that the campaign title is valid perhaps against the table of CAMPAIGN? as suggested below:
CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE);
END ASSIGNMENT3;
/
CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
V_VALID_CAMPAIGN INTEGER;
V_TOTALCOST NUMBER;
BEGIN
-- Check this campaign title is valid
/* Will get you NO_DATA_FOUND here if CTITLE is invalid so wrap in
another BEGIN END block to throw own custom error that the client
of this procedure can handle (if it wants) */
BEGIN
SELECT 1
INTO V_VALID_CAMPAIGN
FROM CAMPAIGN
WHERE CAMPAIGNTITLE = CTITLE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20000,'The campaign title you entered returned no record(s), please enter a valid campaign title.');
END;
-- Now tot up the cost of ads in this campaign and raise the invoice
SELECT SUM(ACTUALCOST)
INTO V_TOTALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
END END_CAMPAIGN;
END ASSIGNMENT3;
/
EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV');
COMMIT;