how to see properties available for access with NEW plpgsql - sql

Say I have a PLPGSQL function
CREATE OR REPLACE FUNCTION function_name
RETURNS TRIGGER AS ...
BEGIN
PERFORM
1
FROM
table1 t1
JOIN
table2 t2 USING( column_name )
WHERE
t1.column_name = NEW.column_name;
RETURN NEW;
END;
DROP TRIGGER IF EXISTS trigger_name
ON table1;
CREATE TRIGGER trigger_name
BEFORE INSERT ON table1
FOR EACH ROW EXECUTE PROCEDURE function_name;
I noticed that only some columns in table1 and table2 are accessible with NEW.column_name. How can I see the full list of columns I can access with NEW?
Additionally, if there is a column in table1 or table2 that I cannot access with NEW, how can I make it accessible to NEW?

Look at this line:
BEFORE INSERT ON table1
This tells you that the trigger is executed before an INSERT ON table1. This means that you will have NEW.column_name for any column of table1, while table2 is unchanged by the trigger and makes no sense to use OLD or NEW on it, hence it illegal. So, to be precise: NEW works for table1 columns and does not work on table2 columns.

Related

Sqlite running update in insert trigger to execute update trigger

Using sqlite I want to have a field 'url' generated from the given 'id' using a trigger both on insert and on update. For example id: '1' => url: 'test.com/1'
The table looks like this:
CREATE TABLE t1 (
id TEXT,
url TEXT
);
Since sqlite can't run the same trigger for update and insert, I see two options to accomplish this.
Option A
run a trigger after insert, that updates the id to itself, which in turns triggers the update trigger
CREATE TRIGGER run_updates_on_insert AFTER INSERT ON t1
BEGIN
UPDATE t1 SET id = NEW.id WHERE id = NEW.id;
END;
CREATE TRIGGER set_url_on_update BEFORE UPDATE on t1
BEGIN
UPDATE t1 SET url = 'test.com/' || NEW.id WHERE id = OLD.id;
END;
Option B
replicating the logic in two separate triggers for update and insert
CREATE TRIGGER set_url_on_insert AFTER INSERT on t1
BEGIN
UPDATE t1 SET url = 'test.com/' || NEW.id WHERE id = NEW.id;
END;
CREATE TRIGGER set_url_on_update BEFORE UPDATE on t1
BEGIN
UPDATE t1 SET url = 'test.com/' || NEW.id WHERE id = OLD.id;
END;
Both of these options give me the desired results, I tend to favor Option A, as I only have to write the update logic once, but I was wondering if there are any other advantages/disadvantage to prefer one to the other?
EDIT: For this particular use case it is better to use generated column (see forpas answer below)
Since version 3.31.0 (2020-01-22) of SQLite, you can create generated columns (stored or virtual), so you don't need any triggers:
CREATE TABLE t1 (
id TEXT,
url TEXT GENERATED ALWAYS AS ('test.com/' || id) STORED
);

How to delete 1 table if another table exists in PostgreSQL using IF-statements

I'm trying to write a query that deletes/drops a table, if another table exists:
DO
$do$
BEGIN
IF EXISTS (SELECT FROM table1) THEN
DELETE FROM table2;
END IF;
END
$do$;
but all that it is doing is deleting rows from table1 if table2 exists rather than the table itself.
Can you use a function similar to the one above or should I use drop table if exists?
delete/drop a table if another table exists
That would be:
do $$
begin
if exists(select 1 from information_schema.tables where table_schema = current_schema() and table_name = 'table1') then
drop table table2;
end if;
end; $$ language plpgsql;
Rationale:
selecting from a table is not the right way to check if it exists - if it doesn't, an error is raised. Instead, you can query the information schema.
to remove a table, you want drop table; delete, on the other hand, removes the content of the table, not the table itself.

Trigger function not working properly on multiple inserts in a table

So basically I've got table1 that people do regular inserts, and table2 that needs to get updated based on what people insert on table1
table1 has
id integer
type text
ref text
geom polygon geometry
table2 has
idb integer
status integer
ref text
geom point geometry
Users can sometimes do bulk inserts into table1. My trigger function to update table2 is
CREATE OR REPLACE FUNCTION table2update()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
update table2 a set status = 50, ref = new."ref"
where new.type = 'POLE' and new."ref" is not null and st_contains(new.geom,a.geom);
RETURN new;
END;
$function$
;
trigger
CREATE TRIGGER derp BEFORE INSERT ON table1 FOR EACH ROW EXECUTE PROCEDURE table2update();
People do their inserts on a software called QGIS but it looks like the following
insert into table1(type,ref,geom)
values('POLE', 'derp','geom'),
('not pole', null, 'geom')
Expected behaviour, all the rows in table2 contained by the polygons in table1 will have the ref updated, as well as the status, but only where the type of inserts in table1 are 'POLE'
Actual behaviour, all the rows get updated with the status, but the type is completely ignored - in other words, the ref stays null but the status changes. When a user pastes a single row of type 'POLE', it works as intended however I do not want to encourage people not inputting all the data
How to fix the code?

Replacing trigger with a procedure

I have a table t1 with fields id, name and date. When I update the date to a certain value I want to move that entry to another table t2 (Remove it in t1 and insert it into t2). I explicitly want to do this on an UPDATE operation and not a DELETE operation.
I tried doing it with a AFTER UPDATE ON t1 trigger, however, I found out that I can't delete an entry from t1 in a trigger for t1.
Does that mean I need to do this with a procedure? If yes, how do I make a procedure run everytime I make an UPDATE of the date field to a certain value in t1?
Create a view for table t1 and then on the view create a INSTEAD OF TRIGGER:
create view v1 as select * from t1;
CREATE OR REPLACE TRIGGER IO_V1
INSTEAD OF UPDATE ON V1
FOR EACH ROW
BEGIN
INSERT INTO t2 ...;
END;
Of course, you can encapsulate all in a procedure and run this procedure rather than UPDATE statement directly.

trigger if else

I have a trigger that verifies if a field is null:
create or replace trigger trig1
after insert on table_1
for each row
begin
if ((select table2.column2 from table2 where table2.id= :new.id) isnull) then
update table2 set table2.column2 = :new.column1 where table2.id = :new.id;
end if;
end trig1;
.
run;
I get an error that the trigger is created with compilation errors. I don't know what the problem is. I use Oracle SQL*Plus 10.2.0
The PL/SQL syntax doesn't allow for including SQL statements in the IF clause.
The correct approach is to separate out the SELECT statement and then test for its result. So that would be:
create or replace trigger trig1
after insert on table_1
for each row
declare
v table2.column2%type;
begin
select table2.column2
into v
from table2
where table2.id= :new.id;
if v is null
then
update table2
set table2.column2 = :new.column1
where table2.id = :new.id;
end if;
end trig1;
Note that this does not handle the existence of multiple rows in table2 matching the criteria, or indeed there being no matching rows. It also doesn't handle locking.
Also, bear in mind that code like this doesn't function well in multi-user environments. That's why I mentioned locking. You ought really to use procedural logic to handle these sorts of requirements. Although as is often the case with ill-conceived triggers the real culprit is a poor data model. table2.column2 should have been normalised out of existence.