trigger product stock [duplicate] - postgresql

This question already has answers here:
Are PostgreSQL column names case-sensitive?
(5 answers)
Closed 7 years ago.
I created a trigger that allows me each time the hen I add a lot to the table amount he adds this amount in the article table, but my error says that the registration (new) items.
CREATE TABLE article (
"idArticle" integer NOT NULL,
"desArticle" character varying,
"famiArticle" integer,
"photoArticle" character varying,
"stkPhArticle" integer,
"stkThArticle" integer,
"seuSurArticle" integer,
"seuAlrArticle" integer,
"seuReapArticle" integer,
"phtAchaArticle" double precision,
"phtVentArticle" double precision,
"pttcArticle" double precision,
"tvaArticle" integer,
CONSTRAINT pk_idarticle PRIMARY KEY ("idArticle"),
CONSTRAINT fk_famiarticle FOREIGN KEY ("famiArticle")
REFERENCES famillearticle (idfa) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_tvaarticle FOREIGN KEY ("tvaArticle")
REFERENCES tvaarticle (idtva) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE TABLE lot (
"idLot" integer NOT NULL,
"idArticle" integer NOT NULL,
"emplacementLot" integer NOT NULL,
"qteLot" integer NOT NULL,
"dlcLot" date NOT NULL,
CONSTRAINT pk_idlot PRIMARY KEY ("idLot"),
CONSTRAINT fk_emplacementlot FOREIGN KEY ("emplacementLot")
REFERENCES etagere ("idEtag") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_idarticle FOREIGN KEY ("idArticle")
REFERENCES article ("idArticle") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
Trigger and function:
CREATE OR REPLACE FUNCTION ajouter_lot_article()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE article SET stkThArticle = stkThArticle + NEW.qteLot
WHERE article.idArticle = New.idArticle;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER apres_ajout_lot
AFTER INSERT
ON lot
FOR EACH ROW
EXECUTE PROCEDURE ajouter_lot_article();
erreur: "NEW" n'a pas de champs "idarticle"

Column names are case sensitive, so you need to put the columns in your trigger inside double quotes.
CREATE OR REPLACE FUNCTION ajouter_lot_article()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE article SET "stkThArticle" = "stkThArticle" + NEW."qteLot"
WHERE article."idArticle" = New."idArticle";
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION ajouter_lot_article()
OWNER TO postgres;

Related

CREATE TRIGGER for two tables in Postgres

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.

Create a history trigger?

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.

Column reference is ambiguous for INSERT ON CONFLICT UPDATE inside a function in PostgreSQL

I have a table given by the source:
create table person (
id serial not null,
name varchar(50) not null unique,
age int not null,
constraint person_pkey primary key (id)
)
And I want to define a function my_func that inserts a new record or updates age with the given one. I implemented it like this:
create or replace function my_func(name varchar(50), age int) returns void as $$
begin
insert into person ("name", "age") values (my_func.name, my_func.age)
on conflict ("name") do update
set age = my_func.age
where person.name = my_func.name;
end
$$ language plpgsql;
It gives me the error:
my_func('Alex', 31);
ERROR: column reference "name" is ambiguous
I can't understand where is this ambiguous "name"? I tried INSERT and UPDATE separately, they work fine. What should I change in my function in order to make it work correctly?
Notice: I would like to keep the same names for the columns and arguments of the function.
It's apparently not a bug, but there is no way to resolve the conflict using the default variable conflict resolution behavior: https://www.postgresql.org/message-id/CAE3TBxyCn9dOF2273ki%3D4NFwsaJdYXiMQ6x2rydsWY_6p8z_zg%40mail.gmail.com
This is not a bug. It's a naming conflict due to having a parameter
and a column with the same name (a).
You can use different name for the parameter or tell Postgres how to
resolve such conflicts:
CREATE OR REPLACE FUNCTION pg_temp.testf(a int)
RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN INSERT INTO testt(a) SELECT testf.a ON CONFLICT (a) DO NOTHING; END
$body$;
See the documentation:
https://www.postgresql.org/docs/current/static/plpgsql-implementation.html
The key part is adding #variable_conflict use_column to the function to resolve the conflict without having to change the parameter name or reference the constraint name instead of the column name.
Likewise I see no reason why the current code shouldn't work; yet that seemes to be the case. I got it working through creating a names constraint and referencing it in the ON Conflict clause.
create table person (
id serial not null,
name varchar(50) not null,
age int not null,
constraint person_pkey primary key (id),
constraint person_name_uk unique (name)
);
--
create or replace function my_func(name varchar, age int) returns void as $$
begin
insert into person (name, age) values (my_func.name, my_func.age)
on conflict on constraint person_name_uk
do update
set age = excluded.age
where person.name = excluded.name;
end
$$ language plpgsql;
select my_func('George',35);
select * from person;
select my_func('George',45);
select * from person;
Still, it's strange!

ERROR: record "old" is not assigned yet

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

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();