2015-03-05 15:35:35 IST HINT No function matches the given name and argument types. You might need to add explicit type casts. - postgresql

I have facing problem in the below function of postgres, I have tried by looking many examples like this, which has been uploaded by many others but still I dint find the solution. Please help me out.
I know the below function is lengthy. Thanks
CREATE OR REPLACE FUNCTION adt_transfer_patient(in_hospital_id integer, in_reg_no character varying(50), in_admt_id numeric, in_transfer_type character varying(25), in_release_cause character varying(1), in_user_id character varying(50), in_new_dept_id integer, in_new_unit_id integer, in_new_ward_id integer, in_consultant integer, in_icd character varying(50), in_icd_desc character varying(50))
RETURNS integer AS
$BODY$
DECLARE
temp_dept_id integer;
temp_unit_id integer;
temp_ward_id integer;
return_int integer;
temp_bed_id integer;
temp_present_position character varying;
temp_is_blocked integer;
max_diagnos_id integer;
tmp_bedno text;
tmp_ward text;
temp_node_ward_id integer;
tmp_tot_record integer;
tmp_int_var integer;
icdcode character varying(25);
disease_remark character varying(200);
BEGIN
temp_is_blocked=0;
--Present Department,Unit,Ward and Bed
SELECT dept_id INTO temp_dept_id FROM department_admission WHERE hospital_id=in_hospital_id AND ipd_admission_id=in_admt_id AND release_date IS NULL ORDER BY admission_date DESC limit 1;
SELECT unit_id INTO temp_unit_id FROM unit_admission WHERE hospital_id=in_hospital_id AND ipd_admission_id=in_admt_id AND release_date IS NULL;
SELECT ward_id INTO temp_ward_id FROM ward_admission WHERE hospital_id=in_hospital_id AND ipd_admission_id=in_admt_id AND release_date IS NULL
ORDER BY admission_date DESC limit 1;
SELECT COALESCE(bedid,'0') INTO temp_bed_id FROM bed_occupancy WHERE hospital_id=in_hospital_id AND ipd_admission_id=in_admt_id AND releasedate IS NULL;
--Tranfer Department
IF in_transfer_type = 'D' THEN
UPDATE department_admission SET release_date=current_timestamp,release_cause=in_release_cause,userid=in_user_id
WHERE reg_no=in_reg_no AND ipd_admission_id=in_admt_id AND hospital_id=in_hospital_id AND release_date IS NULL;
INSERT INTO department_admission(hospital_id,reg_no,ipd_admission_id,dept_id,admission_date,admission_time,userid)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_dept_id,current_timestamp,current_time,in_user_id);
END IF;
--Transfer Unit
IF in_transfer_type = 'D' OR in_transfer_type = 'U' THEN
UPDATE unit_admission SET release_date=current_timestamp,release_cause=in_release_cause,userid=in_user_id
WHERE reg_no=in_reg_no AND ipd_admission_id=in_admt_id AND hospital_id=in_hospital_id AND release_date IS NULL;
INSERT INTO unit_admission(hospital_id,reg_no,ipd_admission_id,unit_id,admission_date,admission_time,userid)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_unit_id,current_timestamp,current_time,in_user_id);
END IF;
--Transfer Ward
IF in_transfer_type = 'D' OR in_transfer_type = 'U' OR in_transfer_type = 'W' THEN
UPDATE ward_admission SET release_date=current_timestamp,release_cause=in_release_cause,userid=in_user_id
WHERE reg_no=in_reg_no AND ipd_admission_id=in_admt_id AND hospital_id=in_hospital_id AND release_date IS NULL;
IF in_transfer_type = 'W' THEN
INSERT INTO ward_admission(hospital_id,reg_no,ipd_admission_id,ward_id,admission_date,admission_time,userid,dept_id,unit_id)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_ward_id,current_timestamp,current_time,in_user_id,temp_dept_id,temp_unit_id);
ELSIF in_transfer_type = 'U' THEN
INSERT INTO ward_admission(hospital_id,reg_no,ipd_admission_id,ward_id,admission_date,admission_time,userid,dept_id,unit_id)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_ward_id,current_timestamp,current_time,in_user_id,temp_dept_id,in_new_unit_id);
ELSIF in_transfer_type = 'D' THEN
INSERT INTO ward_admission(hospital_id,reg_no,ipd_admission_id,ward_id,admission_date,admission_time,userid,dept_id,unit_id)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_ward_id,current_timestamp,current_time,in_user_id,in_new_dept_id,in_new_unit_id);
END IF;
END IF;
--Tranfer Bed
UPDATE bed_occupancy SET releasedate=current_timestamp,userid=in_user_id
WHERE reg_no=in_reg_no AND ipd_admission_id=in_admt_id AND hospital_id=in_hospital_id AND releasedate IS NULL;
--SELECT COUNT(*) into temp_is_blocked FROM mas_link_ward,ward_admission where mas_link_ward.parent_ward_id=ward_admission.ward_id
--and mas_link_ward.node_ward_id=temp_ward_id and ward_admission.release_date is null and abs(extract(epoch from current_timestamp - admission_date)/20000)>48;
-- SELECT COUNT(*) into temp_is_blocked FROM mas_link_ward,ward_admission where mas_link_ward.node_ward_id=ward_admission.ward_id and mas_link_ward.parent_ward_id=temp_ward_id and mas_link_ward.parent_dept_id=temp_dept_id and ward_admission.release_date is null and abs(extract(epoch from current_timestamp - admission_date)/20000)>48;
--SELECT node_ward_id into temp_node_ward_id FROM mas_link_ward,ward_admission where mas_link_ward.node_ward_id=ward_admission.ward_id and mas_link_ward.parent_ward_id=temp_ward_id and mas_link_ward.parent_dept_id=temp_dept_id and ward_admission.release_date is null and abs(extract(epoch from current_timestamp - admission_date)/20000)>48;
if temp_is_blocked is null then
temp_is_blocked=0;
end if;
IF temp_is_blocked>0 THEN
UPDATE mas_bed SET status='B' WHERE bedid=temp_bed_id AND hospital_id=in_hospital_id;
IF in_hospital_id=4 THEN
--*** insert record into notification table ***
select bedno into tmp_bedno from mas_bed where bedid=temp_bed_id;
select ward_name into tmp_ward from mas_ward where ward_id=temp_node_ward_id;
INSERT INTO table_notification(sl_no, notification, group_type, ward_id, dept_id, unit_id, employee_id,seen)
SELECT (SELECT COALESCE(max(sl_no),0) FROM table_notification)+row_number() OVER(),'Bed No'||tmp_bedno||' of ward '||tmp_ward||' is blocked','W',temp_node_ward_id,0,0,uid,0 FROM config.user_ward_mapping
WHERE ward_id=temp_node_ward_id;
END IF;
ELSE
UPDATE mas_bed SET status='A' WHERE bedid=temp_bed_id AND hospital_id=in_hospital_id;
END IF;
IF in_new_bed_id != 0 THEN
INSERT INTO bed_occupancy(hospital_id,reg_no,ipd_admission_id,bedid,occupancydate,occupied,userid,entrytime)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_new_bed_id,current_timestamp,'O',in_user_id,current_time);
UPDATE mas_bed SET status='O' WHERE bedid=in_new_bed_id AND hospital_id=in_hospital_id;
-- Set present Position
temp_present_position='~IPD^'||get_department_name(in_hospital_id,in_new_dept_id)||'^'||get_unit_name(in_hospital_id,in_new_unit_id)||'^'||get_ward_name(in_hospital_id,in_new_ward_id)||'^'||get_bed_no(in_hospital_id,in_new_bed_id);
END IF;
IF in_new_bed_id = 0 THEN
INSERT INTO bed_occupancy(hospital_id,reg_no,ipd_admission_id,bedid,occupancydate,occupied,userid,entrytime,transfer_to_floor)
VALUES (in_hospital_id,in_reg_no,in_admt_id,-1,current_timestamp,'O',in_user_id,current_time,'Y');
--UPDATE bed_occupancy SET transfer_to_floor='Y' WHERE reg_no=in_reg_no AND ipd_admission_id=in_admt_id AND hospital_id=in_hospital_id;
END IF;
--Entry in Patient transfer log
--NOTE: Valid Entry Against Trasnfer Type only
INSERT INTO patient_transfer_log(hospital_id, reg_no, ipd_admission_id, transfer_type, transfer_date,transfer_time, from_department_id, to_department_id, from_unit_id,
to_unit_id, from_ward_id, to_ward_id, from_bed_id, to_bed_id,consultant_id)
VALUES (in_hospital_id,in_reg_no,in_admt_id,in_transfer_type,current_timestamp,current_time,temp_dept_id,in_new_dept_id,temp_unit_id,
in_new_unit_id,temp_ward_id,in_new_ward_id,temp_bed_id,in_new_bed_id,in_consultant);
IF in_transfer_type = 'D' THEN
-- Set present Position
temp_present_position='~IPD^'||get_department_name(in_hospital_id,in_new_dept_id)||'^'||get_unit_name(in_hospital_id,in_new_unit_id)||'^'||get_ward_name(in_hospital_id,in_new_ward_id)||'^'||get_bed_no(in_hospital_id,in_new_bed_id);
ELSIF in_transfer_type = 'U' THEN
-- Set present Position
temp_present_position='~IPD^'||get_department_name(in_hospital_id,temp_dept_id)||'^'||get_unit_name(in_hospital_id,in_new_unit_id)||'^'||get_ward_name(in_hospital_id,in_new_ward_id)||'^'||get_bed_no(in_hospital_id,in_new_bed_id);
ELSIF in_transfer_type = 'W' THEN
-- Set present Position
temp_present_position='~IPD^'||get_department_name(in_hospital_id,temp_dept_id)||'^'||get_unit_name(in_hospital_id,temp_unit_id)||'^'||get_ward_name(in_hospital_id,in_new_ward_id)||'^'||get_bed_no(in_hospital_id,in_new_bed_id);
ELSIF in_transfer_type = 'B' THEN
-- Set present Position
temp_present_position='~IPD^'||get_department_name(in_hospital_id,temp_dept_id)||'^'||get_unit_name(in_hospital_id,temp_unit_id)||'^'||get_ward_name(in_hospital_id,temp_ward_id)||'^'||get_bed_no(in_hospital_id,in_new_bed_id);
END IF;
UPDATE patient_detail SET present_position=COALESCE(present_position,' ^ ^ ^ ^')||temp_present_position,ward_id=in_new_ward_id,ward_entry_date=current_timestamp,bedid=in_new_bed_id,occupancydate=current_timestamp
WHERE reg_no=in_reg_no AND
hospital_id=in_hospital_id AND ipd_admission_id=in_admt_id;
IF in_hospital_id=4 and in_icd<>'' THEN
select count(*) into tmp_tot_record from (select unnest(string_to_array(in_icd, ',')))tbl;
tmp_int_var = 1;
WHILE tmp_int_var <= tmp_tot_record LOOP
SELECT split_part(in_icd, ',', tmp_int_var) INTO icdcode;
SELECT split_part(in_icd_desc, ',', tmp_int_var) INTO disease_remark;
IF icdcode IS NOT NULL THEN
Select coalesce(max(slno),0)+1 into max_diagnos_id FROM tran_diagnosis_log where reg_no=in_reg_no;
INSERT INTO tran_diagnosis(
reg_no, slno,icd_o,remarks,
provisional_final, doctor_id, hospital_id, ipd_admission_no, entry_source,entry_date)
VALUES (in_reg_no, max_diagnos_id,icdcode,disease_remark,'Final', in_user_id::integer, in_hospital_id, in_admt_id,'TRANSFER',current_timestamp);
INSERT INTO tran_diagnosis_log(
reg_no, slno,icd_o,remarks,
provisional_final, doctor_id, hospital_id, ipd_admission_no, entry_source,entry_date)
VALUES (in_reg_no, max_diagnos_id,icdcode,disease_remark,'Final', in_user_id::integer, in_hospital_id, in_admt_id,'TRANSFER',current_timestamp);
tmp_int_var = tmp_int_var+1;
END IF;
END LOOP;
/**--update ipd_patient_admission_detail set disease=in_pro_diagnos where hospital_id=in_hospital_id and reg_no=in_reg_no and ipd_admission_id=in_admt_id;
Select coalesce(max(slno),0)+1 into max_diagnos_id FROM tran_diagnosis_log where reg_no=in_reg_no;
if max_diagnos_id=1 THEN
INSERT INTO tran_diagnosis(
reg_no, slno,
provisional_final, doctor_id, remarks, hospital_id, ipd_admission_no, entry_source)
VALUES (in_reg_no, max_diagnos_id,'Provisional', in_user_id::integer, in_pro_diagnos, in_hospital_id, in_admt_id,'TRANSFER');
INSERT INTO tran_diagnosis_log(
reg_no, slno,
provisional_final, doctor_id, remarks, hospital_id, ipd_admission_no, entry_source)
VALUES (in_reg_no, max_diagnos_id,'Provisional', in_user_id::integer, in_pro_diagnos, in_hospital_id, in_admt_id,'TRANSFER');
END IF;
if max_diagnos_id>1 THEN
INSERT INTO tran_diagnosis_log(
reg_no, slno,
provisional_final, doctor_id, remarks, hospital_id, ipd_admission_no, entry_source)
VALUES (in_reg_no, max_diagnos_id,'Provisional', in_user_id::integer, in_pro_diagnos, in_hospital_id, in_admt_id,'TRANSFER');
END IF; **/
END IF;
RETURN 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Related

