Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

tablefunc.h File Reference

#include "fmgr.h"
Include dependency graph for tablefunc.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

Datum normal_rand (PG_FUNCTION_ARGS)
Datum crosstab (PG_FUNCTION_ARGS)
Datum crosstab_hash (PG_FUNCTION_ARGS)
Datum connectby_text (PG_FUNCTION_ARGS)
Datum connectby_text_serial (PG_FUNCTION_ARGS)

Function Documentation

Datum connectby_text ( PG_FUNCTION_ARGS   ) 

Definition at line 986 of file tablefunc.c.

References ReturnSetInfo::allowedModes, connectby(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, IsA, MemoryContextSwitchTo(), NULL, PG_GETARG_INT32, PG_GETARG_TEXT_PP, pstrdup(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, text_to_cstring(), TupleDescGetAttInMetadata(), and validateConnectbyTupleDesc().

{
    char       *relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char       *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1));
    char       *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2));
    char       *start_with = text_to_cstring(PG_GETARG_TEXT_PP(3));
    int         max_depth = PG_GETARG_INT32(4);
    char       *branch_delim = NULL;
    bool        show_branch = false;
    bool        show_serial = false;
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc   tupdesc;
    AttInMetadata *attinmeta;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;

    /* 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) ||
        rsinfo->expectedDesc == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not " \
                        "allowed in this context")));

    if (fcinfo->nargs == 6)
    {
        branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(5));
        show_branch = true;
    }
    else
        /* default is no show, tilde for the delimiter */
        branch_delim = pstrdup("~");

    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /* get the requested return tuple description */
    tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);

    /* does it meet our needs */
    validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);

    /* OK, use it then */
    attinmeta = TupleDescGetAttInMetadata(tupdesc);

    /* OK, go to work */
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = connectby(relname,
                                  key_fld,
                                  parent_key_fld,
                                  NULL,
                                  branch_delim,
                                  start_with,
                                  max_depth,
                                  show_branch,
                                  show_serial,
                                  per_query_ctx,
                              rsinfo->allowedModes & SFRM_Materialize_Random,
                                  attinmeta);
    rsinfo->setDesc = tupdesc;

    MemoryContextSwitchTo(oldcontext);

    /*
     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
     * tuples are in our tuplestore and passed back through rsinfo->setResult.
     * rsinfo->setDesc is set to the tuple description that we actually used
     * to build our tuples with, so the caller can verify we did what it was
     * expecting.
     */
    return (Datum) 0;
}

Datum connectby_text_serial ( PG_FUNCTION_ARGS   ) 

Definition at line 1065 of file tablefunc.c.

References ReturnSetInfo::allowedModes, connectby(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, IsA, MemoryContextSwitchTo(), NULL, PG_GETARG_INT32, PG_GETARG_TEXT_PP, pstrdup(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, text_to_cstring(), TupleDescGetAttInMetadata(), and validateConnectbyTupleDesc().

{
    char       *relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char       *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1));
    char       *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2));
    char       *orderby_fld = text_to_cstring(PG_GETARG_TEXT_PP(3));
    char       *start_with = text_to_cstring(PG_GETARG_TEXT_PP(4));
    int         max_depth = PG_GETARG_INT32(5);
    char       *branch_delim = NULL;
    bool        show_branch = false;
    bool        show_serial = true;
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc   tupdesc;
    AttInMetadata *attinmeta;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;

    /* 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) ||
        rsinfo->expectedDesc == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not " \
                        "allowed in this context")));

    if (fcinfo->nargs == 7)
    {
        branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(6));
        show_branch = true;
    }
    else
        /* default is no show, tilde for the delimiter */
        branch_delim = pstrdup("~");

    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /* get the requested return tuple description */
    tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);

    /* does it meet our needs */
    validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);

    /* OK, use it then */
    attinmeta = TupleDescGetAttInMetadata(tupdesc);

    /* OK, go to work */
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = connectby(relname,
                                  key_fld,
                                  parent_key_fld,
                                  orderby_fld,
                                  branch_delim,
                                  start_with,
                                  max_depth,
                                  show_branch,
                                  show_serial,
                                  per_query_ctx,
                              rsinfo->allowedModes & SFRM_Materialize_Random,
                                  attinmeta);
    rsinfo->setDesc = tupdesc;

    MemoryContextSwitchTo(oldcontext);

    /*
     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
     * tuples are in our tuplestore and passed back through rsinfo->setResult.
     * rsinfo->setDesc is set to the tuple description that we actually used
     * to build our tuples with, so the caller can verify we did what it was
     * expecting.
     */
    return (Datum) 0;
}

