Creating a PostgreSQL procedural language - Part 5 - Returning Results

March 15, 2020
Creating a PostgreSQL procedural language - Part 5 - Returning Results

This example will just be focusing on returning data from user defined functions, specifically returning a value as opposed to using OUT parameters.  The full code is on Github.

To quickly review, PL/Julia calls jl_eval_string() to execute Julia code and captures the returning jl_value_t data structure, which contains the result of the Julia code executed.  The result needs to be extracted from the jl_value_t data structure and then handled before returning the value to the calling SQL statement.

First we will create a helper function to convert a C string into the data type specified by the RETURN clause of the CREATE FUNCTION statement.

static Datum
cstring_to_type(char * input, Oid typeoid)
{
    HeapTuple typetuple;
    Form_pg_type pg_type_entry;
    Datum ret;

    typetuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0);
    pg_type_entry = (Form_pg_type) GETSTRUCT(typetuple);
    ret = OidFunctionCall3(pg_type_entry->typinput,
            CStringGetDatum(input), 0, -1);
    ReleaseSysCache(typetuple);
    PG_RETURN_DATUM(ret);
}

Let’s modify the user defined function test_julia() from before to now return a FLOAT4 value:

CREATE OR REPLACE FUNCTION test_julia(val INTEGER)
RETURNS FLOAT4 AS $$
    sqrt(val);
$$ LANGUAGE pljulia;

Now we will add some new code after calling jl_eval_string(). The results of the Julia code is captured and the jl_value_t structure needs to be examined to determine what kind of data is in it.  In this example, I only demonstrate how to check if the return type is floating point number.  If the returned data is verified to be a floating point (jl_float64_type), then convert it to a C string called buffer using a snprintf() call.

jl_value_t *ret;
ret = jl_eval_string(compiled_code);
if (jl_typeis(ret, jl_float64_type))
{
    double ret_unboxed = jl_unbox_float64(ret);

    buffer = (char *) palloc0((DOUBLE_LEN + 1) * sizeof(char));
    snprintf(buffer, DOUBLE_LEN, "%f", ret_unboxed);
}

We can now call the cstring_to_type() function with the results of jl_eval_string() copied into buffer and finally return from the user defined function.

PG_RETURN_DATUM(cstring_to_type(buffer, procedure_struct->prorettype));

Try executing the latest test_julia() user defined function again:

julia=# select test_julia(8);

test_julia
------------
2.828427
(1 row)

This is where I’ll stop blogging about PL/Julia development for the time being.  There are still a number of things left to do to make PL/Julia more robust and fully functional.  You may have also noticed various WARNING messages if you’ve been trying the code.  Please take a look at the issues list and feel free to try lending a hand.

Share this