How to implement "type is <typeName> table of rowid index by" concept in postgresql to compare table data with new inserted data

I am trying to convert below piece of code from oracle to postgres(Basically the goal is to insert some records in table based on some date without overlapping and if there is date range overlapping it should raise trigger.) But after lot of efforts unable to get clue how to do it?
DECLARE
V_check PLS_INTEGER;
BEGIN
SELECT COUNT(*) INTO V_check
FROM user_objects
WHERE object_name = 'STORE_ROWID'
AND object_type = 'PACKAGE';
IF V_check=0 THEN
EXECUTE IMMEDIATE 'CREATE PACKAGE store_rowid AS'
||CHR(10)||' TYPE T_rowid IS TABLE OF ROWID INDEX BY BINARY_INTEGER;'
||CHR(10)||' T_newones T_rowid;'
||CHR(10)||' T_empty T_rowid;'
||CHR(10)||'END store_rowid;';
END IF;
END;
CREATE OR REPLACE TRIGGER biu_base_host
BEFORE INSERT OR UPDATE ON BASE_HOST
BEGIN
STORE_ROWID.T_newones := STORE_ROWID.T_empty;
END biu_base_host;
CREATE OR REPLACE TRIGGER aiufr_base_host
AFTER INSERT OR UPDATE ON BASE_HOST
FOR EACH ROW
BEGIN
STORE_ROWID.T_newones(STORE_ROWID.T_newones.COUNT+1) := :NEW.ROWID;
END aiufr_base_host;
CREATE OR REPLACE TRIGGER aiu_base_host
AFTER INSERT OR UPDATE ON BASE_HOST
DECLARE
r BASE_HOST%ROWTYPE;
V_count INTEGER;
V_status PLS_INTEGER;
C_lockid CONSTANT PLS_INTEGER := 1001;
BEGIN
V_status := DBMS_LOCK.request(C_lockid,DBMS_LOCK.x_mode,release_on_commit=>TRUE);
IF V_status NOT IN (0,4)
THEN
RAISE_APPLICATION_ERROR(-20102,'DBMS_LOCK.request ERROR, rc='||V_status);
END IF;
FOR i IN 1..STORE_ROWID.T_newones.COUNT
LOOP
SELECT * INTO r
FROM BASE_HOST
WHERE ROWID = STORE_ROWID.T_newones(i);
SELECT COUNT(*) INTO V_count
FROM BASE_HOST
WHERE ROWID <> STORE_ROWID.T_newones(i)
AND ZONE_ID = r.ZONE_ID
AND SITE_ID = r.SITE_ID
AND ORG_ID = r.ORG_ID
AND (start_date BETWEEN r.start_date AND r.end_date
OR end_date BETWEEN r.start_date AND r.end_date
OR (start_date < r.start_date AND end_date > r.end_date));
IF V_count <> 0 THEN
RAISE_APPLICATION_ERROR(-20001,
'Range from '||
TO_CHAR(r.start_date,'YYYYMMDD-HH24MISS') ||
' to '||
TO_CHAR(r.end_date,'YYYYMMDD-HH24MISS')||
' overlaps existing records');
END IF;
END LOOP;
END aiu_base_host;
exclusion constraint I suppose is perfect way to achieve this and I have written below code to achieve the same functionality:
CREATE EXTENSION btree_gist;
create table base_host( zone_id numeric(3), site_id numeric(3), org_id VARCHAR(16), start_date date, end_date date, reserved_capacity numeric(5,2), last_update date, user_id VARCHAR(32), EXCLUDE USING GIST (zone_id WITH =,site_id WITH =,org_id WITH =,daterange("start_date", "end_date") WITH &&));

