cpostgresqlpostgresql-extensions

PostgreSQL C function to get values


I am trying to write a PostgreSQL function in C.

My goal is finding minimum value of a list. So, my function will be executed like these:

SELECT min_to_max(val) FROM (VALUES(1),(2),(3)) x(val);
SELECT min_to_max(val) FROM my_table;

Here is my C code and I lost here. For example, there is a function called "PG_GETARG_INT32" to get integer values, but I don' t know how to get values from a table in order to process. Any idea?

#include "postgres.h"
#include "fmgr.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(get_sum);

Datum
get_sum(PG_FUNCTION_ARGS)
{   
    ArrayType  *v1,
    bool isnull;

    isnull = PG_ARGISNULL(0);
    if (isnull)
      ereport( ERROR,
               ( errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
               errmsg("The input cannot be empty")));
    
    List a = PG_GETARR_SOMEFUNCTION_2_GET_LIST(0);

    # for loop iteration to find min_val

    # return min_val
}

Edited(2022.05.13 - below is edited part):

Thanks to @Laurenz Albe. I made some progress.

Yet, now I want to go further(No needs to be in C language. As Laurenz Albe stated, I am just taking small steps).

My functions and aggregates like below to find min and max:

CREATE or replace FUNCTION find_min_func(
    state integer,
    next  integer
) RETURNS integer
LANGUAGE plpgsql
STRICT
AS $$
declare
min_val integer;                      
begin                                 
if $1 <= $2 then min_val := $1;       
elsif $2 <$1 then min_val := $2;      
end if;                               
return min_val;                       
END;
$$;

CREATE or replace AGGREGATE find_min(integer)
(
    SFUNC    = find_min_func,
    STYPE    = integer
);

CREATE or replace FUNCTION find_max_func(
    state integer,
    next  integer
) RETURNS integer
LANGUAGE plpgsql
STRICT
AS $$
declare
max_val integer;                      
begin                                 
if $1 >= $2 then max_val := $1;       
elsif $2 > $1 then max_val := $2;      
end if;                               
return max_val;                       
END;
$$;

CREATE or replace AGGREGATE find_max(integer)
(
    SFUNC    = find_max_func, -- State function
    STYPE    = integer       -- State type
);

They are working great but now I want to do something like

SELECT min_to_max(val) FROM (VALUES(1),(2),(3)) x(val);

Expected output: 1 -> 3

So, I just wrote a state function and aggregate pair like below(I know it is wrong):

CREATE or replace FUNCTION find_min_and_max_func(
    state integer,
    next  integer
) RETURNS varchar
LANGUAGE plpgsql
STRICT
AS $$
declare
min_val integer;  
max_val integer;   
output varchar;                   
begin    
if $1 <= $2 then min_val := $1;   max_val := $2;      
elsif $2 <$1 then min_val := $2;   max_val := $1;
end if;
output = cast(min_val as varchar) || '->' ||     cast(max_val as varchar)       ;             
return output;                       
END;
$$;

CREATE or replace AGGREGATE find_min_and_max(integer)
(
    SFUNC    = find_min_and_max_func, -- State function
    STYPE    = varchar       -- State type
);

It is wrong because state function is taking arguments as integer but returns(?) varchar, so it varying.

How can I arrange my state function here?

Thanks!


Solution

  • With the help of @Laurenz Albe(thanks to him again), I found the solution. Also, I checked out:

    1. https://hoverbear.org/blog/postgresql-aggregates-with-rust/
    2. https://hashrocket.com/blog/posts/custom-aggregates-in-postgresql

    Here is my solution:

    CREATE or replace FUNCTION find_min_and_max_func(
        state point,
        next  integer
    ) RETURNS point
    LANGUAGE plpgsql
    STRICT
    AS $$
    declare
    min_val integer;  
    max_val integer;                   
    begin    
    if state[0] <= next then min_val := state[0];  
    elsif next < state[0] then min_val := next;  
    end if;
    if state[1] >= next then max_val := state[1];  
    elsif next > state[1] then max_val := next;  
    end if;               
    return point(min_val, max_val) ;                     
    END;
    $$;
    
    CREATE or replace FUNCTION find_min_and_max_final_func(
        state point
    ) RETURNS varchar
    LANGUAGE plpgsql
    STRICT
    AS $$                 
    begin              
    return cast(state[0] as varchar) || '->' || cast(state[1] as varchar) ;                     
    END;
    $$;
    
    
    CREATE or replace AGGREGATE find_min_and_max(integer)
    (
        SFUNC    = find_min_and_max_func, -- State function
        STYPE    = point,       -- State type
        FINALFUNC = find_min_and_max_final_func,
        initcond = '(1231231232131,0)'
    );
    
    SELECT find_min_and_max(value) FROM UNNEST(ARRAY [1, 2, 3]) as value;
    
     find_min_and_max
    ------------------
     1->6
    (1 row)
    

    Thanks!