skip to Main Content

Creatinf icase function using

CREATE OR REPLACE FUNCTION public.icase(
cond1 boolean,  res1 anyelement,
cond2 boolean,  res2 anyelement,
conddefault anyelement)    
RETURNS anyelement    LANGUAGE 'sql' IMMUTABLE PARALLEL UNSAFE AS $BODY$ 
SELECT CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END; 
$BODY$;

select 
 icase( false, 0, false, 0.,0.)

throws error

function icase(boolean, integer, boolean, numeric, numeric) does not exist

How to allow icase to accept mixed integer/numeric parameters?

2

Answers


  1. Per the link:

    https://www.postgresql.org/docs/current/xfunc-sql.html#XFUNC-SQL-POLYMORPHIC-FUNCTIONS

    With make_array declared as above, you must provide two arguments that are of exactly the same data type; the system will not attempt to resolve any type differences.

    An alternative approach is to use the “common” family of polymorphic types, which allows the system to try to identify a suitable common type.

    So:

    CREATE OR REPLACE FUNCTION public.icase(cond1 boolean, res1 anycompatible, cond2 boolean, res2 anycompatible, conddefault anycompatible)
     RETURNS anycompatible
     LANGUAGE sql
     IMMUTABLE
    AS $function$ 
    SELECT CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END; 
    $function$
    ;
    

    Which when run does:

    select icase(false, 0, false, 0.,0.);                                                                               
     
    icase 
    -------
         0
    
    
    select icase(false, 0, false, 0.,0.0);
     
    icase 
    -------
       0.0
    
    

    Though as you see it will coerce the output to a common suitable type.

    Login or Signup to reply.
  2. How to allow mixed integer and numeric parameters

    Polymorphic types lead to a common, uniform data type. Anyelement will act as a placeholder for 4x the same type, and if you pass 3 different things (4th is the result type which will be adjusted to the 3 params), it’ll just give up.

    Anycompatible won’t give up but rather attempt to convert the 3 different things to one common type, if it’s at all possible, and only if there’s only one unique way to convert them all. It will let you invoke the routine using a mix of types, but they’ll all end up converted.

    Overload your function instead if you want more control (and plan to stay on v12):
    demo at db<>fiddle

    CREATE OR REPLACE FUNCTION public.icase(--inn
       bool,int,bool,numeric,numeric)    
    RETURNS numeric IMMUTABLE AS $body$
    SELECT CASE WHEN $1 THEN $2 
                WHEN $3 THEN $4 
                ELSE $5 
           END 
    $body$ LANGUAGE sql;
    
    CREATE OR REPLACE FUNCTION public.icase(--nin
       bool,numeric,bool,int,numeric)    
    RETURNS numeric IMMUTABLE AS $body$
    SELECT CASE WHEN $1 THEN $2 
                WHEN $3 THEN $4 
                ELSE $5 
           END 
    $body$ LANGUAGE sql;
    
    CREATE OR REPLACE FUNCTION public.icase(--iin
       bool,int,bool,int,numeric)    
    RETURNS numeric IMMUTABLE AS $body$
    SELECT CASE WHEN $1 THEN $2 
                WHEN $3 THEN $4 
                ELSE $5 
           END 
    $body$ LANGUAGE sql;
    
    CREATE OR REPLACE FUNCTION public.icase(--nnn
       bool,numeric,bool,numeric,numeric)    
    RETURNS numeric IMMUTABLE AS $body$
    SELECT CASE WHEN $1 THEN $2 
                WHEN $3 THEN $4 
                ELSE $5 
           END 
    $body$ LANGUAGE sql;
    
    CREATE OR REPLACE FUNCTION public.icase(--iii
       bool,int,bool,int,int)    
    RETURNS int IMMUTABLE AS $body$
    SELECT CASE WHEN $1 THEN $2 
                WHEN $3 THEN $4 
                ELSE $5 
           END 
    $body$ LANGUAGE sql;
    

    With the 5 definitions above, any of the 8 possible combinations of int and numeric on any of the 3 positions gets resolved fine:

    $1 $2 $3 $4 $5 result pg_typeof
    0::bool 0.2::numeric 0::bool 0.4::numeric 0.5::numeric 0.5 numeric
    0::bool 0.2::numeric 0::bool 0.4::numeric 5::int 5 numeric
    0::bool 0.2::numeric 0::bool 4::int 0.5::numeric 0.5 numeric
    0::bool 0.2::numeric 0::bool 4::int 5::int 5 numeric
    0::bool 0.2::numeric 1::bool 0.4::numeric 0.5::numeric 0.4 numeric
    0::bool 0.2::numeric 1::bool 0.4::numeric 5::int 0.4 numeric
    0::bool 0.2::numeric 1::bool 4::int 0.5::numeric 4 numeric
    0::bool 0.2::numeric 1::bool 4::int 5::int 4 numeric
    0::bool 2::int 0::bool 0.4::numeric 0.5::numeric 0.5 numeric
    0::bool 2::int 0::bool 0.4::numeric 5::int 5 numeric
    0::bool 2::int 0::bool 4::int 0.5::numeric 0.5 numeric
    0::bool 2::int 0::bool 4::int 0.5::numeric 5 integer
    0::bool 2::int 1::bool 0.4::numeric 0.5::numeric 0.4 numeric
    0::bool 2::int 1::bool 0.4::numeric 5::int 0.4 numeric
    0::bool 2::int 1::bool 4::int 0.5::numeric 4 integer
    0::bool 2::int 1::bool 4::int 0.5::numeric 4 numeric
    1::bool 0.2::numeric 0::bool 0.4::numeric 0.5::numeric 0.2 numeric
    1::bool 0.2::numeric 0::bool 0.4::numeric 5::int 0.2 numeric
    1::bool 0.2::numeric 0::bool 4::int 0.5::numeric 0.2 numeric
    1::bool 0.2::numeric 0::bool 4::int 5::int 0.2 numeric
    1::bool 0.2::numeric 1::bool 0.4::numeric 0.5::numeric 0.2 numeric
    1::bool 0.2::numeric 1::bool 0.4::numeric 5::int 0.2 numeric
    1::bool 0.2::numeric 1::bool 4::int 0.5::numeric 0.2 numeric
    1::bool 0.2::numeric 1::bool 4::int 5::int 0.2 numeric
    1::bool 2::int 0::bool 0.4::numeric 0.5::numeric 2 numeric
    1::bool 2::int 0::bool 0.4::numeric 5::int 2 numeric
    1::bool 2::int 0::bool 4::int 0.5::numeric 2 integer
    1::bool 2::int 0::bool 4::int 0.5::numeric 2 numeric
    1::bool 2::int 1::bool 0.4::numeric 0.5::numeric 2 numeric
    1::bool 2::int 1::bool 0.4::numeric 5::int 2 numeric
    1::bool 2::int 1::bool 4::int 0.5::numeric 2 integer
    1::bool 2::int 1::bool 4::int 0.5::numeric 2 numeric

    If you wanted to mix parameter types between different calls, not within a single invocation, your definition would already work as long as you used explicit, uniform type casts:
    demo at db<>fiddle

    select icase(false, 0::numeric,false, 0.::numeric,0.::numeric);
    select icase(false, 0::float4, false, 0.::float4, 0.::float4);
    select icase(false, 0::float8, false, 0.::float8, 0.::float8);
    select icase(false, 0::int2,   false, 0.::int2,   0.::int2);
    select icase(false, 0::int4,   false, 0.::int4,   0.::int4);
    select icase(false, 0::int8,   false, 0.::int8,   0.::int8);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search