Re: Memory error in user-defined aggregation function - Mailing list pgsql-general

From Adriaan Joubert
Subject Re: Memory error in user-defined aggregation function
Date
Msg-id CAH9735ygiKFqDcX6mzzHD7nHpyzyKGH5ayUiTBcG-Dem3gZAPA@mail.gmail.com
Whole thread Raw
In response to Memory error in user-defined aggregation function  (Adriaan Joubert <adriaan.joubert@gmail.com>)
List pgsql-general
Hi,

Finally got this running under the debugger and figured out what is
going on. I had been under the impression that

         if (PG_ARGISNULL(0))
                 PG_RETURN_NULL();

         state = (quartile_state *) PG_GETARG_POINTER(0);

would ensure that state was never a null pointer. However this is not
the case, and an additional check for state==0x0 solved the problem.
Somewhat unexpected, I have to say.

I would still be interested in any ways in which this implementation
could be improved. It would be good if there were some model
implementations for this type of thing - without orafce to guide me I
would have had a hard time figuring any of this out from the docs. I'd
gladly make the quartile implementation available for this purpose if
there is interest.

Adriaan


On 7 August 2012 15:04, Adriaan Joubert <adriaan.joubert@gmail.com> wrote:
> Hi,
>
> I've implemented an aggregation function to compute quartiles in C
> borrowing liberally from orafce code. I uses this code in a windowing
> context and it worked fine until today - and I'm not sure what
> changed. This is on 9.1.2 and I have also tried it on 9.1.4.
>
> What I have determined so far (by sprinkling a lot of elog's
> throughout the code) is that it does not seem to be data specific,
> although it seems to depend on the number of aggregations I do (up to
> about 1250 seems to be fine, beyond that it chokes). I also
> established that there does not seem to be a problem with the transfer
> function, and the data is accumulated without any issues. The error I
> see is in the call to first_quartile_final (listed below). The pointer
> to the transfer data structure is not null, but accessing the field
> mstate->nelems causes a segflt. So the transfer data structure pointer
> is bogus.
>
> I've recompiled postgres with debugging enabled and have connected to
> the backend with gdb, but haven't had any joy in persuading gdb to
> actually stop in the correct file so that I can step through. I'll
> keep on trying to make some headway with that.
>
> In the meantime I would appreciate any comments as to whether the
> approach taken is the right one, and whether additional checks can be
> inserted to avoid this segmentation faults.
>
> Many thanks,
>
> Adriaan
>
>
> My transfer data structure is
>
> typedef struct
> {
>   int len; /* allocated length */
>   int nextlen; /* next allocated length */
>   int nelems; /* number of valid entries */
>   float8  *values;
> } quartile_state;
>
> On the first call to the aggregate function this data structure is
> allocated as follows:
>
> static quartile_state *
> quartile_accummulate(quartile_state *mstate, float8 value,
> MemoryContext aggcontext)
> {
>         MemoryContext oldcontext;
>
>         if (mstate == NULL)
>         {
>                 /* First call - initialize */
>                 oldcontext = MemoryContextSwitchTo(aggcontext);
>                 mstate = palloc(sizeof(quartile_state));
>                 mstate->len = 512;
>                 mstate->nextlen = 2 * 512;
>                 mstate->nelems = 0;
>                 mstate->values = palloc(mstate->len * sizeof(float8));
>                 MemoryContextSwitchTo(oldcontext);
>         }
>         else
>         {
>                 if (mstate->nelems >= mstate->len)
>                 {
>                         int newlen = mstate->nextlen;
>
>                         oldcontext = MemoryContextSwitchTo(aggcontext);
>                         mstate->nextlen += mstate->len;
>                         mstate->len = newlen;
>                         mstate->values = repalloc(mstate->values, mstate->len * sizeof(float8));
>                         MemoryContextSwitchTo(oldcontext);
>                 }
>         }
>
>         mstate->values[mstate->nelems++] = value;
>
>         return mstate;
> }
>
>
> And the transfer function itself is
>
> PG_FUNCTION_INFO_V1(quartile_transfer);
> Datum
> quartile_transfer(PG_FUNCTION_ARGS) {
>         MemoryContext   aggcontext;
>         quartile_state *state = NULL;
>         float8 elem;
>
>         if (!AggCheckCallContext(fcinfo, &aggcontext))
>         {
>                 elog(ERROR, "quartile_transform called in non-aggregate context");
>         }
>
>         state = PG_ARGISNULL(0) ? NULL : (quartile_state *) PG_GETARG_POINTER(0);
>         if (PG_ARGISNULL(1))
>                 PG_RETURN_POINTER(state);
>
>         elem = PG_GETARG_FLOAT8(1);
>
>         state = quartile_accummulate(state, elem, aggcontext);
>
>         PG_RETURN_POINTER(state);
> }
>
> The final function for the computation of the first quartile is
>
> PG_FUNCTION_INFO_V1(first_quartile_final);
> Datum
> first_quartile_final(PG_FUNCTION_ARGS) {
>         quartile_state *state = NULL;
>         float8 result;
>
>         if (PG_ARGISNULL(0))
>                 PG_RETURN_NULL();
>
>         state = (quartile_state *) PG_GETARG_POINTER(0);
>
>         /** HERE state->nelems causes a segflt */
>         if (state->nelems<4)
>                 PG_RETURN_NULL();
>
>         result = quartile_result(state, 0.25);
>
>         PG_RETURN_FLOAT8(result);
> }

pgsql-general by date:

Previous
From: Tom Lane
Date:
Subject: Re: Memory error in user-defined aggregation function
Next
From: Aram Fingal
Date:
Subject: Interval to months