I have a view with a trigger that executes on each insert. This trigger actually updates several other tables. Bellow is the pseudo code:
CREATE OR REPLACE FUNCTION insert_proc() RETURNS trigger AS $$
DECLARE
_id int4;
BEGIN
INSERT INTO table1 VALUES (NEW.key) RETURNING id INTO _id;
INSERT INTO table2 VALUES (DEFAULT,_id,NEW.field1,NEW.field2);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_trg INSTEAD OF INSERT ON myview FOR EACH ROW EXECUTE PROCEDURE insert_proc();
table1 is actually having a column of type SERIAL that generates new ids on each insert. This id is used to create a row in table2.
Now, I'd like to retrieve the generated id from Java code. With a "regular" table, I'd use Statement.getGeneratedKeys(). But I can't get this to work with the view/trigger, as the returned ResultSet is empty.
Does anyone have an idea?
Related
I have two identical tables: table1 and table2. I need to replicate all updates from one table to another using an after trigger. But I don't want to list all columns names in the update statement(except the PK). Is it possible to do something like this?
CREATE FUNCTION replicate_changes()
RETURNS trigger
LANGUAGE 'plpgsql' AS
$BODY$
BEGIN
UPDATE table2
SET (*) = NEW.*;
WHERE table2.id = NEW.id;
RETURN NULL;
END;
$BODY$;
CREATE TRIGGER trigger_replicate_changes
AFTER UPDATE
ON table1
FOR EACH ROW
EXECUTE FUNCTION replicate_changes();
You cannot do this directly, but you can delete the existing row and insert/select the new row. (see demo).
create or replace function replicate_changes()
returns trigger
language 'plpgsql' as
$body$
begin
delete from table2 where id = old.id;
insert into table2
select * from table1
where id = new.id;
return null;
end;
$body$;
But be careful, this is a dangerous process. What happens when you insert or delete from table1. As it stand those actions will not be reflected in table2. And what happens when DML is issued directly against table2, or only 1 of the table definitions gets updated (ddl) bu not the other.
I have an already made table:
cotizacion(idCot(PK), unit_price,unit_price_taxes)
I need to convert unit_price_taxes into a generated column that is equal to unit_price*1.16. The issue is I can't find the alter table statement which will give me this. Dropping table and creating it again is not an option as this table is already deeply linked with the rest of the database and reinserting all records is not an option at this point.
I tried the following:
ALTER TABLE cotizacion
alter column unit_price_taxes set
GENERATED ALWAYS AS (unit_price*1.16) STORED;
But it's not working. Does anybody know how to get this done or if it's even possible? I would like to avoid creating a new column.
Thanks!
**EDIT:
I also tried the following trigger implementation:
CREATE OR REPLACE FUNCTION calculate_price_taxes()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
declare pu money;
begin
select unit_price from cotizacion into pu
where idCot = new."idCot";
update cotizacion
set unit_price_taxes = pu * (1.16)
where idCot = new."idCot";
return new;
end;
$function$
;
And the trigger delcaration:
Create or replace trigger price_taxes
after update on cotizacion
for each row
execute procedure
calculate_price_taxes()
The most probable reason for your trigger to go into an infinite recursion is that you are running an UPDATE statement inside the trigger - which is the wrong thing to do. Create a before trigger and assign the calculated value to the new record:
create trigger update_tax()
returns trigger
as
$$
begin
new.unit_price_taxes := unit_price * 1.16;
return new;
end;
$$
language plpgsql;
create trigger update_tax_trigger()
before update or insert on cotizacion
for each row execute procedure update_tax();
The only way to "convert" that column to a generated one, is to drop it and add it again:
alter table cotizacion
drop unit_price_taxes;
alter table cotizacion
add unit_price_taxes numeric generated always as (unit_price*1.16) stored;
Note that this will rewrite the entire table which will block access to it. Adding the trigger will be less invasive.
I'm trying to, somehow, trigger a automatic function drop when a table is dropped and I can't figure out how to do it.
TL;DR: Is there a way to trigger a function drop when a specific table is dropped? (POSTGRESQL 11.7)
Detailed explanation
I'll try to explain my problem using a simplified use case with dummy names.
I have three tables: sensor1, sensor2 and sumSensors;
A FUNCTION (sumdata) was created to INSERT data on sumSensors table. Inside this function I'll fetch data from sensor1 and sensor2 tables and insert its sum on table sumSensors;
A trigger was created for each sensor table which like this:
CREATE TRIGGER trig1
AFTER INSERT ON sensor1
FOR EACH ROW EXECUTE
FUNCTION sumdata();
Now, when a new row is inserted on tables sensor1 OR sensor2, the function sumdata will be executed and insert the sum of last values from both on table sumSensors
If I wanted to DROP FUNTION sumdata CASCADE;, the triggers would be automatically removed from tables sensor1 and sensor2. Until now that's everything fine! But that's not what I want.
My problem is:
Q: And if I just DROP TABLE sumSensors CASCADE;? What would happen to the function which was meant to insert on this table?
A: As expected, since there's no association between sumSensors table and sumdata function, the function won't be dropped (still exist)! The same happens to the triggers which use it (still exist). This means that when a new row is inserted on sensor tables, the function sumdata will be executed and corrupted, leading to a failure (even the INSERT which triggered the function execution won't be actually inserted).
Is there a way to trigger a function drop when a specific table is dropped?
Thank you in advance
There is no dependency tracking for functions in PostgreSQL (as of version 12).
You can use event triggers to maintain the dependencies yourself.
Full example follows.
More information: documentation of event triggers feature, support functions.
BEGIN;
CREATE TABLE _testtable ( id serial primary key, payload text );
INSERT INTO _testtable (payload) VALUES ('Test data');
CREATE FUNCTION _testfunc(integer) RETURNS integer
LANGUAGE SQL AS $$ SELECT $1 + count(*)::integer FROM _testtable; $$;
SELECT _testfunc(100);
CREATE FUNCTION trg_drop_dependent_functions()
RETURNS event_trigger
LANGUAGE plpgsql AS $$
DECLARE
_dropped record;
BEGIN
FOR _dropped IN
SELECT schema_name, object_name
FROM pg_catalog.pg_event_trigger_dropped_objects()
WHERE object_type = 'table'
LOOP
IF _dropped.schema_name = 'public' AND _dropped.object_name = '_testtable' THEN
EXECUTE 'DROP FUNCTION IF EXISTS _testfunc(integer)';
END IF;
END LOOP;
END;
$$;
CREATE EVENT TRIGGER trg_drop_dependent_functions ON sql_drop
EXECUTE FUNCTION trg_drop_dependent_functions();
DROP TABLE _testtable;
ROLLBACK;
I have .sql file in which I am creating a table and inserting rows.
CREATE TABLE vehicle (
name TEXT PRIMARY KEY,
id INTEGER DEFAULT 0
)
INSERT INTO vehicle values('car',1);
INSERT INTO vehicle values('bus',2);
Instead of executing insert commands I want to have a function which will do the inserting above two rows. How I can call the function?
CREATE OR REPLACE FUNCTION insert()
RETURNS VOID AS $$
BEGIN
INSERT INTO vehicle values('car',1);
INSERT INTO vehicle values('bus',2);
RETURN;
END;
$$ LANGUAGE 'plpgsql';
Why do you need such a function?
Probably, all you need is just to wrap everything into single transaction block?
begin;
CREATE TABLE vehicle (
name TEXT PRIMARY KEY,
id INTEGER DEFAULT 0
);
INSERT INTO vehicle values('car',1);
INSERT INTO vehicle values('bus',2);
commit;
?
If no, consider using "anonymous" plpgsql block, w/o defining a function exclicitly -- it's useful when you need to run some code only once:
do $$
begin
INSERT INTO vehicle values('car',1);
INSERT INTO vehicle values('bus',2);
end;
$$ language plpgsql;
Finally, if you do need a function, ok, you defined it correctly, so let's run it. In Postgres, stored procedures are functions, what means that they can easily be integrated into regular SQL statements, so let's just use SELECT (in Postgres, SELECT statements are allowed to have no FROM clause):
select insert();
TRIGEER-->To get a column value from one table to other table when i insert values?
I am having two tables(customer_details and loan_balance).
What i need is, I must get the column (custid)of customer_details table to the loan_balance table when i insert the data into the loan_balance table.
This is the full set up of my query : SQL FIDDLE
So i need a trigger to be raised and the data should be updated automatically without dynamic insertion of custid.
Postgres has an unconventional way of creating triggers:
create a function that returns type trigger and return the NEW row record
create a trigger that executes the function
Here's the code you need:
CREATE FUNCTION synch_custid_proc()
RETURNS trigger AS $$
BEGIN
NEW.custid = (
select max(custid)
from customer_details
where creditid = NEW.creditid
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql
CREATE TRIGGER synch_custid_trig
BEFORE INSERT ON loan_amount
FOR EACH ROW
EXECUTE PROCEDURE synch_custid_proc();
I chosen to select max(custid) rather than simply custid when finding the value in case there are multiple rows that match. You might have to adjust this logic to suit your data.
See a live demo on SQLFiddle