Iterátor pole

Z PostgreSQL
Verze z 7. 3. 2012, 22:07, kterou vytvořil imported>Pavel
(rozdíl) ← Starší verze | zobrazit aktuální verzi (rozdíl) | Novější verze → (rozdíl)
Skočit na navigaci Skočit na vyhledávání

Ukázka tabulkové funkce typu SFRM_Materialize

Funkce vrací celočíselnou řadu odpovídající indexům zadaného pole:

Datum array_subscripts(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(array_subscripts);

Datum
array_subscripts(PG_FUNCTION_ARGS)
{
        ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
        ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
        TupleDesc       tupdesc;
        Tuplestorestate *tupstore;
        MemoryContext per_query_ctx;
        MemoryContext oldcontext;
        int reqdim;
        int     *lb, *dimv;
        int     lower, upper;
        bool reverse;
        int i;

       /* check to see if caller supports us returning a tuplestore */
        if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("set-valued function called in context that cannot accept a set")));
        if (!(rsinfo->allowedModes & SFRM_Materialize))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("materialize mode required, but it is not " \
                                                "allowed in this context")));

        reqdim = (PG_NARGS() < 2) ? 1 : PG_GETARG_INT32(1);
        reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);

        /* Sanity check: does it look like an array at all? */
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
                 PG_RETURN_NULL();

        /* Sanity check: was the requested dim valid */
        if (reqdim <= 0 || reqdim > ARR_NDIM(v))
                 PG_RETURN_NULL();

        lb = ARR_LBOUND(v);
        dimv = ARR_DIMS(v);

        lower = lb[reqdim - 1];
        upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;

        /* need to build tuplestore in query context */
        per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
        oldcontext = MemoryContextSwitchTo(per_query_ctx);


        /* note: for 8.4 and higher - tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); */
        tupdesc = rsinfo->expectedDesc;
        tupstore = tuplestore_begin_heap(true, false, work_mem);

        for (i = lower; i <= upper; i++)
        {
                Datum        value;
                bool         null = false;
                HeapTuple       tuple;

                /* generate junk in short-term context */
                MemoryContextSwitchTo(oldcontext);

                value = !reverse ? Int32GetDatum(i) : Int32GetDatum(upper - i + 1);
                tuple = heap_form_tuple(tupdesc, &value, &null);

                MemoryContextSwitchTo(per_query_ctx);
                tuplestore_puttuple(tupstore, tuple);
        }

        /* clean up and return the tuplestore */
        tuplestore_donestoring(tupstore);

        MemoryContextSwitchTo(oldcontext);

        rsinfo->returnMode = SFRM_Materialize;
        rsinfo->setResult = tupstore;
        rsinfo->setDesc = tupdesc;

        return (Datum) 0;
}

a její obdoba typu SRFM_by_call (sice o zhruba 10% pomalejší zato paměťově šetrnější (má smysl řešit od 10 000 řádků):

#include "funcapi.h"

typedef struct generate_iterator_fctx
{
        int4    lower;
        int4    upper;
        bool    reverse;
} generate_iterator_fctx;

Datum array_subscripts(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(array_subscripts);

/* 
 * array_subscripts(array anyarray, dim int, reverse bool)
 */
Datum
array_subscripts(PG_FUNCTION_ARGS)
{
        FuncCallContext *funcctx;
        MemoryContext oldcontext;
        generate_iterator_fctx *fctx;

        /* stuff done only on the first call of the function */
        if (SRF_IS_FIRSTCALL())
        {
                ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
                int reqdim;
                int     *lb, *dimv;

                /* create a function context for cross-call persistence */
                funcctx = SRF_FIRSTCALL_INIT();

                reqdim = (PG_NARGS() < 2) ? 1 : PG_GETARG_INT32(1);

                /* Sanity check: does it look like an array at all? */
                if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
                        SRF_RETURN_DONE(funcctx);

                /* Sanity check: was the requested dim valid */
                if (reqdim <= 0 || reqdim > ARR_NDIM(v))
                        SRF_RETURN_DONE(funcctx);

                /*
                 * switch to memory context appropriate for multiple function calls
                 */
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
                fctx = (generate_iterator_fctx *) palloc(sizeof(generate_iterator_fctx));

                lb = ARR_LBOUND(v);
                dimv = ARR_DIMS(v);

                fctx->lower = lb[reqdim - 1];
                fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
                fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);

                funcctx->user_fctx = fctx;

                MemoryContextSwitchTo(oldcontext);
        }

        funcctx = SRF_PERCALL_SETUP();

        fctx = funcctx->user_fctx;

        if (fctx->lower <= fctx->upper)
        {
                if (!fctx->reverse)
                        SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
                else
                        SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
        }
        else
                /* do when there is no more left */
                SRF_RETURN_DONE(funcctx);
}

Registrace:

CREATE FUNCTION array_subscripts(anyarray)
RETURNS SETOF int
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

CREATE FUNCTION array_subscripts(anyarray, integer)
RETURNS SETOF int
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

CREATE FUNCTION array_subscripts(anyarray, integer, bool)
RETURNS SETOF int
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Použití:

CREATE FUNCTION array_expand(anyarray)
RETURNS SETOF anyelements
AS
$$
  SELECT $1[i] 
     FROM array_subscripts($1) g(i);
$$ LANGUAGE SQL;

CREATE FUNCTION array_expand2(anyarray)
RETURNS SETOF anyelements
AS
$$
  SELECT $1[i][j] 
     FROM array_subscripts($1,1) d1(i),
          array_subscripts($1,2) d2{j};
$$ LANGUAGE SQL;

postgres=# select array_expand2(ARRAY[[10,11,12],[13,14,15]]);
 array_expand2 
---------------
            10
            11
            12
            13
            14
            15
(6 rows)