Postgres after delete trigger does not fire - postgresql

I created this trigger functions. It works on INSERT and UPDATE. With DELETE operations this function it is not fired. I just tried after and before and the results are the same.
Can anyone find something that justify that ?
DECLARE
v_old_data json;
v_new_data json;
BEGIN
IF (TG_OP = 'UPDATE') THEN
v_old_data := row_to_json(OLD);
v_new_data := row_to_json(NEW);
INSERT INTO auditoria.auditoria_geral (audi_table,audi_user,audi_op,audi_old,audi_new,audi_query)
VALUES (TG_TABLE_NAME::TEXT,new.clnt_autorstat,substring(TG_OP,1,1),v_old_data,v_new_data, current_query());
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
v_old_data := row_to_json(OLD);
INSERT INTO auditoria.auditoria_geral (audi_table,audi_user,audi_op,audi_old,audi_query)
VALUES (TG_TABLE_NAME::TEXT,old.clnt_autorstat,substring(TG_OP,1,1),v_old_data, current_query());
RETURN null;
ELSIF (TG_OP = 'INSERT') THEN
v_new_data := row_to_json(NEW);
INSERT INTO auditoria.auditoria_geral (audi_table,audi_user,audi_op,audi_new,audi_query)
VALUES (TG_TABLE_NAME::TEXT,new.clnt_autorstat,substring(TG_OP,1,1),v_new_data, current_query());
RETURN NEW;
ELSE
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - Other action occurred: %, at %',TG_OP,now();
RETURN NULL;
END IF;
EXCEPTION
WHEN data_exception THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
WHEN unique_violation THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
WHEN OTHERS THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [OTHER] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
END;
CREATE TRIGGER auditoria_clientes
AFTER INSERT OR DELETE OR UPDATE
ON public.clientes
FOR EACH ROW
EXECUTE PROCEDURE public.auditoria_clientes();
Added the create trigger code.
Table "public.clientes"
Column | Type | Modifiers
----------------------+-----------------------------+----------------------------------------------------------------
clnt_codigo | integer | not null default nextval('clientes_clnt_codigo_seq'::regclass)
clnt_primeinome | character varying(50) |
clnt_ultimonome | character varying(50) |
clnt_genero | character varying(15) |
clnt_tp_pele | character varying(50) |
clnt_dtnasc | date |
clnt_telefone | character varying(50) |
clnt_email | character varying(100) |
clnt_conhecto | character varying(50) |
clnt_obs | character varying(1000) |
clnt_status | character varying(50) |
clnt_timestamp | timestamp without time zone |
clnt_autorstat | character varying(50) |
clnt_morada | character varying(200) |
clnt_codpostal | character varying(8) |
clnt_sms | boolean | default false
clnt_generico | boolean | default false
clnt_mes | integer | default 0
clnt_tipo | character varying(50) |
clnt_lojahabitual | integer | default 0
clnt_spa | boolean | default false
clnt_telefone2 | character varying(50) |
clnt_dtcriacao | date |
clnt_timestampaceita | timestamp without time zone |
Indexes:
"CLIENTES_pkey" PRIMARY KEY, btree (clnt_codigo)
"WDIDX_CLIENTES_CLNT_DTNASC" btree (clnt_dtnasc)
"WDIDX_CLIENTES_CLNT_EMAIL" btree (clnt_email)
"WDIDX_CLIENTES_CLNT_GENERICO" btree (clnt_generico)
"WDIDX_CLIENTES_CLNT_PRIMEINOME" btree (clnt_primeinome)
"WDIDX_CLIENTES_CLNT_TELEFONE" btree (clnt_telefone)
"WDIDX_CLIENTES_CLNT_ULTIMONOME" btree (clnt_ultimonome)
"fki_codpostal" btree (clnt_codpostal)
Triggers:
auditoria_clientes AFTER INSERT OR DELETE OR UPDATE ON clientes FOR EACH ROW EXECUTE PROCEDURE auditoria_clientes()
Added the table description like asked in the comments.

Related

PostgreSQL trouble trying to run a stored procedure HINT: No procedure matches the given name and argument types. SQL state: 42883

