Trigger LMD - PL/SQL - oracle-sqldeveloper

I would like to execute the trigger below in order for the error to be raised("You are not allowed to change the date"), that should be triggered when someone tries to change the date in the table, but I can't manage to find out how I can call the trigger. I did try SET DataValiditate = TO_DATE(DataValiditate, 'yyyy/mm/dd') + '2023/10/14';, but it doesn't work. Please could you help me? Thank you!
CREATE TABLE ProduseAchizitionate (
ID_Achizitii NUMBER(9) NOT NULL,
CodProdus NUMBER(9) NOT NULL,
Cantitate NUMBER(9) NOT NULL,
DataValiditate DATE NOT NULL,
CONSTRAINT pk_ProduseAchizitionate PRIMARY KEY(ID_Achizitii)
);
ALTER TABLE ProduseAchizitionate
ADD CONSTRAINT "include" FOREIGN KEY (ID_Achizitii)
REFERENCES Achizitii(ID_Achizitii) ON DELETE CASCADE;
ALTER TABLE ProduseAchizitionate
ADD CONSTRAINT "cuprinde" FOREIGN KEY (CodProdus)
REFERENCES Medicament(CodProdus) ON DELETE CASCADE;
INSERT ALL
INTO ProduseAchizitionate(ID_Achizitii, CodProdus, Cantitate,
DataValiditate)
VALUES(01,04,40,TO_DATE('2023/10/10','yyyy/mm/dd'))
SELECT * FROM dual;
CREATE OR REPLACE TRIGGER produseAchizitionate_update
BEFORE UPDATE OF DataValiditate ON ProduseAchizitionate
FOR EACH ROW
BEGIN
IF (:NEW.DataValiditate <> :OLD.DataValiditate) THEN
RAISE_APPLICATION_ERROR(-20201,'Data expirarii produsului nu poate fi modificata!');
END IF;
END;
/
UPDATE ProduseAchizitionate
SET DataValiditate = TO_DATE(DataValiditate, 'yyyy/mm/dd');

As shown, your UPDATE isn't changing the value of the date at all, so the trigger won't evaluate the error condition. Try replacing your to_date with SYSDATE, or TO_DATE('2023/10/14', 'yyyy/mm/dd'):
CREATE TABLE ProduseAchizitionate (
ID_Achizitii NUMBER(9) NOT NULL,
CodProdus NUMBER(9) NOT NULL,
Cantitate NUMBER(9) NOT NULL,
DataValiditate DATE NOT NULL,
CONSTRAINT pk_ProduseAchizitionate PRIMARY KEY(ID_Achizitii)
);
INSERT ALL
INTO ProduseAchizitionate(ID_Achizitii, CodProdus, Cantitate,
DataValiditate)
VALUES(01,04,40,TO_DATE('2023/10/10','yyyy/mm/dd'))
SELECT * FROM dual;
CREATE OR REPLACE TRIGGER produseAchizitionate_update
BEFORE UPDATE OF DataValiditate ON ProduseAchizitionate
FOR EACH ROW
BEGIN
IF (:NEW.DataValiditate <> :OLD.DataValiditate) THEN
RAISE_APPLICATION_ERROR(-20201,'Data expirarii produsului nu poate fi modificata!');
END IF;
END;
/
UPDATE ProduseAchizitionate SET DataValiditate = SYSDATE;
Output:
Table PRODUSEACHIZITIONATE created.
1 row inserted.
Trigger PRODUSEACHIZITIONATE_UPDATE compiled
Error starting at line : 37 in command -
UPDATE ProduseAchizitionate SET DataValiditate = SYSDATE
Error report -
ORA-20201: Data expirarii produsului nu poate fi modificata!
ORA-06512: at "PETE.PRODUSEACHIZITIONATE_UPDATE", line 3
ORA-04088: error during execution of trigger 'PETE.PRODUSEACHIZITIONATE_UPDATE'

Related

Stack depth Limit INSERT trigger

