NEW is just a record that you can automagically convert to a jsonb with to_jsonb(NEW) and get the exact syntax and behaviour you want:
demo at db<>fiddle
create table test(
a int generated by default as identity primary key
,b boolean,c text,d "char",e "char",f "char");
create table debug_(
payload jsonb
,comment text
,is_valid_as_date boolean
,id int generated by default as identity primary key
,ts timestamp default clock_timestamp());
select setseed(.46);
create function f_test_trigger()returns trigger as $f$
declare new_jsonb jsonb:=to_jsonb(new);
begin
insert into debug_ values
(new_jsonb['c'], 'just one of the columns')
,(new_jsonb['x'], 'this column does not exist')
,(new_jsonb[chr(97+(random()*25)::int)], '1st random lowercase letter key')
,(new_jsonb[chr(97+(random()*25)::int)], '2nd random lowercase letter key');
update debug_
set is_valid_as_date = pg_input_is_valid( payload#>>'{}'--as unquoted text
,'date');
return new;
end $f$ language plpgsql;
create trigger test_trigger after insert on test
for each row execute function f_test_trigger();
insert into test(b,c,d,e,f)values(true,'2024-10-01','d','e','f');
table debug_;
| payload |
comment |
is_valid_as_date |
id |
ts |
| "2024-10-01" |
just one of the columns |
TRUE |
1 |
2025-01-15 18:26:13.079506 |
| null |
this column does not exist |
null |
2 |
2025-01-15 18:26:13.079812 |
| null |
1st random lowercase letter key |
null |
3 |
2025-01-15 18:26:13.079839 |
| "d" |
2nd random lowercase letter key |
false |
4 |
2025-01-15 18:26:13.079847 |
As long as the text value you're dealing with is acceptable as an input for your given target type, your cast() will work. Recycling an answer from a related thread: in PostgreSQL 16 and above, there's pg_input_is_valid() that lets you make sure before you attempt to cast().
pg_input_is_valid ( string text, type text ) → boolean
Tests whether the given string is valid input for the specified data type, returning true or false.
This function will only work as desired if the data type's input function has been updated to report invalid input as a “soft” error. Otherwise, invalid input will abort the transaction, just as if the string had been cast to the type directly.
pg_input_is_valid('42', 'integer') → t
pg_input_is_valid('42000000000', 'integer') → f
pg_input_is_valid('1234.567', 'numeric(7,4)') → f
In PostgreSQL 15 and earlier, you can build your own:
create or replace function is_interpretable_as(arg text, arg_type text)
returns boolean language plpgsql as $$
begin
execute format('select cast(%L as %s)', arg, arg_type);
return true;
exception when others then
return false;
end $$;
plpgsqlno. You would need to use something likeplpython3u