I create 2 tables with postgresql:
afp
(
id bigint NOT NULL DEFAULT nextval('afp_id_seq'::regclass),
perm_number character varying(90),
type bigint,
start_date character varying(50),
CONSTRAINT afp_pkey PRIMARY KEY (id)
)
history_afp
(
id bigint NOT NULL DEFAULT nextval('history_afp_id_seq'::regclass),
reason bigint,
type bigint,
ask_date character varying(50),
CONSTRAINT history_afp_pkey PRIMARY KEY (id)
)
I wanted to have an history trigger so when I will delete a row in afp the row will go in history_afp
CREATE FUNCTION public.before_update()
RETURNS TRIGGER
SET SCHEMA 'public'
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO public.history_afp (
type,
ask_date
)
VALUES (
OLD.type,
OLD.start_date);
RETURN NEW;
END;
$$;
CREATE TRIGGER before_update_trigger
BEFORE DELETE
ON public.afp
FOR EACH ROW
EXECUTE PROCEDURE public.before_update();
When I delete the row in table afp, it creates my new row in history_afp but it creates again the row in my table afp.
Someone have any idea why I have this error ?
You should
RETURN OLD;
rather than
RETURN NEW;
The problem is that NEW is undefined in delete triggers, and returning NULL will abort the deletion.
Related
I have a the following DDL and would like to generate an ID on insert to the table, but ONLY if the ID has not been provided already.
drop table if exists table1;
create table table1 (
pk_id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
other_id INT GENERATED ALWAYS AS IDENTITY -- DO THIS ONLY IF NULL
)
I was thinking of using a stored procedure to do this and have attempted a start with the below code, but have been unable to get it working:
create or replace function conditionally_generated_identity(other_id integer)
returns table (id integer) as $$
begin
if other_id is null then
return query other_id generated always as identity;
elsif other_id is not null then
return query other_id;
end if;
end;
$$ language plpgsql;
CREATE TRIGGER for two tables in Postgres
I am using Postgres. I have two tables here in my database, payroll and staff_absences. staff_absences is a link table to a payroll table that logs what absence the staff has and to calculate in payroll table.
The staff absent is calculated from start_date - end_date by a trigger in the staff_absences table. I'm trying to input absences_no into payroll by a trigger.
The trigger is accepted by the schema and I can insert data into staff_absences table. However, it gives me this error when I try to update payroll table.
{ERROR: missing FROM-clause entry for table "staff_absences"
LINE 1: SELECT NEW.staff_absences.start_date - NEW.staff_absences.en...
^
QUERY: SELECT NEW.staff_absences.start_date - NEW.staff_absences.end_date
CONTEXT: PL/pgSQL function lowlands_db.pay_trgr() line 4 at assignment}
These are the create scripts for payroll and staff_absences:
CREATE TABLE lowlands_db.payroll
(
payroll_id integer NOT NULL,
annual_salary numeric NOT NULL,
appointment_bonus numeric,
absences_id integer NOT NULL,
staff_id integer NOT NULL,
total_monthly_salary numeric,
monthly_salary numeric,
absence_no numeric,
tax numeric,
CONSTRAINT payroll_pkey PRIMARY KEY (payroll_id),
CONSTRAINT payroll_staff_id_fkey FOREIGN KEY (staff_id)
REFERENCES lowlands_db.staff (staff_id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
CREATE TABLE lowlands_db.staff_absences
(
absences_id integer NOT NULL,
start_date date NOT NULL,
end_date date,
cause_of_absences text COLLATE pg_catalog."default" NOT NULL,
staff_id integer NOT NULL,
absen_no numeric,
CONSTRAINT staff_absences_pkey PRIMARY KEY (absences_id),
CONSTRAINT staff_absences_staff_id_fkey FOREIGN KEY (staff_id)
REFERENCES lowlands_db.staff (staff_id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
CREATE FUNCTION lowlands_db.asb_pay_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
UPDATE lowlands_db.payroll
SET lowlands_db.payroll.absence_no = new.staff_absences.absen_no
WHERE lowlands_db.staff_absences.absences_id = NEW.lowlands_db.payroll.absences_id;
RETURN NEW;
END;
$BODY$;
CREATE FUNCTION lowlands_db.pay_trgr()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
NEW.absences_no = NEW.staff_absences.start_date - NEW.staff_absences.end_date;
NEW.monthly_salary = ROUND(NEW.annual_salary /12 );
NEW.tax = ROUND(NEW.monthly_salary * .2);
NEW.total_monthly_salary = ROUND((NEW.monthly_salary - NEW.tax)+ NEW.appointment_bonus);
RETURN NEW;
END;
$BODY$;
your help in advance.
I'm having difficulties in this simple trigger. My purpose is to verify before inserting a new register if is there's a register with the same field content which is "tag_id". If NEW tag_id is the same tag_id of any register on my table "coordenadas", then it updates, if not, it inserts a new one. When I try to insert sth, I get the error:
ERROR: record "old" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: PL/pgSQL function verifica_coo() line 7 at IF
I have this table:
CREATE TABLE public.coordenadas
(
id bigint NOT NULL,
pos_data timestamp without time zone,
pos_latitude double precision,
pos_longitude double precision,
tag_id bigint NOT NULL,
gado_id bigint NOT NULL,
CONSTRAINT coordenadas_pkey PRIMARY KEY (id),
CONSTRAINT coordenadas_gado_id_fkey FOREIGN KEY (gado_id)
REFERENCES public.gado (gado_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkj14dwmpa6g037ardymqc2q4lj FOREIGN KEY (tag_id)
REFERENCES public.tag (tag_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fktawrw6tlliq4ace5p7io87c5p FOREIGN KEY (gado_id)
REFERENCES public.gado (gado_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
This trigger:
CREATE TRIGGER verifica_coo
BEFORE INSERT OR UPDATE ON coordenadas
FOR EACH ROW EXECUTE PROCEDURE verifica_coo();
This function:
CREATE OR REPLACE FUNCTION public.verifica_coo()
RETURNS trigger AS $verifica_coo$
BEGIN
--
-- Verifica se é para inserir ou atualizar os dados na tabela.
--
IF (NEW.tag_id != OLD.tag_id ) THEN
INSERT INTO coordenadas (pos_data,pos_latitude,pos_longitude,tag_id,gado_id)
VALUES (NEW.pos_data,NEW.pos_latitude,NEW.pos_longitude,NEW.tag_id,NEW.gado_id);
ELSE
UPDATE coordenadas SET pos_data = NEW.pos_data, pos_latitude = NEW.pos_latitude, pos_longitude = NEW.pos_longitude WHERE tag_id = NEW.tag_id;
END IF;
RETURN NEW;
END;
$verifica_coo$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.verifica_coo()
OWNER TO postgres;
My insert:
INSERT INTO coordenadas (pos_data,pos_latitude,pos_longitude,tag_id,gado_id) VALUES ('21/08/2016', '-23.563844' ,'-46.322525', '2','2');
This is because:
OLD
Data type RECORD; variable holding the old database row for
UPDATE/DELETE operations in row-level triggers. This variable is
unassigned in statement-level triggers and for INSERT operations.
So you first need to check if you are doing an insert or update. That information is available in TG_OP
IF TG_OP = 'UPDATE' THEN
-- some code involving OLD
ELSE
-- other code
I have a trigger function that copy row of unique values to another table on update or insert that ALMOST work.
The trigger should only insert a new row to the sample table if the number don't exist in it before. Atm. it insert a new row to the sample table with the value NULL if the number already exist in the table. I dont want it to do anything if maintbl.number = sample.nb_main
EDIT: sample table and sample data
CREATE TABLE schema.main(
sid SERIAL NOT NULL,
number INTEGER,
CONSTRAINT sid_pk PRIMARY KEY (sid)
)
CREATE TABLE schema.sample(
gid SERIAL NOT NULL,
nb_main INTEGER,
CONSTRAINT gid_pk PRIMARY KEY (gid)
Example and desired result
schema.main schema.sample
number nb_main
234233 234233
234234 555555
234234
555555
555555
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO schema.sample(
nb_main)
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION schema.update_number()
OWNER TO postgres;
CREATE TRIGGER update_number
AFTER INSERT OR UPDATE
ON schema.maintbl
FOR EACH ROW
EXECUTE PROCEDURE schema.update_number();
I just found out that my select query is probably wrong, if I run SELECT query by itself it return one row 'NULL' but i should not?
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
Any good advice?
Best
If I understood correctly, you wish to append to schema.sample a number that has been inserted or updated in schema.maintbl, right?
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
IF (SELECT COUNT(*) FROM schema.sample WHERE number = NEW.number) = 0 THEN
INSERT INTO schema.sample(nb_main) VALUES (NEW.number);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
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();