Datum crosstab ( PG_FUNCTION_ARGS   ) 

Definition at line 349 of file tablefunc.c.

References ReturnSetInfo::allowedModes, BuildTupleFromCStrings(), compatCrosstabTupleDescs(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, get_call_result_type(), heap_freetuple(), i, IsA, ReturnSetInfo::isDone, MemoryContextSwitchTo(), NULL, palloc0(), pfree(), PG_GETARG_TEXT_PP, PG_RETURN_NULL, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, SPI_connect(), SPI_execute(), SPI_finish(), SPI_getvalue(), SPI_OK_SELECT, SPI_processed, SPI_tuptable, text_to_cstring(), SPITupleTable::tupdesc, TupleDescGetAttInMetadata(), tuplestore_begin_heap(), tuplestore_puttuple(), TYPEFUNC_COMPOSITE, TYPEFUNC_RECORD, values, work_mem, xpfree, xpstrdup, and xstreq.

{
    char       *sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    Tuplestorestate *tupstore;
    TupleDesc   tupdesc;
    int         call_cntr;
    int         max_calls;
    AttInMetadata *attinmeta;
    SPITupleTable *spi_tuptable;
    TupleDesc   spi_tupdesc;
    bool        firstpass;
    char       *lastrowid;
    int         i;
    int         num_categories;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;
    int         ret;
    int         proc;

    /* 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")));

    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;

    /* Connect to SPI manager */
    if ((ret = SPI_connect()) < 0)
        /* internal error */
        elog(ERROR, "crosstab: SPI_connect returned %d", ret);

    /* Retrieve the desired rows */
    ret = SPI_execute(sql, true, 0);
    proc = SPI_processed;

    /* If no qualifying tuples, fall out early */
    if (ret != SPI_OK_SELECT || proc <= 0)
    {
        SPI_finish();
        rsinfo->isDone = ExprEndResult;
        PG_RETURN_NULL();
    }

    spi_tuptable = SPI_tuptable;
    spi_tupdesc = spi_tuptable->tupdesc;

    /*----------
     * The provided SQL query must always return three columns.
     *
     * 1. rowname
     *  the label or identifier for each row in the final result
     * 2. category
     *  the label or identifier for each column in the final result
     * 3. values
     *  the value for each column in the final result
     *----------
     */
    if (spi_tupdesc->natts != 3)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid source data SQL statement"),
                 errdetail("The provided SQL must return 3 "
                           "columns: rowid, category, and values.")));

    /* get a tuple descriptor for our result type */
    switch (get_call_result_type(fcinfo, NULL, &tupdesc))
    {
        case TYPEFUNC_COMPOSITE:
            /* success */
            break;
        case TYPEFUNC_RECORD:
            /* failed to determine actual type of RECORD */
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("function returning record called in context "
                            "that cannot accept type record")));
            break;
        default:
            /* result type isn't composite */
            elog(ERROR, "return type must be a row type");
            break;
    }

    /*
     * Check that return tupdesc is compatible with the data we got from SPI,
     * at least based on number and type of attributes
     */
    if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc))
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("return and sql tuple descriptions are " \
                        "incompatible")));

    /*
     * switch to long-lived memory context
     */
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /* make sure we have a persistent copy of the result tupdesc */
    tupdesc = CreateTupleDescCopy(tupdesc);

    /* initialize our tuplestore in long-lived context */
    tupstore =
        tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
                              false, work_mem);

    MemoryContextSwitchTo(oldcontext);

    /*
     * Generate attribute metadata needed later to produce tuples from raw C
     * strings
     */
    attinmeta = TupleDescGetAttInMetadata(tupdesc);

    /* total number of tuples to be examined */
    max_calls = proc;

    /* the return tuple always must have 1 rowid + num_categories columns */
    num_categories = tupdesc->natts - 1;

    firstpass = true;
    lastrowid = NULL;

    for (call_cntr = 0; call_cntr < max_calls; call_cntr++)
    {
        bool        skip_tuple = false;
        char      **values;

        /* allocate and zero space */
        values = (char **) palloc0((1 + num_categories) * sizeof(char *));

        /*
         * now loop through the sql results and assign each value in sequence
         * to the next category
         */
        for (i = 0; i < num_categories; i++)
        {
            HeapTuple   spi_tuple;
            char       *rowid;

            /* see if we've gone too far already */
            if (call_cntr >= max_calls)
                break;

            /* get the next sql result tuple */
            spi_tuple = spi_tuptable->vals[call_cntr];

            /* get the rowid from the current sql result tuple */
            rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);

            /*
             * If this is the first pass through the values for this rowid,
             * set the first column to rowid
             */
            if (i == 0)
            {
                xpstrdup(values[0], rowid);

                /*
                 * Check to see if the rowid is the same as that of the last
                 * tuple sent -- if so, skip this tuple entirely
                 */
                if (!firstpass && xstreq(lastrowid, rowid))
                {
                    xpfree(rowid);
                    skip_tuple = true;
                    break;
                }
            }

            /*
             * If rowid hasn't changed on us, continue building the output
             * tuple.
             */
            if (xstreq(rowid, values[0]))
            {
                /*
                 * Get the next category item value, which is always attribute
                 * number three.
                 *
                 * Be careful to assign the value to the array index based on
                 * which category we are presently processing.
                 */
                values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);

                /*
                 * increment the counter since we consume a row for each
                 * category, but not for last pass because the outer loop will
                 * do that for us
                 */
                if (i < (num_categories - 1))
                    call_cntr++;
                xpfree(rowid);
            }
            else
            {
                /*
                 * We'll fill in NULLs for the missing values, but we need to
                 * decrement the counter since this sql result row doesn't
                 * belong to the current output tuple.
                 */
                call_cntr--;
                xpfree(rowid);
                break;
            }
        }

        if (!skip_tuple)
        {
            HeapTuple   tuple;

            /* build the tuple and store it */
            tuple = BuildTupleFromCStrings(attinmeta, values);
            tuplestore_puttuple(tupstore, tuple);
            heap_freetuple(tuple);
        }

        /* Remember current rowid */
        xpfree(lastrowid);
        xpstrdup(lastrowid, values[0]);
        firstpass = false;

        /* Clean up */
        for (i = 0; i < num_categories + 1; i++)
            if (values[i] != NULL)
                pfree(values[i]);
        pfree(values);
    }

    /* let the caller know we're sending back a tuplestore */
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;

    /* release SPI related resources (and return to caller's context) */
    SPI_finish();

    return (Datum) 0;
}