For some time I've been trying to get into PostgreSQL looking forward to use it as a RDBMS for a PyQt5 system. So far my main difficulty arises when trying to run a stored procedure where I usually get the 42883 error code. I already went through the need to cast the string variables to varchar and the date data to date which helped me sort same errors, but I couldn't pass through 42883. When checking the documentation, the error describes so many possibilities, some of them not to clear though, I couldn't get any clue from it.
I tried enclosing table names with quotes with similar results.
This is my first stored procedure try - even though simple is just a try since I foresee the need of using transactions -
CREATE OR REPLACE PROCEDURE public.es_load_image(IN _coordinates character varying, IN _photo
character varying, IN _report_date date, IN _reporterid smallint, IN _categoryid smallint, IN
_description character varying)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO image_points
(geom, photo, report_date, reporterid, categoryid, description)
VALUES(ST_GeomFromText('POINT('||_coordinates ||')', 4326), _photo, _report_date,
_reporterid,_categoryid, _description);
END;
$$
After running the stored procedure (PgAdmin4) as follows:
CALL es_load_image('-59.74553210 -35.75147550'::varchar,
'C:/gis/photos/photo_13052022_162549.jpg'::varchar,
'2022-05-13'::date, 3, 0, 'Just testing'::varchar)
I get this notice:
"ERROR: procedure es_load_image(character varying, character varying, date, integer, integer,
character varying) does not exist
LINE 1: CALL es_load_image('-59.74553210 -35.75147550'::varchar,
^
HINT: No procedure matches the given name and argument types. You might need to add explicit
type casts.
SQL state: 42883
Character: 6"
I supposed there is a simple solution to this issue but I wasn't able to figure it out.
Note: PostgreSQL 14.1
To follow up on my comment:
CREATE OR REPLACE PROCEDURE public.es_load_image(IN _coordinates character varying, IN _photo character varying, IN _report_date date, IN _reporterid smallint, IN _categoryid smallint, IN _description character varying)
LANGUAGE plpgsql
AS $procedure$
BEGIN
RAISE NOTICE '%, %, %, %, %, %', _coordinates, _photo, _report_date, _reporterid, _categoryid, _description;
END;
$procedure$
CALL es_load_image('-59.74553210 -35.75147550',
'C:/gis/photos/photo_13052022_162549.jpg',
'2022-05-13', 3::smallint, 0::smallint, 'Just testing')
;
NOTICE: -59.74553210 -35.75147550, C:/gis/photos/photo_13052022_162549.jpg, 2022-05-13, 3, 0, Just testing
The casting is done according to this:
select castsource::regtype, casttarget::regtype, castcontext from pg_cast where castsource = 'integer'::regtype;
castsource | casttarget | castcontext
------------+------------------+-------------
integer | bigint | i
integer | smallint | a
integer | real | i
integer | double precision | i
integer | numeric | i
integer | money | a
integer | boolean | e
integer | oid | i
integer | regproc | i
integer | regprocedure | i
integer | regoper | i
integer | regoperator | i
integer | regclass | i
integer | regcollation | i
integer | regtype | i
integer | regconfig | i
integer | regdictionary | i
integer | regrole | i
integer | regnamespace | i
integer | "char" | e
integer | bit | e
Where from here pg_cast:
castcontext char
Indicates what contexts the cast can be invoked in. e means only as an explicit cast (using CAST or :: syntax). a means implicitly in assignment to a target column, as well as explicitly. i means implicitly in expressions, as well as the other cases.

PostgreSQL: Function not updating records when called in a nested select

I have a plpgsql function updating records in my PostgreSQL 11 database (the goal is to change record ids and all foreign keys referencing it):
CREATE OR REPLACE FUNCTION replace_nip(old_nip character varying, new_nip character varying)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
nb_nips_found int;
BEGIN
raise notice 'NIP to replace: %, New NIP: %', old_nip, new_nip;
SELECT COUNT (nip) INTO nb_nips_found
FROM data_infos_patient WHERE nip = new_nip;
if nb_nips_found > 0 then
raise notice 'New NIP already existing, not doing anything.';
else
raise notice 'NIP will be replaced.';
-- temporary disable constraints till the end of the transaction
SET CONSTRAINTS ALL DEFERRED;
UPDATE data_prelevements SET PRV_NIP = new_nip WHERE PRV_NIP = old_nip;
UPDATE data_infos_biologie SET NIP = new_nip WHERE NIP = old_nip;
UPDATE data_chromosomes SET NIP = new_nip WHERE NIP = old_nip;
-- ...
-- A lot of other Updates ...
-- ...
UPDATE data_infos_patient SET NIP = new_nip WHERE NIP = old_nip;
end if;
return nb_nips_found;
END;
$function$
;
When I call it for updating a single record, like this:
select replace_nip('123456789','987654321');
it works, I got 0 as result (exit code which means the replacement has been done), and the records are well updated.
But as I have a lot of records to update, I created a table containing a list of old and new ids:
Table tmp_nip_old_new:
|old_nip |new_nip |
|---------------|---------------|
| 0000000002 |9999999992 |
| 0000000003 |9999999993 |
| 0000000004 |9999999994 |
| 0000000005 |9999999995 |
And I call the function like this:
select old_nip, new_nip, replace_nip(old_nip,new_nip) as result from (
select old_nip, new_nip
from tmp_nip_old_new
) as tmp_nip;
I got the excepted result:
|old_nip |new_nip |result |
|---------------|---------------|-----------|
| 0000000002 |9999999992 |0 |
| 0000000003 |9999999993 |0 |
| 0000000004 |9999999994 |0 |
| 0000000005 |9999999995 |0 |
BUT the records are NOT updated...
What am I doing wrong? Thanks.

PostgreSQL 9.5 - func doesn't enter the loop

I created a function that doesn't work well. It doesn't enter the loop and I don't understand why.
My func:
CREATE OR REPLACE FUNCTION Control_Reports_Pg.control_reports_fn (P_Report_Type smallint, P_Log_File_Name text,C_Path text) RETURNS bigint AS $body$
DECLARE
V_Return smallint;
V_Function_Return smallint:=1;
C_Daily_Reports varchar(400);
C_Function_Name varchar(200) := 'Control_Reports_Fn';
Rec_Daily_Reports CONTROL_REPORTS%ROWTYPE;
BEGIN
C_Daily_Reports := 'SELECT REPORT_ORDER,PROCEDURE_NAME,DIRECTORY_NAME,FILE_NAME,TITLE FROM CONTROL_REPORTS WHERE RUN_FLAG=1::bigint AND REPORT_TYPE=' || P_Report_Type || '::smallint ORDER BY REPORT_ORDER ';
RAISE NOTICE 'sql to run over in loop : %',C_Daily_Reports;
FOR Rec_Daily_Reports IN EXECUTE C_Daily_Reports
LOOP
RAISE NOTICE 'INSIDE LOOP OF CONTROL_REPORTS, Procedure_name : %, File_name : %, Directory_name : %',Rec_Daily_Reports.Directory_Name,Rec_Daily_Reports.File_Name, Rec_Daily_Reports.Title;
END LOOP;
........
mydb=> \d control_reports;
Table "mysc.control_reports"
Column | Type | Modifiers
----------------+------------------------+-----------
report_order | bigint |
report_type | smallint |
procedure_name | character varying(100) |
directory_name | character varying(100) |
file_name | character varying(100) |
title | character varying(500) |
run_flag | bigint |
The errors I get when I run the func from psql:
mysc=> select control_reports_pg.control_reports_fn(1::smallint ,
'daily_log_control_file.txt'::text,'/PostgreSQL/comb_logs'::text);
NOTICE: sql to run over in loop : SELECT
REPORT_ORDER,PROCEDURE_NAME,DIRECTORY_NAME,FILE_NAME,TITLE FROM CONTROL_REPORTS
WHERE RUN_FLAG=1::bigint AND REPORT_TYPE=1::smallint ORDER BY REPORT_ORDER
NOTICE: FUNC : Control_Reports_Fn, SQLERRM: invalid input syntax for
integer: "Chrg_in_b"
When I run the select in psql I don't get any errors and I get a result. Chrg_in_b is the value of the column Procedure_name of the first row that I get back from the select query. (If I drop the ORDER BY I just get a different procedure_name but same error).

Improving PL/pgSQL function

I just finished writing my first PLSQL function. Here what it does.
The SQL function attempt to reset the duplicate timestamp to NULL.
From table call_records find all timestamp that are duplicated.(using group by)
loop through each timestamp.Find all record with same timestamp (times-1, so that only 1 record for a given times is present)
From all the records found in step 2 update the timestamp to NULL
Here how the SQL function looks like.
CREATE OR REPLACE FUNCTION nullify() RETURNS INTEGER AS $$
DECLARE
T call_records.timestamp%TYPE;
-- Not sure why row_type does not work
-- R call_records%ROWTYPE;
S integer;
CRNS bigint[];
TMPS bigint[];
sql_stmt varchar = '';
BEGIN
FOR T,S IN (select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp having count(timestamp) > 1)
LOOP
sql_stmt := format('SELECT ARRAY(select plain_crn from call_records where timestamp=%s limit %s)',T,S-1);
EXECUTE sql_stmt INTO TMPS;
CRNS := array_cat(CRNS,TMPS);
END LOOP;
sql_stmt = format('update call_records set timestamp=null where plain_crn in (%s)',array_to_string(CRNS,','));
RAISE NOTICE '%',sql_stmt;
EXECUTE sql_stmt ;
RETURN 1;
END
$$ LANGUAGE plpgsql;
Help me understand more PL/pgSQL language my suggesting me how it can be done better.
#a_horse_with_no_name: Here how the DB structure looks like
\d+ call_records;
id integer primary key
plain_crn bigint
timestamp bigint
efd integer default 0
id | efd | plain_crn | timestamp
----------+------------+------------+-----------
1 | 2016062936 | 8777444059 | 14688250050095
2 | 2016062940 | 8777444080 | 14688250050095
3 | 2016063012 | 8880000000 | 14688250050020
4 | 2016043011 | 8000000000 | 14688240012012
5 | 2016013011 | 8000000001 | 14688250050020
6 | 2016022011 | 8440000001 |
Now,
select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp having count(timestamp) > 1
timestamp | count
-----------------+-----------
14688250050095 | 2
14688250050020 | 2
All that I want is to update the duplicate timestamp to null so that only one of them record has the given timestamp.
In short the above query should return result like this
select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp;
timestamp | count
-----------------+-----------
14688250050095 | 1
14688250050020 | 1
You can use array variables directly (filter with predicate =ANY() - using dynamic SQL is wrong for this purpose:
postgres=# DO $$
DECLARE x int[] = '{1,2,3}';
result int[];
BEGIN
SELECT array_agg(v)
FROM generate_series(1,10) g(v)
WHERE v = ANY(x)
INTO result;
RAISE NOTICE 'result is: %', result;
END;
$$;
NOTICE: result is: {1,2,3}
DO
Next - this is typical void function - it doesn't return any interesting. Usually these functions returns nothing when all is ok or raises exception. The returning 1 RETURN 1 is useless.
CREATE OR REPLACE FUNCTION foo(par int)
RETURNS void AS $$
BEGIN
IF EXISTS(SELECT * FROM footab WHERE id = par)
THEN
...
ELSE
RAISE EXCEPTION 'Missing data for parameter: %', par;
END IF;
END;
$$ LANGUAGE plpgsql;

Create dynamic tables based on a loop in PostgreSQL 9.2

I have a function where I want to create a table for a every year based on the year from bill date which I will be looping.
CREATE OR REPLACE FUNCTION ccdb.ccdb_archival()
RETURNS void AS
$BODY$
DECLARE dpsql text;
DECLARE i smallint;
BEGIN
FOR i IN SELECT DISTINCT EXTRACT(year FROM bill_date) FROM ccdb.bills ORDER BY 1 LOOP
DO $$
BEGIN
CREATE TABLE IF NOT EXISTS ccdb_archival.bills||i (LIKE ccdb.bills INCLUDING ALL);
BEGIN
ALTER TABLE ccdb_archival.bills ADD COLUMN archival_date timestamp;
EXCEPTION
WHEN duplicate_column THEN RAISE NOTICE 'column archival_date already exists in <table_name>.';
END;
END;
$$;
INSERT INTO ccdb_archival.bills
SELECT *, now() AS archival_date
FROM ccdb.bills
WHERE bill_date::date >= current_date - interval '3 years' AND bill_date::date < current_date - interval '8 years';
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
I want to concatenate the year with the actual table name for each year.
I am unable to do the same with the above code. I get an error:
ERROR: syntax error at or near "||"
LINE 3: CREATE TABLE IF NOT EXISTS ccdb_archival.bills||i (LI...
Please suggest how do I achieve my requirement.
you cannot compose strings with metadata. You should utilize execute: http://www.postgresql.org/docs/9.1/static/ecpg-sql-execute-immediate.html
To create N tables with a prefix use this script.
This code uses a for loop and variable to creates 10 table starting with prefix 'sbtest' namely sbtest1, sbtest2 ... sbtest10
create_table.sql
do $$
DECLARE myvar integer;
begin
for myvar in 1..10 loop
EXECUTE format('CREATE TABLE sbtest%s (
id SERIAL NOT NULL,
k INTEGER NOT NULL,
c CHAR(120) NOT NULL,
pad CHAR(60) NOT NULL,
PRIMARY KEY (id))', myvar);
end loop;
end; $$
Run it using psql -U user_name -d database_name -f create_table.sql
Example Table sbtest1 is as
id | k | c | pad
----+---+---+-----
(0 rows)
Table "public.sbtest1"
Column | Type | Collation | Nullable | Default | Storage | Stats
target | Description
--------+----------------+-----------+----------+-------------------------------------+----------+------
--------+-------------
id | integer | | not null | nextval('sbtest1_id_seq'::regclass) | plain |
|
k | integer | | not null | | plain |
|
c | character(120) | | not null | | extended |
|
pad | character(60) | | not null | | extended |
|
Indexes:
"sbtest1_pkey" PRIMARY KEY, btree (id)
Access method: heap