Hi,

I've found out that currently in some situations jsonb_to_recordset can lead to
a crash. Minimal example that I've managed to create looks like this:

    CREATE OR REPLACE FUNCTION test(data JSONB)
      RETURNS INTEGER AS $$
    DECLARE
      test_var int;
    BEGIN
      WITH jsonb_values AS (
          SELECT
            (SELECT SUM(value)
             FROM jsonb_to_recordset(element #> '{values}')
             AS q(value INTEGER)) AS value_sum
          FROM jsonb_array_elements(data) AS element
      ) SELECT SUM(value_sum) FROM jsonb_values INTO test_var;
      RETURN test_var;
    END;
    $$ LANGUAGE plpgsql;


And then:

    =# SELECT test('[
        {
            "values": [
                {
                    "value": 1
                },
                {
                    "value": 3
                }
            ]
        },
        {
            "values": [
                {
                    "value": 1
                },
                {
                    "value": 3
                }
            ]
        }
    ]' :: JSONB);
    server closed the connection unexpectedly
            This probably means the server terminated abnormally
            before or while processing the request.
    The connection to the server was lost. Attempting reset: Failed.

After brief investigation it looks like an issue with tupdesc from the function
cache:

    if (!cache)
    {
        fcinfo->flinfo->fn_extra = cache =
            MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(*cache));
    //...

    rsi->setDesc = cache->c.io.composite.tupdesc;

Then after the first call of populate_recordset_worker rsi->setDesc is being
reset since we never increased tdrefcount and the next call will use wrong
cache data. Apparently, it can be fixed by incrementing a tdrefcount (see the
attached patch).
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e358b5a..9250646 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3728,6 +3728,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = cache->c.io.composite.tupdesc;
+	rsi->setDesc->tdrefcount = 1;
 
 	PG_RETURN_NULL();
 }

Reply via email to