Datum crosstab_hash ( PG_FUNCTION_ARGS   ) 

Definition at line 633 of file tablefunc.c.

References ReturnSetInfo::allowedModes, CreateTupleDescCopy(), crosstab_hash(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, get_crosstab_tuplestore(), IsA, load_categories_hash(), MemoryContextSwitchTo(), NULL, PG_GETARG_TEXT_PP, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, and text_to_cstring().

Referenced by crosstab_hash().

{
    char       *sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char       *cats_sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc   tupdesc;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;
    HTAB       *crosstab_hash;

    /* 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) ||
        rsinfo->expectedDesc == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not " \
                        "allowed in this context")));

    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /* get the requested return tuple description */
    tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);

    /*
     * Check to make sure we have a reasonable tuple descriptor
     *
     * Note we will attempt to coerce the values into whatever the return
     * attribute type is and depend on the "in" function to complain if
     * needed.
     */
    if (tupdesc->natts < 2)
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("query-specified return tuple and " \
                        "crosstab function are not compatible")));

    /* load up the categories hash table */
    crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);

    /* let the caller know we're sending back a tuplestore */
    rsinfo->returnMode = SFRM_Materialize;

    /* now go build it */
    rsinfo->setResult = get_crosstab_tuplestore(sql,
                                                crosstab_hash,
                                                tupdesc,
                                                per_query_ctx,
                             rsinfo->allowedModes & SFRM_Materialize_Random);

    /*
     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
     * tuples are in our tuplestore and passed back through rsinfo->setResult.
     * rsinfo->setDesc is set to the tuple description that we actually used
     * to build our tuples with, so the caller can verify we did what it was
     * expecting.
     */
    rsinfo->setDesc = tupdesc;
    MemoryContextSwitchTo(oldcontext);

    return (Datum) 0;
}

