PostgreSQL - How to handle invalid values when casting to timestamp in SELECT from jsonb data - postgresql

I am trying to get data from a jsonb column and need to cast an attribute from the payload to timestamp. But the query aborts when the value is null or invalid.
This is my select statement.
Select
(jsonb_path_query(AnchorNode, '$.TestDate')#>> '{}')::timestamp as TestDate
From (
Select
jsonb_path_query_first(payload, '$.node1[*].node2[*]') as AnchorNode
From TestTable
) subq1
The invalid values could be only the Date or null.
How can I change the query to handle this. There are about 7 or 8 date fields where I would need to do this
Thank you

Maybie try to cast text inside a function and return null on exception
create or replace function str_to_timestamp(_date text) returns timestamp
language plpgsql AS
$$
BEGIN
return _date::timestamp;
EXCEPTION WHEN OTHERS THEN
return null::timestamp;
END;
$$;

It was possible to achieve above using CASE statement. It may also be done using COALESCE
Select
case
when jsonb_typeof(AnchorNode -> 'TestDate') is null then null
when jsonb_typeof(AnchorNode -> 'TestDate') = 'string' then (AnchorNode ->> 'TestDate')::timestamp
end TestDateTS,
From (
Select
jsonb_path_query_first(payload, '$.node1[*].node2[*]') as AnchorNode
From TestTable
) subq1
OR
COALESCE((AnchorNode ->> 'TestDate')::timestamp, null) as "TestDate"

Related

Postgresql - Returned type record does not match expected type uuid

I have very simple function that should return few rows including the uuid type as the first column.
Function:
CREATE OR REPLACE FUNCTION public.export_wizard()
RETURNS TABLE(id uuid, my_column text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query select (w.id, w.my_column) from wizard w;
END;
$function$
;
Table (short version):
CREATE TABLE IF NOT EXISTS public.wizard
(
id uuid NOT NULL,
my_column text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT wizard_pkey PRIMARY KEY (id)
)
After calling the function like this: select * from export_wizard();
I got an error:
SQL Error [42804]: ERROR: structure of query does not match function result type
Detail: Returned type record does not match expected type uuid in column 1.
Where: PL/pgSQL function export_wizard() line 3 at RETURN QUERY
Thanks for any advices.
I made terrible mistake by adding (no idea why) brackets in SELECT statement as mentioned by #Adrian Klaver.
Correct: return query select w.id, w.my_column from wizard w;

COALESCE doesn't seem to fire in Postgres 12 SQL stored function

Using Postgres 12.x, I've just run into a weird problem. For some reason, I'm not getting COALESCE to work in a stored SQL function. Meaning, I get NULL instead of my first non-null value.
I suspect that I've missed something obvioius, but can't see it. Can anyone spot what the problem is? I've built a stand-alone example that reproduces the issue.
Thanks
Test Setup
BEGIN;
------------------------------------
-- Define table
------------------------------------
DROP TABLE IF EXISTS lookup_table CASCADE;
CREATE TABLE IF NOT EXISTS lookup_table (
id uuid NOT NULL DEFAULT NULL, -- Values are geneated by an outside system.
name_ citext NOT NULL DEFAULT NULL
);
------------------------------------
-- Seed
------------------------------------
INSERT INTO lookup_table (id, name_)
VALUES ('6ea225f3-9819-49eb-bdb8-f3ae23af6337', 'John'),
('ae2282c6-ca9b-413f-b182-a1ca69fc3c78', 'Alice'),
('7ee1ce1b-4efc-426c-a6f8-5b2375cb357e', 'Magrite'),
('71c3d96a-3ac7-454e-bfff-e5551428c017', 'Arjun');
------------------------------------
-- Define the lookup fnction
------------------------------------
CREATE OR REPLACE FUNCTION lookup_function (id_in uuid)
RETURNS citext AS
$BODY$
-- For some reason, COALESCE doesn't work here, but does outside of a function. Weird.
-- Tried with CTE, same same.
SELECT COALESCE (name_ , '') FROM lookup_table WHERE id = id_in;
$BODY$
LANGUAGE SQL;
------------------------------------
-- Test it out
------------------------------------
SELECT 'Alice' AS description,
lookup_function ('ae2282c6-ca9b-413f-b182-a1ca69fc3c78') AS result
UNION ALL
SELECT 'Should return an empty string, returns NULL instead' AS description,
lookup_function ('00000000-0000-0000-0000-000000000000') AS result
Standard Call
For comparison, here's a standard call right in a SELECT that works fine:
select coalesce (name_, '')
from lookup_table
where id = '00000000-0000-0000-0000-000000000000';
-- Returns an empty string, not NULL.
But, in a SQL function, this expression returns NULL instead.
CTE Attempt
Here's my stab at using a CTE to help out in the function, but I still get back NULL.
------------------------------------
-- Define the lookup fnction
------------------------------------
CREATE OR REPLACE FUNCTION lookup_function (id_in uuid)
RETURNS citext AS
$BODY$
WITH lookup_result AS (
select name_ from lookup_table where id = id_in
)
SELECT CASE WHEN name_ IS NULL THEN ''
ELSE name_ END
FROM lookup_result
$BODY$
LANGUAGE SQL;
The lookup function doesn't return anything from the select statement inside it as the where clause fails. The coalesce is never executed therefore.
A null is returned by default.
You can rewrite the select as:
select coalesce((SELECT name_ FROM lookup_table WHERE id = id_in),'');
The coalesce will turn NULL value for name_ into the empty string. But you don't have a NULL value for that column, you have zero rows. This gets converted to NULL at a later stage, where the coalesce can't get its hands on it.
Maybe you want this:
CREATE OR REPLACE FUNCTION lookup_function (id_in uuid)
RETURNS citext AS
$BODY$
SELECT COALESCE (name_ , '') FROM (values (id_in))f(x) left join lookup_table on id = x;
$BODY$
LANGUAGE SQL;

Returning Table with null value causes error

I have a simple function that has optional parameters. When I leave out a parameter, which should just default to null, I get an error that it is not an integer.
Here is the function:
CREATE FUNCTION rewrite(_postid integer DEFAULT NULL::integer,
_url character varying DEFAULT NULL::character varying)
RETURNS TABLE
(
PostTypeID integer,
DestinationURL varchar,
)
LANGUAGE plpgsql
AS
$function$
BEGIN
RETURN QUERY
SELECT
NULL AS PostTypeID,
_url AS DestinationURL,
FROM reference.destinations dest1
WHERE length(TRIM(dest1.DestinationURL)) > 0
AND _url LIKE '%' || TRIM(dest1.DestinationURL)) || '%'
ORDER BY length(dest1.DestinationURL)) DESC
LIMIT 1;
END;
$function$
If I run SELECT * FROM utility.rewrite(_url := 'wikipedia.org') then I get this error:
[42804] ERROR: structure of query does not match function result type
Detail: Returned type text does not match expected type integer in
column 1.
So column1 must be the PostTypeID column in my RETURNS TABLE definition. But I am selecting NULL AS PostTypeID so why is it not just returning NULL?
If I run SELECT * FROM utility.rewrite(_postid = 0, _url := 'wikipedia.org') then it works fine. But I don't want 0 to be returned, I want NULL.
Just because you use the alias posttypeid in the query does not mean that PostgreSQL infers the data type of your PL/pgSQL variable.
Even though NULL can be any data type, PostgreSQL has to determine a data type for the result column of the query. Lacking other information, it arbitrarily chooses text.
Mapping the query result type to the function result type happens later, in PL/pgSQL. That is what causes the error you observe.
You can avoid the problem by specifying the type of NULL with an explicit type cast:
SELECT CAST (NULL AS integer)

