simple Postgres Trigger function on update not triggering - postgresql

Probably something dumb, but I just can't make it work.
I have this table:
Table "public.office"
Column | Type | Collation | Nullable | Default
-------------+--------------------------+-----------+----------+-------------------------------
id | integer | | not null | generated by default as identity
name | text | | not null |
url | text | | |
domain | text | | |
...
Triggers:
updated_save_domain_from_url AFTER UPDATE OF url ON office FOR EACH ROW EXECUTE PROCEDURE save_domain_from_url()
And this trigger function:
-- TRIGGER FUNCTION
CREATE OR REPLACE FUNCTION public.save_domain_from_url()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
new.domain := substring(new.url from '(?:.*://)?(?:www\.)?([^/]*)');
RETURN new;
END;
$function$
Example row:
id | name | url | domain
-----+---------------------------------+--------+--------
425 | Van Halewyck & Marco Architects | [NULL] | [NULL]
When I update the URL:
update office set url = 'http://vanhalewyck-marco.com/en' where id = 425;
The domain is still null, trigger did not work:
id | name | url | domain
-----+---------------------------------+---------------------------------+--------
425 | Van Halewyck & Marco Architects | http://vanhalewyck-marco.com/en | [NULL]
Any hints of what might be going on, please help!!
Thanks

You need to have the trigger defined as BEFORE trigger. AFTER triggers (as yours) cannot alter the new record as they are executed after the record is inserted.
Best regards,
Bjarni

Related

Issue while passing arrays to stored Function

trying to delete records by passing arrays into the stored function.
testing=# select * from links;
id | url | name | description | last_update
----+------------------------------------+---------------------+-------------+-------------
1 | https://www.postgresqltutorial.com | PostgreSQL Tutorial | |
2 | http://www.oreilly.com | O'Reilly Media | |
7 | http://www.postgresql.org | PostgreSQL | |
8 | https://www.google.com | Google | | 2013-06-01
(4 rows)
My Function
CREATE OR REPLACE FUNCTION testing(TEXT[])
RETURNS INTEGER AS
$BODY$
DECLARE emp_id INTEGER;
BEGIN
SELECT id into emp_id from links e where name = ANY($1);
DELETE FROM links WHERE id = emp_id;
return emp_id;
END
$BODY$
LANGUAGE plpgsql;
Query to call function
SELECT * from testing(ARRAY['PostgreSQL','Google']::TEXT[]);
We pass two records to delete from the links table, But instead of that only one record is deleting.
testing=# select * from links;
id | url | name | description | last_update
----+------------------------------------+---------------------+-------------+-------------
1 | https://www.postgresqltutorial.com | PostgreSQL Tutorial | |
2 | http://www.oreilly.com | O'Reilly Media | |
7 | http://www.postgresql.org | PostgreSQL | |
(3 rows)
SELECT ... INTO only stores the first row in the variable and ignores the rest.
You should declare the function as RETURNS SETOF integer, omit the SELECT and run only the DELETE as detailed in my other answer. Then you can use an SQL function rather than a PL/pgSQL one, which will simplify everything.

Transactions confirmation received , but table remains empty

In my application, i write transactions to post gres schema prod.
In order to debug, I have using the psql command line client on OSX
My table the only fields I have to fill are the are message field (json blob) and and status field (text).
Here is what the schema looks like
Table "prod.suggestions"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------------+--------------------------+-----------+----------+--------------------+----------+--------------+-------------
id | uuid | | not null | uuid_generate_v4() | plain | |
message | jsonb | | not null | | extended | |
status | text | | not null | | extended | |
transaction_hash | text | | | | extended | |
created_at | timestamp with time zone | | | CURRENT_TIMESTAMP | plain | |
updated_at | timestamp with time zone | | | CURRENT_TIMESTAMP | plain | |
Indexes:
"suggestions_pkey" PRIMARY KEY, btree (id)
Triggers:
update_updated_at_on_prod_suggestions BEFORE UPDATE ON prod.suggestions FOR EACH ROW EXECUTE PROCEDURE update_updated_at()
here is the function the trigger executes:
create function update_updated_at()
returns trigger
as
$body$
begin
new.updated_at = current_timestamp;
return new;
end;
$body$
language plpgsql;
Here is query to write the message:
INSERT INTO prod.suggestions (message, status) VALUES ('{"name": "Paint house", "tags": ["Improvements", "Office"], "finished": true}' , 'rcvd');
It returns INSERT 0 1 which I assume is a sucesss.
however when i query the table, it doesnt return anything.
select * from prod.suggestions;
I will appreciate any pointers on this.
This had nothing to do with postgres. I have another workers thread that was deleting all the data from the table.

