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;
Related
I am writing 1 PostgreSQL function for some operation.
Writing SQL migration for that function but facing formatting error as liquibase is not able to recognize some portion.
Function Liquibase Migration:
CREATE OR REPLACE FUNCTION schema.fncn(trId integer, sts integer, stIds character varying)
RETURNS double precision
LANGUAGE plpgsql
AS '
DECLARE
abc integer;
query CHAR(1500);
xyz integer;
BEGIN
query := ''select sum(t.a)
FROM schema.tbl t
where t.id in(1,2)
and t.status ='' || sts ||
'' and t.status <> 2
and t.tr_id ='' || trId ||
'' and t.sw in('''', ''N'')'';
IF stIds is not null then
query := query || '' AND t.st_id IN ('' || stIds || '')'';
ELSE
END IF;
EXECUTE query INTO abc;
SELECT abc INTO xyz;
RETURN xyz;
END;
'
;
Following error it throwing:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "N"
Reason: liquibase.exception.DatabaseException: ERROR: syntax error at or near "N"
Any suggestion what I am missing?
The immediate problem is the nesting of ' of single quotes. To make that easier, use dollar quoting for the function body. You can nest dollar quoted string by choosing different delimiters.
To avoid any problems with concatenation of parameters, use parameter place holders in the query and pass the values with the USING clause. That will however require two different execute calls.
I assume stIds is a comma separated string of values. To use that as a (single) placeholder, convert it to an array using string_to_array() - or even better: change the type of the input parameter to text[] and pass an array directly.
The query variable is better defined as text, don't use char. There is also no need to copy the result of the query into a different variable (which by the way would be more efficient using xyz := abc; rather than a select into)
CREATE OR REPLACE FUNCTION schema.fncn(trId integer, sts integer, stIds character varying)
RETURNS double precision
LANGUAGE plpgsql
AS
$body$
DECLARE
abc integer;
query text;
BEGIN
query := $q$ select sum(t.a)
FROM schema.tbl t
where t.id in (1,2)
and t.status = $1
and t.status <> 2
and t.tr_id = $2
and t.sw in ('''', 'N') $q$;
IF stIds is not null then
query := query || $sql$ AND t.st_id = ANY (string_to_array($4, ',') $sql$;
EXECUTE query INTO abc
using trid, sts, stids;
ELSE
EXECUTE query INTO abc
using trid, sts;
END IF;
RETURN abc;
END;
$body$
;
Note that in the Liquibase change, you must use splitStatements=false in order to run this without errors.
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)
I want to pass entire where condition as parameter in Postgrel.
Is it possible? I have created procedure like,
CREATE OR REPLACE FUNCTION public.pro_select_all_item_query_builder_data(IN cond character varying)
RETURNS TABLE(id integer, name character varying) AS
$BODY$
BEGIN
FOR id, name IN
SELECT product.id, product.name FROM product WHERE cond
.............
and call it like,
SELECT *
FROM pro_select_all_item_query_builder_data('product.status_id = 1')
It showing ERROR: argument of WHERE must be type boolean, not type character varying
Can you please support me to solve this issue?
Make sure cond has a valid condition, otherwise it will throw syntax error.
FOR id, name IN
EXECUTE ('SELECT product.id, product.name FROM product WHERE ' || cond)
LOOP
....
END LOOP;
I have a table with field "mac" of type MACADDR. Now I would like to treat a situation (probably with trigger?) when somebody inserts empty string instead of mac address. I would like to turn this empty string to NULL, so postgresql will not complain: invalid input syntax for type macaddr: ""
What I have now in trigger function is this:
IF CHAR_LENGTH(NEW.mac) = 0 THEN
NEW.mac := NULL;
END IF;
But it does not seem to work. What would you do, if you want to treat this on DB level?
Thank you very much. -Jan
PS: I am a postgresql newbie. But a fascinated one :)
You can't do what you want with a trigger. Your incoming empty string will be parsed and converted to a macaddr (or at least the parsing will be attempted) before the trigger is executed. However, you could write a simple function to convert empty strings to NULL and use that in your INSERT:
create function macaddr_ify(m varchar) returns macaddr as $$
begin
if m is null or length(m) = 0 then
return null;
end if;
return cast(m as macaddr);
end;
$$ language plpgsql;
And then:
insert into t (addr) values (macaddr_ify(str));
I'd recommend that your client application properly convert empty MAC address strings to NULLs itself though.
EDIT Taking COST 100 out made the command go through, however, I'm still unable to run my query because it yields this error:
ERROR: function group_concat(character) does not exist
HINT: No function matches the given name and argument types. You may need to add explicit type casts.
The query I'm running is this:
select tpid, group_concat(z) as z,
group_concat(cast(r as char(2))) as r,
group_concat(to_char(datecreated,'DD-Mon-YYYY HH12:MI am')) as datecreated,
group_concat(to_char(datemodified,'DD-Mon-YYYY HH12:MI am')) as datemodified
from tpids group by tpid order by tpid, zip
This function seems to work fine locally, but moving it online yields this error... Is there something I'm missing?
CREATE OR REPLACE FUNCTION group_concat(text, text)
RETURNS text AS
$BODY$
SELECT CASE
WHEN $2 IS NULL THEN $1
WHEN $1 IS NULL THEN $2
ELSE $1 operator(pg_catalog.||) ',' operator(pg_catalog.||) $2
END
$BODY$
LANGUAGE 'sql' IMMUTABLE
COST 100;
ALTER FUNCTION group_concat(text, text) OWNER TO j76dd3;
As the hint message states, you are missing an argument to your stored procedure. It expects 2 arguments, but in your column statments:
group_concat(cast(r as char(2))) as r,
group_concat(to_char(datecreated,'DD-Mon-YYYY HH12:MI am')) as datecreated,
group_concat(to_char(datemodified,'DD-Mon-YYYY HH12:MI am')) as datemodified
you supply only one.
Since PostgreSQL 8.4 default values for arguments are allowed. Take a look at the reference for more information and examples.