stack depth limit exceeded when fired a trigger

I am trying to build a slow changing dimensional table, it track all the history of records. The schema of the table is like this:
CREATE TABLE test.dim
(id text,
column1 text,
column2 text,
begin_date timestamp without time zone,
is_current boolean,
end_date timestamp without time zone)
I defined a trigger function, and fire it before each insert action:
CREATE OR REPLACE FUNCTION test.slow_change_func()
RETURNS trigger AS
$BODY$
DECLARE
BEGIN
IF ( NOT EXISTS ( SELECT 1 FROM yang_test.dim
WHERE id= NEW.id
AND(column1 = NEW.column1 OR (column1 is null AND NEW.column1 is null))
AND (column2 = NEW.column2 OR (column2 is null AND NEW.column2 is null))
AND is_current
)
)
THEN UPDATE yang_test.dim
SET (end_date, is_current) = (now(), FALSE)
WHERE id = NEW.id
AND is_current;
INSERT INTO test.dim (id, column1, column2, begin_date, is_current, end_date)
VALUES ( NEW.id, NEW.column1, NEW.column2, now(), TRUE, 'infinity'::timestamp );
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
CREATE TRIGGER slow_change_trigger
BEFORE INSERT
ON test.dim
FOR EACH ROW
EXECUTE PROCEDURE test.slow_change_func();
When I try to test it,
INSERT INTO test.dim (id, column1, column2, begin_date, is_current, end_date)
VALUES ( 1, 'hello', 'world', now(), TRUE, 'infinity'::timestamp )
it will throw an error: stack depth limit exceeded. it looks like the function is running a loop. any suggestion s?
I think I have figure this out, this will match my requirement:
CREATE OR REPLACE FUNCTION yang_test.slow_change_func()
RETURNS trigger AS
$BODY$
DECLARE
BEGIN
IF ( NOT EXISTS ( SELECT 1 FROM yang_test.dim
WHERE id= NEW.id
AND(column1 = NEW.column1 OR (column1 is null AND NEW.column1 is null))
AND (column2 = NEW.column2 OR (column2 is null AND NEW.column2 is null))
AND is_current
)
)
THEN UPDATE yang_test.dim
SET (end_date, is_current) = (now(), FALSE)
WHERE id = NEW.id
AND is_current;
ELSE RETURN null;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Having an ordinal column with no gaps