Trigger to update a count number based on another table records

I have these two tables Review & Listing
| listing_id | review_id | comment |
|------------|----------------------------|--------------------|
| 5629709 | 123 | Beautiful |
| 4156372 | 231 | Wonderful |
| 4156372 | 432 | Very Good |
| 4156372 | 649 | Excellent |
| listing_id | number_of_reviews |
|------------|----------------------------|
| 5629709 | 1 |
| 4156372 | 2 |
Is there a way to create an trigger function that when the Review table has an update (insert or delete) then the number_of_reviews column in the Listing table updates also(+1 or -1)?
CREATE OR REPLACE FUNCTION function_number_of_reviews() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
Update public."Listing" set number_of_reviews = number_of_reviews + 1 where id = new.listing_id;
end if;
if TG_OP='DELETE' then
Update public."Listing" set number_of_reviews = number_of_reviews - 1 where id = old.listing_id;
end if;
RETURN new;
END;
$BODY$
language plpgsql;
CREATE TRIGGER trig_number_of_reviews
AFTER INSERT OR DELETE ON public."Review"
FOR EACH ROW
EXECUTE PROCEDURE function_number_of_reviews();
This is the wright way
Materializing values that can be calculated from other values bears a severe risk for inconsistencies. If possible avoid that.
In your case drop the number_of_reviews column in listing and create a view calculating the numbers for that column instead.
CREATE VIEW listing_with_number_of_reviews
AS
SELECT l.listing_id,
count(r.review_id) number_of_reviews
FROM listing l
LEFT JOIN review r
ON r.listing_id = l.listing_id
GROUP BY l.listing_id;

postgREST can find relation

I'm trying to set up postgREST. Have been following the tutorial at http://postgrest.org/en/v5.1/tutorials/tut0.html. Here is what I see. First, the schemas:
entercarlson=# \dn
List of schemas
Name | Owner
--------+---------
api | carlson
public | carlson
Then a table:
carlson=# \d api.todos
Table "api.todos"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------------------------------------
id | integer | | not null | nextval('api.todos_id_seq'::regclass)
done | boolean | | not null | false
task | text | | not null |
due | timestamp with time zone | | |
Indexes:
"todos_pkey" PRIMARY KEY, btree (id)
Finally, some data:
carlson=# select * from api.todos;
id | done | task | due
----+------+-------------------+-----
1 | f | finish tutorial 0 |
2 | f | pat self on back |
(2 rows)
But then I get this:
$ curl http://localhost:3000/todos
{"hint":null,"details":null,"code":"42P01","message":"relation
\"api.todos\" does not exist"}
Which is consistent with this:
carlson=# \d
Did not find any relations.
What am I doing wrong?
PS. I don't see which database this schema belongs to
Seems you're targeting the wrong database, check the db-uri config value and make sure this uri contains the api.todos table through psql.
Also, want to clarify that the search_path is modified by PostgREST on each request, so if you ALTER your connection user search_path it'll have no effect on the schemas PostgREST searches.

postgresql check session config setting availability

I have a sensor data table full of 'holes', the data is only saved when it changes over an specified threshold.
| time stamp | value1 | value2 |
| 2012-01-01 | 1 | |
| 2012-01-02 | | 2 |
| 2012-01-05 | | |
| 2012-01-20 | | |
| 2012-03-01 | 3 | 1 |
I need to do cross row calculations, but the holes in the tables make it impractical. My solution was to create an auxiliary function to wrap up the related field, and use session parameter as a persistent variable. sth like this:
CREATE FUNCTION wrap_data( data1 numeric(3,1) )
... LANGUAGE plpgsql
IF data1 <> NULL THEN
EXECUTE 'SET SESSION app.data1 = $1;' USING data1;
END IF;
RETURN current_setting('app.data1')::numeric(3,1)
...
It works but before executing the function, I always have to initialized the variables outside of the function by:
SET SESSION app.data1 = 11.1;
Or it will complain parameter not unrecognized.
I am looking for a function like IfParameterExist(), which allow me to check the existence of this variable without raising error inside my plpgsql function.