I have this table on PostgreSQL and I'd like to make a trigger that every time I edit one row of the table publications the last_edit_date is updated to now():
CREATE FUNCTION trigger_update_question_timestamp()
RETURNS "trigger" AS $func$
BEGIN
UPDATE publications SET last_edit_date = now() WHERE publicationid = NEW.publicationid;
RETURN NULL;
END;
$func$ LANGUAGE plpgsql;
CREATE TRIGGER answer_update_question_timestamp AFTER INSERT OR UPDATE ON publications
FOR EACH ROW EXECUTE PROCEDURE trigger_update_question_timestamp();
CREATE TABLE publications
(
publicationid SERIAL PRIMARY KEY,
body VARCHAR(1000) NOT NULL ,
creation_date TIMESTAMP DEFAULT now() NOT NULL,
userid INTEGER NOT NULL,
last_edit_date TIMESTAMP,
CONSTRAINT body_length CHECK (CHAR_LENGTH(body) >= 10 AND CHAR_LENGTH(body) <= 1000),
CONSTRAINT "FK_publications_users"
FOREIGN KEY ("userid") REFERENCES users ("userid") ON DELETE SET NULL ON UPDATE CASCADE
);
I'm using PhpStorm and when I manually edit a line I get the following error:
[54001] ERROR: stack depth limit exceeded Hint: Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate. Where: SQL statement "SELECT 1 FROM ONLY "public"."users" x WHERE "userid" OPERATOR(pg_catalog.=) $1 FOR KEY SHARE OF x" SQL statement "UPDATE publications SET last_edit_date = now() WHERE publicationid = NEW.publicationid" PL/pgSQL function trigger_update_question_timestamp()
What does this exactly means? Does it have to do with my trigger or any other definition?
Your trigger is recursive, that is, the trigger function executes an UPDATE that calls the trigger function again.
The solution here is to use a BEFORE INSERT OR UPDATE trigger, that modifies NEW as follows:
BEGIN
NEW.last_edit_date := current_timestamp;
RETURN NEW;
END;

Insert record dynamically inside of Procedural Trigger