I want to have an ordinal column in which values always start from 1 and have no gaps. I have devised a solution with triggers, but I'd like to know if there is a better or more elegant way.
BEFORE INSERT trigger renumbers the rows that come after the inserted value. If value is not provided or too high, it is set to row count + 1. Similarly, AFTER DELETE trigger renumbers the rows that come after the deleted value. Both triggers lock rows before changing the value.
CREATE OR REPLACE FUNCTION ids_insert() RETURNS trigger AS $BODY$
DECLARE
_lock_sql text;
_id bigint;
BEGIN
IF TG_OP = 'INSERT' THEN
IF NEW.id < 1 THEN
RAISE EXCEPTION 'ID must be greater than zero.';
END IF;
EXECUTE format('SELECT COUNT(*) + 1 FROM %I', TG_TABLE_NAME)
INTO _id;
IF NEW.id IS NULL OR NEW.id > _id THEN
NEW.id := _id;
ELSE
_lock_sql := format(
'SELECT id FROM %I '
'WHERE id >= %s '
'ORDER BY id DESC '
'FOR UPDATE', TG_TABLE_NAME, NEW.id
);
FOR _id IN EXECUTE _lock_sql LOOP
EXECUTE format('UPDATE %I SET id = id + 1 WHERE id = %s', TG_TABLE_NAME, _id);
END LOOP;
END IF;
ELSE
IF NEW.id != OLD.id THEN
RAISE EXCEPTION 'Changing the ID directly is not allowed.';
END IF;
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION ids_delete() RETURNS trigger AS $BODY$
DECLARE
_lock_sql text;
_id bigint;
BEGIN
_lock_sql := format(
'SELECT id FROM %I '
'WHERE id > %s '
'ORDER BY id '
'FOR UPDATE', TG_TABLE_NAME, OLD.id
);
FOR _id IN EXECUTE _lock_sql LOOP
EXECUTE format('UPDATE %I SET id = id - 1 WHERE id = %s', TG_TABLE_NAME, _id);
END LOOP;
RETURN OLD;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TABLE test (
id bigint PRIMARY KEY,
...
)
CREATE TRIGGER test_insert BEFORE INSERT OR UPDATE OF id ON test
FOR EACH ROW WHEN (pg_trigger_depth() < 1) EXECUTE PROCEDURE ids_insert();
CREATE TRIGGER test_delete AFTER DELETE ON test
FOR EACH ROW EXECUTE PROCEDURE ids_delete();