how to convert a varchar field value into an integer field

for example in a table one field is character varying so how to convert that character varying to an Integer type, what exactly is I need to get the return value in Integer datatype
Database : postgresql-9.2
i would suggest to make Functions to do the job i.e
CREATE OR REPLACE FUNCTION chartoint(charparam character varying)
RETURNS integer AS
$BODY$
SELECT CASE WHEN trim($1) ~ '[0-9]+' THEN CAST(trim($1) AS integer) ELSE NULL END;
$BODY$
LANGUAGE sql IMMUTABLE STRICT
You may try like this:
SELECT NULLIF(your_value, '')::int
or extend that like thats
SELECT CAST(coalesce(column_name, '0') AS integer) as Value
from table_name
SELECT mycolumn::integer FROM mytable WHERE something
SELECT CASE WHEN mycolumn = NULL THEN NULL ELSE mycolumn :: Integer END
FROM mytable WHERE something

postgres check if null then cast to numeric

I am trying check if a value is null if so the select null else cast to numeric, but it throws an error. This is actually part of an insert statement
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power ='null' then NULL else cast(:power as numeric) end from abc
error that I get is
Error: ERROR: invalid input syntax for type numeric: "null"
:power is a variable that can be given any value using java code. If I give a value of null it give an error.
In code I get the following error from the java stack trace
org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to numeric
Error:
SELECT CASE WHEN 'null' = 'null' THEN NULL ELSE cast('null' AS numeric) END
No error:
DO $$
DECLARE
power text := 'null';
BEGIN
PERFORM CASE WHEN power = 'null' THEN NULL ELSE cast(power AS numeric) END;
END;
$$
Explanation:
If you build a query string, the expression cast('null' AS numeric) or simply 'null'::numeric always raises an exception, even in an ELSE block that is never executed, because it is invalid input syntax and the exception is raised during the syntax check (like the error message implies), not during execution.
A CASE statement like you display only makes sense with a parameter or variable not with literals. The second instance of the literal has no connection to the first instance whatsoever after the query string has been assembled.
For dynamic SQL like that, you need to check the value before you build the query string. Or you use a function or prepared statement and pass the value as parameter. That would work, too.
More advice after comment:
In your particular case you could check the value in the app and build a query string like this:
INSERT INTO tbl(name, abc_id, created, power)
SELECT 'xyz'
, abc_id
, now()
, <insert_value_of_power_or_NULL_here> -- automatically converted to numeric
FROM abc
You may be interested in a different approach to INSERT data from a file conditionally.
Use COPY for files local to the server or psql's meta-command \copy for files local to the client.
if the field value is null, and you want in this case to map it to some value you can use coalesce(field_name, 'Some value') or coalesce(field_name, 123).
For full documentation see here.
You have to check with the IS operator, and not with the equal when you dealing with NULL :
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power IS null then NULL else cast(:power as numeric) end from abc
INSERT into someTable(name,created,power) SELECT 'xyz',now(),
case :power when 'null' then NULL else :power end::numeric from abc
I was trying to do something similar in order to update/insert some records where a numeric value can be null or not.
You can validate a variable before you send it to the function or inside the function depending the value passed
(For me using a variable is better than use CASE WHEN THEN ELSE END CASE every time you need to validate the value)
So to work with the NULL values using a regular comparison operand in order to find a record to update can be done by turning transform_null_equals to ON
I hope this help someone
CREATE OR REPLACE FUNCTION update_insert_transaction(vcodaccount integer, vcodaccountaux text,
vdescription text, vcodgroup integer)
RETURNS integer AS $$
DECLARE
n integer = 0;
vsql text = 'NULL';
BEGIN
IF vcodaccountaux <> '' THEN
vsql = vcodaccountaux;
END IF;
SET LOCAL transform_null_equals TO ON;
EXECUTE 'UPDATE account_import_conf SET (codaccount, codaccountaux, description, codgroup) =
('||vcodaccount||','||vsql||',trim('||quote_literal(vdescription)||'),'||vcodgroup||')
WHERE codaccount='||vcodaccount||' AND codaccountaux = '||vsql||' RETURNING * ';
GET DIAGNOSTICS n = ROW_COUNT;
IF n = 0 THEN
EXECUTE 'INSERT INTO account_import_conf (codaccount, codaccountaux, description, codgroup)
SELECT '||vcodaccount||','||vsql||' ,trim('||quote_literal(vdescription)||'),'||vcodgroup||';';
END IF;
RETURN n;
END;$$
LANGUAGE plpgsql;