We are looking to convert our database over to Postgres (9.3.5), which I have no experience with, and I am trying to get our audit tables up and running. I understand that each table will need its own trigger, but all triggers can call a single function.
The trigger on the table is passing a list of the columns that need to be audited since some of our columns are not tracked.
Here are some of the posts I followed:
- https://stackoverflow.com/a/7915100/229897
- http://www.postgresql.org/docs/9.3/static/plpgsql-statements.html
- http://www.postgresql.org/docs/9.4/static/plpgsql-trigger.html
When I run this I get the error: ERROR: syntax error at or near "$1"
DROP TABLE IF EXISTS people;
DROP TABLE IF EXISTS a_people;
CREATE TABLE IF NOT EXISTS people (
record_id SERIAL PRIMARY KEY NOT NULL,
first_name VARCHAR NOT NULL,
last_name VARCHAR NOT NULL,
last_updated_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS a_people (
record_id SERIAL PRIMARY KEY NOT NULL,
a_record_id INT,
first_name VARCHAR NULL,
last_name VARCHAR NULL,
last_updated_on TIMESTAMP
);
/******************************************************/
--the function
CREATE OR REPLACE FUNCTION audit_func()
RETURNS TRIGGER AS
$BODY$
DECLARE
audit TEXT := TG_TABLE_SCHEMA || '.a_' || TG_TABLE_NAME;
cols TEXT := TG_ARGV[0];
BEGIN
EXECUTE format('INSERT INTO %1$s(a_%2$s) SELECT %2$s FROM ($1)', audit, cols) USING OLD;
NEW.last_updated_on = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
/******************************************************/
--the trigger calling the function to update inbound records
CREATE TRIGGER build_user_full_name_trg
BEFORE UPDATE
ON people
FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE audit_func('record_id,first_name,last_name');
/******************************************************/
INSERT INTO people (first_name, last_name) VALUES ('George','Lincoln');
UPDATE people SET last_name = 'Washington' WHERE first_name = 'George';
SELECT * FROM people;
I welcome your assistance (and patience)!
This subselect should work:
EXECUTE format('INSERT INTO %1$s(a_%2$s) SELECT %2$s FROM (select ($1).*) XX', audit, cols) USING OLD;

CONSTRAINT to check values from a remotely related table (via join etc.)

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
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 she 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;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

how to create a column constant in Postgresql

create table test(
t_id SERIAL primary key,
t_date CONSTANT date default CURRENT_DATE
);
ERROR: syntax error at or near "date"
LINE 3: t_date CONSTANT date default CURRENT_DATE
^
********** Error **********
ERROR: syntax error at or near "date"
SQL state: 42601
For a default value you can use a function,
CREATE TABLE test(
t_id SERIAL primary key,
t_date date DEFAULT now()
);
about constant, I never used, even other SQL (!), only in a PL/SQL context ...
If you need a "no update" constraint, you can use a trigger. Example:
CREATE FUNCTION correct_update() RETURNS trigger AS $$
BEGIN
NEW.t_date=OLD.t_date;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER no_date_update
BEFORE BEFORE UPDATE ON test
FOR EACH ROW
WHEN (OLD.t_date IS DISTINCT FROM NEW.t_date)
EXECUTE PROCEDURE correct_update();
For a complete control, you need also trigg the INSERT event, (and does not need a default value anymore because insert trigger will do):
create table test(
t_id SERIAL primary key,
t_date date -- a default will be redundant
);
CREATE FUNCTION correct_date() RETURNS trigger AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.t_date=now(); -- default value
ELSIF TG_OP = 'UPDATE' THEN -- optional AND OLD.t_date != NEW.t_date
NEW.t_date=OLD.t_date; -- "constant" behaviour
END IF;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER constant_date
BEFORE INSERT OR UPDATE ON test
FOR EACH ROW
EXECUTE PROCEDURE correct_date();
The OLD.t_date != NEW.t_date comparison is optional, because not affects performance... But is a good practice to use it. Another way is to check in the trigger, by WHEN, but only update triggers can use OLD... So, the best create-triggers for the same correct_date() function (with no old/new comparison) are:
CREATE TRIGGER constant_date_ins
BEFORE INSERT ON test
FOR EACH ROW
EXECUTE PROCEDURE correct_date();
CREATE TRIGGER constant_date_upd
BEFORE UPDATE ON test
FOR EACH ROW
WHEN (OLD.t_date IS DISTINCT FROM NEW.t_date)
EXECUTE PROCEDURE correct_date();
Contextualizing in a scenario
As commented above in the question, there are a lack of contextualization , ex. explaining "why you think this should work and what it should do".
Scenario-1: the db-master need to block careless programmers
We can imagine a framework like CakePHP with a "created" field and a database-master that wants that this field have a "constant behaviour", preventing that careless programmers affects this "expected constraint".
That kind of scenario was used in the anwser.
Scenario-2: the project decision is to alert by error
This is the suggestion #IgorRomanchenko ...
... now here as a Wiki, you can EDIT and add new solution/example ...
You want a check constraint
create table test(
t_id SERIAL primary key,
t_date date default CURRENT_DATE check(t_date = current_date)
);
insert into test(t_date) values (default);
INSERT 0 1
insert into test(t_date) values ('2014-01-01');
ERROR: new row for relation "test" violates check constraint "test_t_date_check"
DETAIL: Failing row contains (2, 2014-01-01).
Or may be a foreign key constraint which allows multiple possible values and can be updated without altering the table's schema
create table date_constraint (
date_constraint date primary key
);
insert into date_constraint (date_constraint) values (current_date);
create table test(
t_id SERIAL primary key,
t_date date
default CURRENT_DATE
references date_constraint(date_constraint)
);
insert into test(t_date) values (default);
INSERT 0 1
insert into test(t_date) values ('2014-01-01');
ERROR: insert or update on table "test" violates foreign key constraint "test_t_date_fkey"
DETAIL: Key (t_date)=(2014-01-01) is not present in table "date_constraint".
http://www.postgresql.org/docs/current/static/ddl-constraints.html

Trigger not letting me DELETE in PostgreSQL

I am trying to capture data changes on a table and am executing the following trigger function AFTER INSERT OR UPDATE as well as BEFORE UPDATE OR DELETE:
CREATE OR REPLACE FUNCTION cdc_test_function()
RETURNS trigger AS
$BODY$
DECLARE op cdc_operation_enum;
BEGIN
op = TG_OP;
IF (TG_WHEN = 'BEFORE') THEN
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_BEFORE';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT, DEFAULT, op, DEFAULT, DEFAULT, OLD.*);
ELSE
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_AFTER';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT, DEFAULT, op, DEFAULT, DEFAULT, NEW.*);
END IF;
RETURN NEW;
END;
My change table (CDC_TEST) is capturing everything properly and I can both INSERT and UPDATE records just fine in my TEST table. However, when I try to DELETE, it records the DELETE entry perfectly in CDC_TEST, but the record remains in my TEST table. If I disable the trigger, then I can DELETE from TEST just fine. Here is the code I used to create my tables as well as the code for my enum:
CREATE TABLE test
(
test_id serial NOT NULL,
value text NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (test_id )
)
CREATE TABLE cdc_test
(
cdc_test_id bigserial NOT NULL,
cdc_timestamp timestamp with time zone DEFAULT now(),
cdc_opeation cdc_operation_enum,
cdc_user name DEFAULT "current_user"(),
cdc_transaction_id bigint DEFAULT txid_current(),
test_id integer,
value text,
CONSTRAINT cdc_test_pkey PRIMARY KEY (cdc_test_id )
)
CREATE TYPE cdc_operation_enum AS ENUM( 'DELETE', 'INSERT', 'UPDATE_BEFORE', 'UPDATE_AFTER', 'UPDATE' );
Return OLD when the trigger runs for a deletion, NEW for an update.