PostgreSQL log trigger optimalization

I spent a lot of time trying to optimize our pgsql log trigger which started to be a problem. I did huge progress (from 18min to 2.5min by inserting 3M rows) but I would like to know if some pgSql masters will be able to do it even better.
CREATE OR REPLACE FUNCTION table_log_trig()
RETURNS trigger AS
$BODY$
DECLARE
col TEXT; -- Single column name to save
newVal TEXT; -- New value for column
oldVal TEXT; -- Old value for column
colLimit TEXT[]; -- Columns that should be logged
BEGIN
IF TG_ARGV[0] IS NOT NULL THEN
-- Trigger specifies columns to log
SELECT array_agg(unnest)
FROM unnest(string_to_array(TG_ARGV[0], ','))
INTO colLimit;
ELSE
-- Trigger with no params. Log all columns
SELECT array_agg(json_object_keys)
FROM json_object_keys(row_to_json(NEW))
WHERE json_object_keys NOT IN ('id', 'created_at', 'updated_at') -- Exceptions
INTO colLimit;
END IF;
-- Loop over columns that should be saved in log
FOREACH col IN ARRAY colLimit
LOOP
-- INSERT & UPDATE
EXECUTE 'SELECT ($1).' || col || '::text' INTO newVal USING NEW;
-- UPDATE
IF TG_OP = 'UPDATE' THEN
EXECUTE 'SELECT ($1).' || col || '::text' INTO oldVal USING OLD;
END iF;
-- Add only new or changed data
IF
newVal != oldVal OR
(oldVal IS NULL AND newVal IS NOT NULL) OR
(oldVal IS NOT NULL AND newVal IS NULL)
THEN
INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action)
VALUES (NEW.id, col, oldVal, newVal, NOW(), 999, 'O');
END IF;
END LOOP;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
row_to_json() returns both column names and values; you may as well make use of these values, rather than extracting them later via dynamic SQL.
I haven't thoroughly tested this, let alone benchmarked it, but here's the gist of it:
CREATE OR REPLACE FUNCTION table_log_trig() RETURNS trigger AS
$$
DECLARE
OldJson JSONB = NULL;
BEGIN
IF TG_OP <> 'INSERT' THEN
OldJson := to_jsonb(old);
END IF;
INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action)
SELECT new.id, key, OldValues.value, NewValues.value, now(), 999, 'O'
FROM jsonb_each(to_jsonb(new)) NewValues
LEFT JOIN jsonb_each(OldJson) OldValues USING (key)
WHERE
(
(TG_ARGV[0] IS NULL AND key NOT IN ('id', 'created_at', 'updated_at')) OR
(TG_ARGV[0] IS NOT NULL AND key = ANY(string_to_array(TG_ARGV[0], ',')))
) AND
OldValues.value::text IS DISTINCT FROM NewValues.value::text;
RETURN NULL;
END
$$
LANGUAGE plpgsql VOLATILE;

