0

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!

2 Answers 2

1

To create an aggregate function, you have to use CREATE AGGREGATE. You create that in SQL. What you may need to implement in C is the state transition function (SFUNC), perhaps also others, depending on the kind of aggregate you want to create.

The aggregate function does not have to read from the table; the PostgreSQL executor will feed it the data it needs.

If you start writing C functions, you should perhaps start with something simpler than aggregate functions.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the comment. I made some progress and need to go further. Any idea about my updated problem?
I guess solution is in the final function in the aggregation functions.
You only need a "final function" if the intermediate state is different from the result.
Thank you for your help. Reading what you wrote is really helpful. I found a way now. I will post my last notes. I hope we meet in a conference in Europe.
1

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.