Datum normal_rand ( PG_FUNCTION_ARGS   ) 

Definition at line 174 of file tablefunc.c.

References FuncCallContext::call_cntr, normal_rand_fctx::carry_val, Float8GetDatum(), get_normal_pair(), FuncCallContext::max_calls, normal_rand_fctx::mean, MemoryContextSwitchTo(), FuncCallContext::multi_call_memory_ctx, palloc(), PG_GETARG_FLOAT8, PG_GETARG_UINT32, SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, normal_rand_fctx::stddev, normal_rand_fctx::use_carry, and FuncCallContext::user_fctx.

{
    FuncCallContext *funcctx;
    int         call_cntr;
    int         max_calls;
    normal_rand_fctx *fctx;
    float8      mean;
    float8      stddev;
    float8      carry_val;
    bool        use_carry;
    MemoryContext oldcontext;

    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL())
    {
        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();

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

        /* total number of tuples to be returned */
        funcctx->max_calls = PG_GETARG_UINT32(0);

        /* allocate memory for user context */
        fctx = (normal_rand_fctx *) palloc(sizeof(normal_rand_fctx));

        /*
         * Use fctx to keep track of upper and lower bounds from call to call.
         * It will also be used to carry over the spare value we get from the
         * Box-Muller algorithm so that we only actually calculate a new value
         * every other call.
         */
        fctx->mean = PG_GETARG_FLOAT8(1);
        fctx->stddev = PG_GETARG_FLOAT8(2);
        fctx->carry_val = 0;
        fctx->use_carry = false;

        funcctx->user_fctx = fctx;

        MemoryContextSwitchTo(oldcontext);
    }

    /* stuff done on every call of the function */
    funcctx = SRF_PERCALL_SETUP();

    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    fctx = funcctx->user_fctx;
    mean = fctx->mean;
    stddev = fctx->stddev;
    carry_val = fctx->carry_val;
    use_carry = fctx->use_carry;

    if (call_cntr < max_calls)  /* do when there is more left to send */
    {
        float8      result;

        if (use_carry)
        {
            /*
             * reset use_carry and use second value obtained on last pass
             */
            fctx->use_carry = false;
            result = carry_val;
        }
        else
        {
            float8      normval_1;
            float8      normval_2;

            /* Get the next two normal values */
            get_normal_pair(&normval_1, &normval_2);

            /* use the first */
            result = mean + (stddev * normval_1);

            /* and save the second */
            fctx->carry_val = mean + (stddev * normval_2);
            fctx->use_carry = true;
        }

        /* send the result */
        SRF_RETURN_NEXT(funcctx, Float8GetDatum(result));
    }
    else
        /* do when there is no more left */
        SRF_RETURN_DONE(funcctx);
}