PostgreSQL: compare NULL and normal value

I have a trigger in PostgreSQL
CREATE OR REPLACE FUNCTION table_update_func_pk1() RETURNS trigger AS $$
DECLARE
ri RECORD;
old_value TEXT;
new_value TEXT;
BEGIN
FOR ri IN
SELECT column_name FROM information_schema.columns
WHERE
table_schema = quote_ident('public')
AND table_name = quote_ident(TG_TABLE_NAME)
ORDER BY ordinal_position
LOOP
EXECUTE 'SELECT ($1).' || ri.column_name || '::text' INTO new_value USING NEW;
EXECUTE 'SELECT ($1).' || ri.column_name || '::text' INTO old_value USING OLD;
IF new_value <> old_value AND ri.column_name != 'update_by' THEN
INSERT INTO protokoll(datetime, operation, tabelle, field, pk1, old_value, new_value, update_by)
VALUES(now(), TG_OP, TG_TABLE_NAME, ri.column_name, NEW.cfg, old_value, new_value, NEW.update_by);
END IF;
END LOOP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Sometimes if old_value is changed from NULL to normal value (or from normal value to NULL), the condition "new_value <> old_value" is NOT true but unknown. I would like ask, there is a method that I can get true in the case. Thanks.
Use is distinct from:
if new_value IS DISTINCT FROM old_value and ri.column_name <> 'update_by' then
...
end if;