Header And Logo

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

Data Structures | Typedefs | Enumerations | Functions

functions.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "storage/proc.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for functions.c:

Go to the source code of this file.

Data Structures

struct  DR_sqlfunction
struct  execution_state
struct  SQLFunctionCache
struct  SQLFunctionParseInfo

Typedefs

typedef struct execution_state execution_state
typedef SQLFunctionCacheSQLFunctionCachePtr
typedef struct SQLFunctionParseInfo SQLFunctionParseInfo

Enumerations

enum  ExecStatus { F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE }

Functions

static Nodesql_fn_param_ref (ParseState *pstate, ParamRef *pref)
static Nodesql_fn_post_column_ref (ParseState *pstate, ColumnRef *cref, Node *var)
static Nodesql_fn_make_param (SQLFunctionParseInfoPtr pinfo, int paramno, int location)
static Nodesql_fn_resolve_param_name (SQLFunctionParseInfoPtr pinfo, const char *paramname, int location)
static Listinit_execution_state (List *queryTree_list, SQLFunctionCachePtr fcache, bool lazyEvalOK)
static void init_sql_fcache (FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
static void postquel_start (execution_state *es, SQLFunctionCachePtr fcache)
static bool postquel_getnext (execution_state *es, SQLFunctionCachePtr fcache)
static void postquel_end (execution_state *es)
static void postquel_sub_params (SQLFunctionCachePtr fcache, FunctionCallInfo fcinfo)
static Datum postquel_get_single_result (TupleTableSlot *slot, FunctionCallInfo fcinfo, SQLFunctionCachePtr fcache, MemoryContext resultcontext)
static void sql_exec_error_callback (void *arg)
static void ShutdownSQLFunction (Datum arg)
static void sqlfunction_startup (DestReceiver *self, int operation, TupleDesc typeinfo)
static void sqlfunction_receive (TupleTableSlot *slot, DestReceiver *self)
static void sqlfunction_shutdown (DestReceiver *self)
static void sqlfunction_destroy (DestReceiver *self)
SQLFunctionParseInfoPtr prepare_sql_fn_parse_info (HeapTuple procedureTuple, Node *call_expr, Oid inputCollation)
void sql_fn_parser_setup (struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
Datum fmgr_sql (PG_FUNCTION_ARGS)
bool check_sql_fn_retval (Oid func_id, Oid rettype, List *queryTreeList, bool *modifyTargetList, JunkFilter **junkFilter)
DestReceiverCreateSQLFunctionDestReceiver (void)

Typedef Documentation

Definition at line 128 of file functions.c.


Enumeration Type Documentation

enum ExecStatus
Enumerator:
F_EXEC_START 
F_EXEC_RUN 
F_EXEC_DONE 

Definition at line 58 of file functions.c.

{
    F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
} ExecStatus;


Function Documentation

bool check_sql_fn_retval ( Oid  func_id,
Oid  rettype,
List queryTreeList,
bool modifyTargetList,
JunkFilter **  junkFilter 
)

Definition at line 1514 of file functions.c.

References Assert, AssertArg, tupleDesc::attrs, Query::canSetTag, CMD_DELETE, CMD_INSERT, CMD_SELECT, CMD_UPDATE, Query::commandType, CreateTupleDescCopy(), ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCleanTargetListLength(), ExecInitJunkFilter(), ExecInitJunkFilterConversion(), TargetEntry::expr, exprType(), format_type_be(), get_func_result_type(), get_typcollation(), get_typtype(), INT4OID, InvalidOid, IsBinaryCoercible(), IsPolymorphicType, lappend(), lfirst, linitial, list_concat(), makeConst(), makeRelabelType(), makeTargetEntry(), tupleDesc::natts, NULL, RECORDOID, TargetEntry::resjunk, TargetEntry::resno, TargetEntry::ressortgroupref, Query::returningList, Query::setOperations, Query::targetList, TYPEFUNC_COMPOSITE, TYPTYPE_BASE, TYPTYPE_COMPOSITE, TYPTYPE_DOMAIN, TYPTYPE_ENUM, TYPTYPE_RANGE, Query::utilityStmt, and VOIDOID.

Referenced by fmgr_sql_validator(), init_sql_fcache(), inline_function(), and inline_set_returning_function().

{
    Query      *parse;
    List      **tlist_ptr;
    List       *tlist;
    int         tlistlen;
    char        fn_typtype;
    Oid         restype;
    ListCell   *lc;

    AssertArg(!IsPolymorphicType(rettype));

    if (modifyTargetList)
        *modifyTargetList = false;      /* initialize for no change */
    if (junkFilter)
        *junkFilter = NULL;     /* initialize in case of VOID result */

    /*
     * Find the last canSetTag query in the list.  This isn't necessarily the
     * last parsetree, because rule rewriting can insert queries after what
     * the user wrote.
     */
    parse = NULL;
    foreach(lc, queryTreeList)
    {
        Query      *q = (Query *) lfirst(lc);

        if (q->canSetTag)
            parse = q;
    }

    /*
     * If it's a plain SELECT, it returns whatever the targetlist says.
     * Otherwise, if it's INSERT/UPDATE/DELETE with RETURNING, it returns
     * that. Otherwise, the function return type must be VOID.
     *
     * Note: eventually replace this test with QueryReturnsTuples?  We'd need
     * a more general method of determining the output type, though.  Also, it
     * seems too dangerous to consider FETCH or EXECUTE as returning a
     * determinable rowtype, since they depend on relatively short-lived
     * entities.
     */
    if (parse &&
        parse->commandType == CMD_SELECT &&
        parse->utilityStmt == NULL)
    {
        tlist_ptr = &parse->targetList;
        tlist = parse->targetList;
    }
    else if (parse &&
             (parse->commandType == CMD_INSERT ||
              parse->commandType == CMD_UPDATE ||
              parse->commandType == CMD_DELETE) &&
             parse->returningList)
    {
        tlist_ptr = &parse->returningList;
        tlist = parse->returningList;
    }
    else
    {
        /* Empty function body, or last statement is a utility command */
        if (rettype != VOIDOID)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("return type mismatch in function declared to return %s",
                    format_type_be(rettype)),
                     errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE RETURNING.")));
        return false;
    }

    /*
     * OK, check that the targetlist returns something matching the declared
     * type.  (We used to insist that the declared type not be VOID in this
     * case, but that makes it hard to write a void function that exits after
     * calling another void function.  Instead, we insist that the tlist
     * return void ... so void is treated as if it were a scalar type below.)
     */

    /*
     * Count the non-junk entries in the result targetlist.
     */
    tlistlen = ExecCleanTargetListLength(tlist);

    fn_typtype = get_typtype(rettype);

    if (fn_typtype == TYPTYPE_BASE ||
        fn_typtype == TYPTYPE_DOMAIN ||
        fn_typtype == TYPTYPE_ENUM ||
        fn_typtype == TYPTYPE_RANGE ||
        rettype == VOIDOID)
    {
        /*
         * For scalar-type returns, the target list must have exactly one
         * non-junk entry, and its type must agree with what the user
         * declared; except we allow binary-compatible types too.
         */
        TargetEntry *tle;

        if (tlistlen != 1)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("return type mismatch in function declared to return %s",
                    format_type_be(rettype)),
              errdetail("Final statement must return exactly one column.")));

        /* We assume here that non-junk TLEs must come first in tlists */
        tle = (TargetEntry *) linitial(tlist);
        Assert(!tle->resjunk);

        restype = exprType((Node *) tle->expr);
        if (!IsBinaryCoercible(restype, rettype))
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("return type mismatch in function declared to return %s",
                    format_type_be(rettype)),
                     errdetail("Actual return type is %s.",
                               format_type_be(restype))));
        if (modifyTargetList && restype != rettype)
        {
            tle->expr = (Expr *) makeRelabelType(tle->expr,
                                                 rettype,
                                                 -1,
                                                 get_typcollation(rettype),
                                                 COERCE_IMPLICIT_CAST);
            /* Relabel is dangerous if TLE is a sort/group or setop column */
            if (tle->ressortgroupref != 0 || parse->setOperations)
                *modifyTargetList = true;
        }

        /* Set up junk filter if needed */
        if (junkFilter)
            *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
    }
    else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
    {
        /* Returns a rowtype */
        TupleDesc   tupdesc;
        int         tupnatts;   /* physical number of columns in tuple */
        int         tuplogcols; /* # of nondeleted columns in tuple */
        int         colindex;   /* physical column index */
        List       *newtlist;   /* new non-junk tlist entries */
        List       *junkattrs;  /* new junk tlist entries */

        /*
         * If the target list is of length 1, and the type of the varnode in
         * the target list matches the declared return type, this is okay.
         * This can happen, for example, where the body of the function is
         * 'SELECT func2()', where func2 has the same composite return type as
         * the function that's calling it.
         *
         * XXX Note that if rettype is RECORD, the IsBinaryCoercible check
         * will succeed for any composite restype.  For the moment we rely on
         * runtime type checking to catch any discrepancy, but it'd be nice to
         * do better at parse time.
         */
        if (tlistlen == 1)
        {
            TargetEntry *tle = (TargetEntry *) linitial(tlist);

            Assert(!tle->resjunk);
            restype = exprType((Node *) tle->expr);
            if (IsBinaryCoercible(restype, rettype))
            {
                if (modifyTargetList && restype != rettype)
                {
                    tle->expr = (Expr *) makeRelabelType(tle->expr,
                                                         rettype,
                                                         -1,
                                                   get_typcollation(rettype),
                                                         COERCE_IMPLICIT_CAST);
                    /* Relabel is dangerous if sort/group or setop column */
                    if (tle->ressortgroupref != 0 || parse->setOperations)
                        *modifyTargetList = true;
                }
                /* Set up junk filter if needed */
                if (junkFilter)
                    *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
                return false;   /* NOT returning whole tuple */
            }
        }

        /* Is the rowtype fixed, or determined only at runtime? */
        if (get_func_result_type(func_id, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
        {
            /*
             * Assume we are returning the whole tuple. Crosschecking against
             * what the caller expects will happen at runtime.
             */
            if (junkFilter)
                *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
            return true;
        }
        Assert(tupdesc);

        /*
         * Verify that the targetlist matches the return tuple type. We scan
         * the non-deleted attributes to ensure that they match the datatypes
         * of the non-resjunk columns.  For deleted attributes, insert NULL
         * result columns if the caller asked for that.
         */
        tupnatts = tupdesc->natts;
        tuplogcols = 0;         /* we'll count nondeleted cols as we go */
        colindex = 0;
        newtlist = NIL;         /* these are only used if modifyTargetList */
        junkattrs = NIL;

        foreach(lc, tlist)
        {
            TargetEntry *tle = (TargetEntry *) lfirst(lc);
            Form_pg_attribute attr;
            Oid         tletype;
            Oid         atttype;

            if (tle->resjunk)
            {
                if (modifyTargetList)
                    junkattrs = lappend(junkattrs, tle);
                continue;
            }

            do
            {
                colindex++;
                if (colindex > tupnatts)
                    ereport(ERROR,
                            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                             errmsg("return type mismatch in function declared to return %s",
                                    format_type_be(rettype)),
                    errdetail("Final statement returns too many columns.")));
                attr = tupdesc->attrs[colindex - 1];
                if (attr->attisdropped && modifyTargetList)
                {
                    Expr       *null_expr;

                    /* The type of the null we insert isn't important */
                    null_expr = (Expr *) makeConst(INT4OID,
                                                   -1,
                                                   InvalidOid,
                                                   sizeof(int32),
                                                   (Datum) 0,
                                                   true,        /* isnull */
                                                   true /* byval */ );
                    newtlist = lappend(newtlist,
                                       makeTargetEntry(null_expr,
                                                       colindex,
                                                       NULL,
                                                       false));
                    /* NULL insertion is dangerous in a setop */
                    if (parse->setOperations)
                        *modifyTargetList = true;
                }
            } while (attr->attisdropped);
            tuplogcols++;

            tletype = exprType((Node *) tle->expr);
            atttype = attr->atttypid;
            if (!IsBinaryCoercible(tletype, atttype))
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                         errmsg("return type mismatch in function declared to return %s",
                                format_type_be(rettype)),
                         errdetail("Final statement returns %s instead of %s at column %d.",
                                   format_type_be(tletype),
                                   format_type_be(atttype),
                                   tuplogcols)));
            if (modifyTargetList)
            {
                if (tletype != atttype)
                {
                    tle->expr = (Expr *) makeRelabelType(tle->expr,
                                                         atttype,
                                                         -1,
                                                   get_typcollation(atttype),
                                                         COERCE_IMPLICIT_CAST);
                    /* Relabel is dangerous if sort/group or setop column */
                    if (tle->ressortgroupref != 0 || parse->setOperations)
                        *modifyTargetList = true;
                }
                tle->resno = colindex;
                newtlist = lappend(newtlist, tle);
            }
        }

        /* remaining columns in tupdesc had better all be dropped */
        for (colindex++; colindex <= tupnatts; colindex++)
        {
            if (!tupdesc->attrs[colindex - 1]->attisdropped)
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                         errmsg("return type mismatch in function declared to return %s",
                                format_type_be(rettype)),
                     errdetail("Final statement returns too few columns.")));
            if (modifyTargetList)
            {
                Expr       *null_expr;

                /* The type of the null we insert isn't important */
                null_expr = (Expr *) makeConst(INT4OID,
                                               -1,
                                               InvalidOid,
                                               sizeof(int32),
                                               (Datum) 0,
                                               true,    /* isnull */
                                               true /* byval */ );
                newtlist = lappend(newtlist,
                                   makeTargetEntry(null_expr,
                                                   colindex,
                                                   NULL,
                                                   false));
                /* NULL insertion is dangerous in a setop */
                if (parse->setOperations)
                    *modifyTargetList = true;
            }
        }

        if (modifyTargetList)
        {
            /* ensure resjunk columns are numbered correctly */
            foreach(lc, junkattrs)
            {
                TargetEntry *tle = (TargetEntry *) lfirst(lc);

                tle->resno = colindex++;
            }
            /* replace the tlist with the modified one */
            *tlist_ptr = list_concat(newtlist, junkattrs);
        }

        /* Set up junk filter if needed */
        if (junkFilter)
            *junkFilter = ExecInitJunkFilterConversion(tlist,
                                                CreateTupleDescCopy(tupdesc),
                                                       NULL);

        /* Report that we are returning entire tuple result */
        return true;
    }
    else
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("return type %s is not supported for SQL functions",
                        format_type_be(rettype))));

    return false;
}

DestReceiver* CreateSQLFunctionDestReceiver ( void   ) 

Definition at line 1867 of file functions.c.

References palloc0().

Referenced by CreateDestReceiver().

{
    DR_sqlfunction *self = (DR_sqlfunction *) palloc0(sizeof(DR_sqlfunction));

    self->pub.receiveSlot = sqlfunction_receive;
    self->pub.rStartup = sqlfunction_startup;
    self->pub.rShutdown = sqlfunction_shutdown;
    self->pub.rDestroy = sqlfunction_destroy;
    self->pub.mydest = DestSQLFunction;

    /* private fields will be set by postquel_start */

    return (DestReceiver *) self;
}

Datum fmgr_sql ( PG_FUNCTION_ARGS   ) 

Definition at line 972 of file functions.c.

References ReturnSetInfo::allowedModes, ErrorContextCallback::arg, Assert, ErrorContextCallback::callback, CommandCounterIncrement(), CreateTupleDescCopy(), ReturnSetInfo::econtext, elog, ereport, errcode(), errmsg(), ERROR, error_context_stack, F_EXEC_DONE, F_EXEC_START, SQLFunctionCache::fcontext, SQLFunctionCache::func_state, GetTransactionSnapshot(), init_sql_fcache(), IsA, ReturnSetInfo::isDone, JunkFilter::jf_cleanTupType, JunkFilter::jf_resultSlot, SQLFunctionCache::junkFilter, SQLFunctionCache::lazyEval, execution_state::lazyEval, lfirst, lnext, PGPROC::lxid, SQLFunctionCache::lxid, MemoryContextDelete(), MemoryContextSwitchTo(), MyProc, execution_state::next, NULL, PG_GET_COLLATION, PointerGetDatum, PopActiveSnapshot(), postquel_end(), postquel_get_single_result(), postquel_getnext(), postquel_start(), postquel_sub_params(), ErrorContextCallback::previous, PushActiveSnapshot(), execution_state::qd, SQLFunctionCache::readonly_func, RegisterExprContextCallback(), SQLFunctionCache::rettype, ReturnSetInfo::returnMode, SQLFunctionCache::returnsSet, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SQLFunctionCache::shutdown_reg, ShutdownSQLFunction(), QueryDesc::snapshot, execution_state::status, SubTransactionIsActive(), SQLFunctionCache::subxid, SQLFunctionCache::tstore, tuplestore_begin_heap(), tuplestore_clear(), tuplestore_gettupleslot(), UnregisterExprContextCallback(), UpdateActiveSnapshotCommandId(), VOIDOID, and work_mem.

{
    SQLFunctionCachePtr fcache;
    ErrorContextCallback sqlerrcontext;
    MemoryContext oldcontext;
    bool        randomAccess;
    bool        lazyEvalOK;
    bool        is_first;
    bool        pushed_snapshot;
    execution_state *es;
    TupleTableSlot *slot;
    Datum       result;
    List       *eslist;
    ListCell   *eslc;

    /*
     * Setup error traceback support for ereport()
     */
    sqlerrcontext.callback = sql_exec_error_callback;
    sqlerrcontext.arg = fcinfo->flinfo;
    sqlerrcontext.previous = error_context_stack;
    error_context_stack = &sqlerrcontext;

    /* Check call context */
    if (fcinfo->flinfo->fn_retset)
    {
        ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

        /*
         * For simplicity, we require callers to support both set eval modes.
         * There are cases where we must use one or must use the other, and
         * it's not really worthwhile to postpone the check till we know. But
         * note we do not require caller to provide an expectedDesc.
         */
        if (!rsi || !IsA(rsi, ReturnSetInfo) ||
            (rsi->allowedModes & SFRM_ValuePerCall) == 0 ||
            (rsi->allowedModes & SFRM_Materialize) == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("set-valued function called in context that cannot accept a set")));
        randomAccess = rsi->allowedModes & SFRM_Materialize_Random;
        lazyEvalOK = !(rsi->allowedModes & SFRM_Materialize_Preferred);
    }
    else
    {
        randomAccess = false;
        lazyEvalOK = true;
    }

    /*
     * Initialize fcache (build plans) if first time through; or re-initialize
     * if the cache is stale.
     */
    fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;

    if (fcache != NULL)
    {
        if (fcache->lxid != MyProc->lxid ||
            !SubTransactionIsActive(fcache->subxid))
        {
            /* It's stale; unlink and delete */
            fcinfo->flinfo->fn_extra = NULL;
            MemoryContextDelete(fcache->fcontext);
            fcache = NULL;
        }
    }

    if (fcache == NULL)
    {
        init_sql_fcache(fcinfo->flinfo, PG_GET_COLLATION(), lazyEvalOK);
        fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
    }

    /*
     * Switch to context in which the fcache lives.  This ensures that our
     * tuplestore etc will have sufficient lifetime.  The sub-executor is
     * responsible for deleting per-tuple information.  (XXX in the case of a
     * long-lived FmgrInfo, this policy represents more memory leakage, but
     * it's not entirely clear where to keep stuff instead.)
     */
    oldcontext = MemoryContextSwitchTo(fcache->fcontext);

    /*
     * Find first unfinished query in function, and note whether it's the
     * first query.
     */
    eslist = fcache->func_state;
    es = NULL;
    is_first = true;
    foreach(eslc, eslist)
    {
        es = (execution_state *) lfirst(eslc);

        while (es && es->status == F_EXEC_DONE)
        {
            is_first = false;
            es = es->next;
        }

        if (es)
            break;
    }

    /*
     * Convert params to appropriate format if starting a fresh execution. (If
     * continuing execution, we can re-use prior params.)
     */
    if (is_first && es && es->status == F_EXEC_START)
        postquel_sub_params(fcache, fcinfo);

    /*
     * Build tuplestore to hold results, if we don't have one already. Note
     * it's in the query-lifespan context.
     */
    if (!fcache->tstore)
        fcache->tstore = tuplestore_begin_heap(randomAccess, false, work_mem);

    /*
     * Execute each command in the function one after another until we either
     * run out of commands or get a result row from a lazily-evaluated SELECT.
     *
     * Notes about snapshot management:
     *
     * In a read-only function, we just use the surrounding query's snapshot.
     *
     * In a non-read-only function, we rely on the fact that we'll never
     * suspend execution between queries of the function: the only reason to
     * suspend execution before completion is if we are returning a row from a
     * lazily-evaluated SELECT.  So, when first entering this loop, we'll
     * either start a new query (and push a fresh snapshot) or re-establish
     * the active snapshot from the existing query descriptor.  If we need to
     * start a new query in a subsequent execution of the loop, either we need
     * a fresh snapshot (and pushed_snapshot is false) or the existing
     * snapshot is on the active stack and we can just bump its command ID.
     */
    pushed_snapshot = false;
    while (es)
    {
        bool        completed;

        if (es->status == F_EXEC_START)
        {
            /*
             * If not read-only, be sure to advance the command counter for
             * each command, so that all work to date in this transaction is
             * visible.  Take a new snapshot if we don't have one yet,
             * otherwise just bump the command ID in the existing snapshot.
             */
            if (!fcache->readonly_func)
            {
                CommandCounterIncrement();
                if (!pushed_snapshot)
                {
                    PushActiveSnapshot(GetTransactionSnapshot());
                    pushed_snapshot = true;
                }
                else
                    UpdateActiveSnapshotCommandId();
            }

            postquel_start(es, fcache);
        }
        else if (!fcache->readonly_func && !pushed_snapshot)
        {
            /* Re-establish active snapshot when re-entering function */
            PushActiveSnapshot(es->qd->snapshot);
            pushed_snapshot = true;
        }

        completed = postquel_getnext(es, fcache);

        /*
         * If we ran the command to completion, we can shut it down now. Any
         * row(s) we need to return are safely stashed in the tuplestore, and
         * we want to be sure that, for example, AFTER triggers get fired
         * before we return anything.  Also, if the function doesn't return
         * set, we can shut it down anyway because it must be a SELECT and we
         * don't care about fetching any more result rows.
         */
        if (completed || !fcache->returnsSet)
            postquel_end(es);

        /*
         * Break from loop if we didn't shut down (implying we got a
         * lazily-evaluated row).  Otherwise we'll press on till the whole
         * function is done, relying on the tuplestore to keep hold of the
         * data to eventually be returned.  This is necessary since an
         * INSERT/UPDATE/DELETE RETURNING that sets the result might be
         * followed by additional rule-inserted commands, and we want to
         * finish doing all those commands before we return anything.
         */
        if (es->status != F_EXEC_DONE)
            break;

        /*
         * Advance to next execution_state, which might be in the next list.
         */
        es = es->next;
        while (!es)
        {
            eslc = lnext(eslc);
            if (!eslc)
                break;          /* end of function */

            es = (execution_state *) lfirst(eslc);

            /*
             * Flush the current snapshot so that we will take a new one for
             * the new query list.  This ensures that new snaps are taken at
             * original-query boundaries, matching the behavior of interactive
             * execution.
             */
            if (pushed_snapshot)
            {
                PopActiveSnapshot();
                pushed_snapshot = false;
            }
        }
    }

    /*
     * The tuplestore now contains whatever row(s) we are supposed to return.
     */
    if (fcache->returnsSet)
    {
        ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

        if (es)
        {
            /*
             * If we stopped short of being done, we must have a lazy-eval
             * row.
             */
            Assert(es->lazyEval);
            /* Re-use the junkfilter's output slot to fetch back the tuple */
            Assert(fcache->junkFilter);
            slot = fcache->junkFilter->jf_resultSlot;
            if (!tuplestore_gettupleslot(fcache->tstore, true, false, slot))
                elog(ERROR, "failed to fetch lazy-eval tuple");
            /* Extract the result as a datum, and copy out from the slot */
            result = postquel_get_single_result(slot, fcinfo,
                                                fcache, oldcontext);
            /* Clear the tuplestore, but keep it for next time */
            /* NB: this might delete the slot's content, but we don't care */
            tuplestore_clear(fcache->tstore);

            /*
             * Let caller know we're not finished.
             */
            rsi->isDone = ExprMultipleResult;

            /*
             * Ensure we will get shut down cleanly if the exprcontext is not
             * run to completion.
             */
            if (!fcache->shutdown_reg)
            {
                RegisterExprContextCallback(rsi->econtext,
                                            ShutdownSQLFunction,
                                            PointerGetDatum(fcache));
                fcache->shutdown_reg = true;
            }
        }
        else if (fcache->lazyEval)
        {
            /*
             * We are done with a lazy evaluation.  Clean up.
             */
            tuplestore_clear(fcache->tstore);

            /*
             * Let caller know we're finished.
             */
            rsi->isDone = ExprEndResult;

            fcinfo->isnull = true;
            result = (Datum) 0;

            /* Deregister shutdown callback, if we made one */
            if (fcache->shutdown_reg)
            {
                UnregisterExprContextCallback(rsi->econtext,
                                              ShutdownSQLFunction,
                                              PointerGetDatum(fcache));
                fcache->shutdown_reg = false;
            }
        }
        else
        {
            /*
             * We are done with a non-lazy evaluation.  Return whatever is in
             * the tuplestore.  (It is now caller's responsibility to free the
             * tuplestore when done.)
             */
            rsi->returnMode = SFRM_Materialize;
            rsi->setResult = fcache->tstore;
            fcache->tstore = NULL;
            /* must copy desc because execQual will free it */
            if (fcache->junkFilter)
                rsi->setDesc = CreateTupleDescCopy(fcache->junkFilter->jf_cleanTupType);

            fcinfo->isnull = true;
            result = (Datum) 0;

            /* Deregister shutdown callback, if we made one */
            if (fcache->shutdown_reg)
            {
                UnregisterExprContextCallback(rsi->econtext,
                                              ShutdownSQLFunction,
                                              PointerGetDatum(fcache));
                fcache->shutdown_reg = false;
            }
        }
    }
    else
    {
        /*
         * Non-set function.  If we got a row, return it; else return NULL.
         */
        if (fcache->junkFilter)
        {
            /* Re-use the junkfilter's output slot to fetch back the tuple */
            slot = fcache->junkFilter->jf_resultSlot;
            if (tuplestore_gettupleslot(fcache->tstore, true, false, slot))
                result = postquel_get_single_result(slot, fcinfo,
                                                    fcache, oldcontext);
            else
            {
                fcinfo->isnull = true;
                result = (Datum) 0;
            }
        }
        else
        {
            /* Should only get here for VOID functions */
            Assert(fcache->rettype == VOIDOID);
            fcinfo->isnull = true;
            result = (Datum) 0;
        }

        /* Clear the tuplestore, but keep it for next time */
        tuplestore_clear(fcache->tstore);
    }

    /* Pop snapshot if we have pushed one */
    if (pushed_snapshot)
        PopActiveSnapshot();

    /*
     * If we've gone through every command in the function, we are done. Reset
     * the execution states to start over again on next call.
     */
    if (es == NULL)
    {
        foreach(eslc, fcache->func_state)
        {
            es = (execution_state *) lfirst(eslc);
            while (es)
            {
                es->status = F_EXEC_START;
                es = es->next;
            }
        }
    }

    error_context_stack = sqlerrcontext.previous;

    MemoryContextSwitchTo(oldcontext);

    return result;
}

static List * init_execution_state ( List queryTree_list,
SQLFunctionCachePtr  fcache,
bool  lazyEvalOK 
) [static]

Definition at line 464 of file functions.c.

References Assert, Query::canSetTag, CMD_SELECT, CMD_UTILITY, CommandIsReadOnly(), PlannedStmt::commandType, Query::commandType, CreateCommandTag(), ereport, errcode(), errmsg(), ERROR, PlannedStmt::hasModifyingCTE, IsA, SQLFunctionCache::junkFilter, lappend(), SQLFunctionCache::lazyEval, execution_state::lazyEval, lfirst, execution_state::next, NULL, palloc(), pg_plan_query(), execution_state::qd, SQLFunctionCache::readonly_func, execution_state::setsResult, execution_state::status, execution_state::stmt, PlannedStmt::utilityStmt, and Query::utilityStmt.

Referenced by init_sql_fcache().

{
    List       *eslist = NIL;
    execution_state *lasttages = NULL;
    ListCell   *lc1;

    foreach(lc1, queryTree_list)
    {
        List       *qtlist = (List *) lfirst(lc1);
        execution_state *firstes = NULL;
        execution_state *preves = NULL;
        ListCell   *lc2;

        foreach(lc2, qtlist)
        {
            Query      *queryTree = (Query *) lfirst(lc2);
            Node       *stmt;
            execution_state *newes;

            Assert(IsA(queryTree, Query));

            /* Plan the query if needed */
            if (queryTree->commandType == CMD_UTILITY)
                stmt = queryTree->utilityStmt;
            else
                stmt = (Node *) pg_plan_query(queryTree, 0, NULL);

            /* Precheck all commands for validity in a function */
            if (IsA(stmt, TransactionStmt))
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                /* translator: %s is a SQL statement name */
                         errmsg("%s is not allowed in a SQL function",
                                CreateCommandTag(stmt))));

            if (fcache->readonly_func && !CommandIsReadOnly(stmt))
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                /* translator: %s is a SQL statement name */
                       errmsg("%s is not allowed in a non-volatile function",
                              CreateCommandTag(stmt))));

            /* OK, build the execution_state for this query */
            newes = (execution_state *) palloc(sizeof(execution_state));
            if (preves)
                preves->next = newes;
            else
                firstes = newes;

            newes->next = NULL;
            newes->status = F_EXEC_START;
            newes->setsResult = false;  /* might change below */
            newes->lazyEval = false;    /* might change below */
            newes->stmt = stmt;
            newes->qd = NULL;

            if (queryTree->canSetTag)
                lasttages = newes;

            preves = newes;
        }

        eslist = lappend(eslist, firstes);
    }

    /*
     * Mark the last canSetTag query as delivering the function result; then,
     * if it is a plain SELECT, mark it for lazy evaluation. If it's not a
     * SELECT we must always run it to completion.
     *
     * Note: at some point we might add additional criteria for whether to use
     * lazy eval.  However, we should prefer to use it whenever the function
     * doesn't return set, since fetching more than one row is useless in that
     * case.
     *
     * Note: don't set setsResult if the function returns VOID, as evidenced
     * by not having made a junkfilter.  This ensures we'll throw away any
     * output from a utility statement that check_sql_fn_retval deemed to not
     * have output.
     */
    if (lasttages && fcache->junkFilter)
    {
        lasttages->setsResult = true;
        if (lazyEvalOK &&
            IsA(lasttages->stmt, PlannedStmt))
        {
            PlannedStmt *ps = (PlannedStmt *) lasttages->stmt;

            if (ps->commandType == CMD_SELECT &&
                ps->utilityStmt == NULL &&
                !ps->hasModifyingCTE)
                fcache->lazyEval = lasttages->lazyEval = true;
        }
    }

    return eslist;
}

static void init_sql_fcache ( FmgrInfo finfo,
Oid  collation,
bool  lazyEvalOK 
) [static]

Definition at line 568 of file functions.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_proc_prosrc, BlessTupleDesc(), check_sql_fn_retval(), elog, ereport, errcode(), errmsg(), ERROR, SQLFunctionCache::fcontext, FmgrInfo::fn_expr, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, FmgrInfo::fn_oid, SQLFunctionCache::fname, format_type_be(), SQLFunctionCache::func_state, get_fn_expr_rettype(), get_typlenbyval(), GetCurrentSubTransactionId(), GETSTRUCT, HeapTupleIsValid, init_execution_state(), InvalidOid, IsPolymorphicType, JunkFilter::jf_resultSlot, SQLFunctionCache::junkFilter, lappend(), lfirst, list_concat(), list_copy(), PGPROC::lxid, SQLFunctionCache::lxid, MemoryContextSwitchTo(), MyProc, NameStr, NULL, ObjectIdGetDatum, palloc0(), pg_analyze_and_rewrite_params(), pg_parse_query(), SQLFunctionCache::pinfo, prepare_sql_fn_parse_info(), PROCOID, pstrdup(), SQLFunctionCache::readonly_func, ReleaseSysCache(), SQLFunctionCache::rettype, SQLFunctionCache::returnsSet, SQLFunctionCache::returnsTuple, SearchSysCache1, sql_fn_parser_setup(), SQLFunctionCache::src, SQLFunctionCache::subxid, SysCacheGetAttr(), TextDatumGetCString, TupleTableSlot::tts_tupleDescriptor, SQLFunctionCache::typbyval, type_is_rowtype(), and SQLFunctionCache::typlen.

Referenced by fmgr_sql().

{
    Oid         foid = finfo->fn_oid;
    MemoryContext fcontext;
    MemoryContext oldcontext;
    Oid         rettype;
    HeapTuple   procedureTuple;
    Form_pg_proc procedureStruct;
    SQLFunctionCachePtr fcache;
    List       *raw_parsetree_list;
    List       *queryTree_list;
    List       *flat_query_list;
    ListCell   *lc;
    Datum       tmp;
    bool        isNull;

    /*
     * Create memory context that holds all the SQLFunctionCache data.  It
     * must be a child of whatever context holds the FmgrInfo.
     */
    fcontext = AllocSetContextCreate(finfo->fn_mcxt,
                                     "SQL function data",
                                     ALLOCSET_DEFAULT_MINSIZE,
                                     ALLOCSET_DEFAULT_INITSIZE,
                                     ALLOCSET_DEFAULT_MAXSIZE);

    oldcontext = MemoryContextSwitchTo(fcontext);

    /*
     * Create the struct proper, link it to fcontext and fn_extra.  Once this
     * is done, we'll be able to recover the memory after failure, even if the
     * FmgrInfo is long-lived.
     */
    fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
    fcache->fcontext = fcontext;
    finfo->fn_extra = (void *) fcache;

    /*
     * get the procedure tuple corresponding to the given function Oid
     */
    procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(foid));
    if (!HeapTupleIsValid(procedureTuple))
        elog(ERROR, "cache lookup failed for function %u", foid);
    procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);

    /*
     * copy function name immediately for use by error reporting callback
     */
    fcache->fname = pstrdup(NameStr(procedureStruct->proname));

    /*
     * get the result type from the procedure tuple, and check for polymorphic
     * result type; if so, find out the actual result type.
     */
    rettype = procedureStruct->prorettype;

    if (IsPolymorphicType(rettype))
    {
        rettype = get_fn_expr_rettype(finfo);
        if (rettype == InvalidOid)      /* this probably should not happen */
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                     errmsg("could not determine actual result type for function declared to return type %s",
                            format_type_be(procedureStruct->prorettype))));
    }

    fcache->rettype = rettype;

    /* Fetch the typlen and byval info for the result type */
    get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);

    /* Remember whether we're returning setof something */
    fcache->returnsSet = procedureStruct->proretset;

    /* Remember if function is STABLE/IMMUTABLE */
    fcache->readonly_func =
        (procedureStruct->provolatile != PROVOLATILE_VOLATILE);

    /*
     * We need the actual argument types to pass to the parser.  Also make
     * sure that parameter symbols are considered to have the function's
     * resolved input collation.
     */
    fcache->pinfo = prepare_sql_fn_parse_info(procedureTuple,
                                              finfo->fn_expr,
                                              collation);

    /*
     * And of course we need the function body text.
     */
    tmp = SysCacheGetAttr(PROCOID,
                          procedureTuple,
                          Anum_pg_proc_prosrc,
                          &isNull);
    if (isNull)
        elog(ERROR, "null prosrc for function %u", foid);
    fcache->src = TextDatumGetCString(tmp);

    /*
     * Parse and rewrite the queries in the function text.  Use sublists to
     * keep track of the original query boundaries.  But we also build a
     * "flat" list of the rewritten queries to pass to check_sql_fn_retval.
     * This is because the last canSetTag query determines the result type
     * independently of query boundaries --- and it might not be in the last
     * sublist, for example if the last query rewrites to DO INSTEAD NOTHING.
     * (It might not be unreasonable to throw an error in such a case, but
     * this is the historical behavior and it doesn't seem worth changing.)
     *
     * Note: since parsing and planning is done in fcontext, we will generate
     * a lot of cruft that lives as long as the fcache does.  This is annoying
     * but we'll not worry about it until the module is rewritten to use
     * plancache.c.
     */
    raw_parsetree_list = pg_parse_query(fcache->src);

    queryTree_list = NIL;
    flat_query_list = NIL;
    foreach(lc, raw_parsetree_list)
    {
        Node       *parsetree = (Node *) lfirst(lc);
        List       *queryTree_sublist;

        queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
                                                          fcache->src,
                                       (ParserSetupHook) sql_fn_parser_setup,
                                                          fcache->pinfo);
        queryTree_list = lappend(queryTree_list, queryTree_sublist);
        flat_query_list = list_concat(flat_query_list,
                                      list_copy(queryTree_sublist));
    }

    /*
     * Check that the function returns the type it claims to.  Although in
     * simple cases this was already done when the function was defined, we
     * have to recheck because database objects used in the function's queries
     * might have changed type.  We'd have to do it anyway if the function had
     * any polymorphic arguments.
     *
     * Note: we set fcache->returnsTuple according to whether we are returning
     * the whole tuple result or just a single column.  In the latter case we
     * clear returnsTuple because we need not act different from the scalar
     * result case, even if it's a rowtype column.  (However, we have to force
     * lazy eval mode in that case; otherwise we'd need extra code to expand
     * the rowtype column into multiple columns, since we have no way to
     * notify the caller that it should do that.)
     *
     * check_sql_fn_retval will also construct a JunkFilter we can use to
     * coerce the returned rowtype to the desired form (unless the result type
     * is VOID, in which case there's nothing to coerce to).
     */
    fcache->returnsTuple = check_sql_fn_retval(foid,
                                               rettype,
                                               flat_query_list,
                                               NULL,
                                               &fcache->junkFilter);

    if (fcache->returnsTuple)
    {
        /* Make sure output rowtype is properly blessed */
        BlessTupleDesc(fcache->junkFilter->jf_resultSlot->tts_tupleDescriptor);
    }
    else if (fcache->returnsSet && type_is_rowtype(fcache->rettype))
    {
        /*
         * Returning rowtype as if it were scalar --- materialize won't work.
         * Right now it's sufficient to override any caller preference for
         * materialize mode, but to add more smarts in init_execution_state
         * about this, we'd probably need a three-way flag instead of bool.
         */
        lazyEvalOK = true;
    }

    /* Finally, plan the queries */
    fcache->func_state = init_execution_state(queryTree_list,
                                              fcache,
                                              lazyEvalOK);

    /* Mark fcache with time of creation to show it's valid */
    fcache->lxid = MyProc->lxid;
    fcache->subxid = GetCurrentSubTransactionId();

    ReleaseSysCache(procedureTuple);

    MemoryContextSwitchTo(oldcontext);
}

static void postquel_end ( execution_state es  )  [static]

Definition at line 859 of file functions.c.

References QueryDesc::dest, ExecutorEnd(), ExecutorFinish(), FreeQueryDesc(), NULL, execution_state::qd, _DestReceiver::rDestroy, execution_state::status, and QueryDesc::utilitystmt.

Referenced by fmgr_sql(), and ShutdownSQLFunction().

{
    /* mark status done to ensure we don't do ExecutorEnd twice */
    es->status = F_EXEC_DONE;

    /* Utility commands don't need Executor. */
    if (es->qd->utilitystmt == NULL)
    {
        ExecutorFinish(es->qd);
        ExecutorEnd(es->qd);
    }

    (*es->qd->dest->rDestroy) (es->qd->dest);

    FreeQueryDesc(es->qd);
    es->qd = NULL;
}

static Datum postquel_get_single_result ( TupleTableSlot slot,
FunctionCallInfo  fcinfo,
SQLFunctionCachePtr  fcache,
MemoryContext  resultcontext 
) [static]

Definition at line 928 of file functions.c.

References datumCopy(), ExecFetchSlotTupleDatum(), FunctionCallInfoData::isnull, MemoryContextSwitchTo(), SQLFunctionCache::returnsTuple, slot_getattr(), SQLFunctionCache::typbyval, SQLFunctionCache::typlen, and value.

Referenced by fmgr_sql().

{
    Datum       value;
    MemoryContext oldcontext;

    /*
     * Set up to return the function value.  For pass-by-reference datatypes,
     * be sure to allocate the result in resultcontext, not the current memory
     * context (which has query lifespan).  We can't leave the data in the
     * TupleTableSlot because we intend to clear the slot before returning.
     */
    oldcontext = MemoryContextSwitchTo(resultcontext);

    if (fcache->returnsTuple)
    {
        /* We must return the whole tuple as a Datum. */
        fcinfo->isnull = false;
        value = ExecFetchSlotTupleDatum(slot);
        value = datumCopy(value, fcache->typbyval, fcache->typlen);
    }
    else
    {
        /*
         * Returning a scalar, which we have to extract from the first column
         * of the SELECT result, and then copy into result context if needed.
         */
        value = slot_getattr(slot, 1, &(fcinfo->isnull));

        if (!fcinfo->isnull)
            value = datumCopy(value, fcache->typbyval, fcache->typlen);
    }

    MemoryContextSwitchTo(oldcontext);

    return value;
}

static bool postquel_getnext ( execution_state es,
SQLFunctionCachePtr  fcache 
) [static]

Definition at line 823 of file functions.c.

References QueryDesc::dest, EState::es_processed, QueryDesc::estate, ExecutorRun(), ForwardScanDirection, execution_state::lazyEval, NULL, QueryDesc::params, QueryDesc::plannedstmt, PROCESS_UTILITY_QUERY, ProcessUtility(), execution_state::qd, SQLFunctionCache::src, and QueryDesc::utilitystmt.

Referenced by fmgr_sql().

{
    bool        result;

    if (es->qd->utilitystmt)
    {
        /* ProcessUtility needs the PlannedStmt for DECLARE CURSOR */
        ProcessUtility((es->qd->plannedstmt ?
                        (Node *) es->qd->plannedstmt :
                        es->qd->utilitystmt),
                       fcache->src,
                       PROCESS_UTILITY_QUERY,
                       es->qd->params,
                       es->qd->dest,
                       NULL);
        result = true;          /* never stops early */
    }
    else
    {
        /* Run regular commands to completion unless lazyEval */
        long        count = (es->lazyEval) ? 1L : 0L;

        ExecutorRun(es->qd, ForwardScanDirection, count);

        /*
         * If we requested run to completion OR there was no tuple returned,
         * command must be complete.
         */
        result = (count == 0L || es->qd->estate->es_processed == 0);
    }

    return result;
}

static void postquel_start ( execution_state es,
SQLFunctionCachePtr  fcache 
) [static]

Definition at line 756 of file functions.c.

References ActiveSnapshotSet(), Assert, CreateDestReceiver(), CreateQueryDesc(), CreateUtilityQueryDesc(), CurrentMemoryContext, DR_sqlfunction::cxt, DestSQLFunction, ExecutorStart(), DR_sqlfunction::filter, GetActiveSnapshot(), InvalidSnapshot, IsA, SQLFunctionCache::junkFilter, execution_state::lazyEval, _DestReceiver::mydest, None_Receiver, NULL, SQLFunctionCache::paramLI, DR_sqlfunction::pub, execution_state::qd, execution_state::setsResult, SQLFunctionCache::src, execution_state::status, execution_state::stmt, SQLFunctionCache::tstore, DR_sqlfunction::tstore, and QueryDesc::utilitystmt.

Referenced by fmgr_sql().

{
    DestReceiver *dest;

    Assert(es->qd == NULL);

    /* Caller should have ensured a suitable snapshot is active */
    Assert(ActiveSnapshotSet());

    /*
     * If this query produces the function result, send its output to the
     * tuplestore; else discard any output.
     */
    if (es->setsResult)
    {
        DR_sqlfunction *myState;

        dest = CreateDestReceiver(DestSQLFunction);
        /* pass down the needed info to the dest receiver routines */
        myState = (DR_sqlfunction *) dest;
        Assert(myState->pub.mydest == DestSQLFunction);
        myState->tstore = fcache->tstore;
        myState->cxt = CurrentMemoryContext;
        myState->filter = fcache->junkFilter;
    }
    else
        dest = None_Receiver;

    if (IsA(es->stmt, PlannedStmt))
        es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
                                 fcache->src,
                                 GetActiveSnapshot(),
                                 InvalidSnapshot,
                                 dest,
                                 fcache->paramLI, 0);
    else
        es->qd = CreateUtilityQueryDesc(es->stmt,
                                        fcache->src,
                                        GetActiveSnapshot(),
                                        dest,
                                        fcache->paramLI);

    /* Utility commands don't need Executor. */
    if (es->qd->utilitystmt == NULL)
    {
        /*
         * In lazyEval mode, do not let the executor set up an AfterTrigger
         * context.  This is necessary not just an optimization, because we
         * mustn't exit from the function execution with a stacked
         * AfterTrigger level still active.  We are careful not to select
         * lazyEval mode for any statement that could possibly queue triggers.
         */
        int         eflags;

        if (es->lazyEval)
            eflags = EXEC_FLAG_SKIP_TRIGGERS;
        else
            eflags = 0;         /* default run-to-completion flags */
        ExecutorStart(es->qd, eflags);
    }

    es->status = F_EXEC_RUN;
}

static void postquel_sub_params ( SQLFunctionCachePtr  fcache,
FunctionCallInfo  fcinfo 
) [static]

Definition at line 879 of file functions.c.

References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, SQLFunctionParseInfo::argtypes, Assert, i, ParamExternData::isnull, FunctionCallInfoData::nargs, NULL, ParamListInfoData::numParams, palloc(), ParamListInfoData::paramFetch, ParamListInfoData::paramFetchArg, SQLFunctionCache::paramLI, ParamListInfoData::params, ParamListInfoData::parserSetup, ParamListInfoData::parserSetupArg, ParamExternData::pflags, SQLFunctionCache::pinfo, ParamExternData::ptype, and ParamExternData::value.

Referenced by fmgr_sql().

{
    int         nargs = fcinfo->nargs;

    if (nargs > 0)
    {
        ParamListInfo paramLI;
        int         i;

        if (fcache->paramLI == NULL)
        {
            /* sizeof(ParamListInfoData) includes the first array element */
            paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
                                      (nargs - 1) * sizeof(ParamExternData));
            /* we have static list of params, so no hooks needed */
            paramLI->paramFetch = NULL;
            paramLI->paramFetchArg = NULL;
            paramLI->parserSetup = NULL;
            paramLI->parserSetupArg = NULL;
            paramLI->numParams = nargs;
            fcache->paramLI = paramLI;
        }
        else
        {
            paramLI = fcache->paramLI;
            Assert(paramLI->numParams == nargs);
        }

        for (i = 0; i < nargs; i++)
        {
            ParamExternData *prm = &paramLI->params[i];

            prm->value = fcinfo->arg[i];
            prm->isnull = fcinfo->argnull[i];
            prm->pflags = 0;
            prm->ptype = fcache->pinfo->argtypes[i];
        }
    }
    else
        fcache->paramLI = NULL;
}

SQLFunctionParseInfoPtr prepare_sql_fn_parse_info ( HeapTuple  procedureTuple,
Node call_expr,
Oid  inputCollation 
)

Definition at line 184 of file functions.c.

References Anum_pg_proc_proargmodes, Anum_pg_proc_proargnames, SQLFunctionParseInfo::argnames, SQLFunctionParseInfo::argtypes, SQLFunctionParseInfo::collation, ereport, errcode(), errmsg(), ERROR, SQLFunctionParseInfo::fname, format_type_be(), get_call_expr_argtype(), get_func_input_arg_names(), GETSTRUCT, InvalidOid, IsPolymorphicType, NameStr, SQLFunctionParseInfo::nargs, NULL, palloc(), palloc0(), PointerGetDatum, PROCNAMEARGSNSP, pstrdup(), and SysCacheGetAttr().

Referenced by fmgr_sql_validator(), init_sql_fcache(), inline_function(), and inline_set_returning_function().

{
    SQLFunctionParseInfoPtr pinfo;
    Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
    int         nargs;

    pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo));

    /* Function's name (only) can be used to qualify argument names */
    pinfo->fname = pstrdup(NameStr(procedureStruct->proname));

    /* Save the function's input collation */
    pinfo->collation = inputCollation;

    /*
     * Copy input argument types from the pg_proc entry, then resolve any
     * polymorphic types.
     */
    pinfo->nargs = nargs = procedureStruct->pronargs;
    if (nargs > 0)
    {
        Oid        *argOidVect;
        int         argnum;

        argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
        memcpy(argOidVect,
               procedureStruct->proargtypes.values,
               nargs * sizeof(Oid));

        for (argnum = 0; argnum < nargs; argnum++)
        {
            Oid         argtype = argOidVect[argnum];

            if (IsPolymorphicType(argtype))
            {
                argtype = get_call_expr_argtype(call_expr, argnum);
                if (argtype == InvalidOid)
                    ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                             errmsg("could not determine actual type of argument declared %s",
                                    format_type_be(argOidVect[argnum]))));
                argOidVect[argnum] = argtype;
            }
        }

        pinfo->argtypes = argOidVect;
    }

    /*
     * Collect names of arguments, too, if any
     */
    if (nargs > 0)
    {
        Datum       proargnames;
        Datum       proargmodes;
        int         n_arg_names;
        bool        isNull;

        proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
                                      Anum_pg_proc_proargnames,
                                      &isNull);
        if (isNull)
            proargnames = PointerGetDatum(NULL);        /* just to be sure */

        proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
                                      Anum_pg_proc_proargmodes,
                                      &isNull);
        if (isNull)
            proargmodes = PointerGetDatum(NULL);        /* just to be sure */

        n_arg_names = get_func_input_arg_names(proargnames, proargmodes,
                                               &pinfo->argnames);

        /* Paranoia: ignore the result if too few array entries */
        if (n_arg_names < nargs)
            pinfo->argnames = NULL;
    }
    else
        pinfo->argnames = NULL;

    return pinfo;
}

static void ShutdownSQLFunction ( Datum  arg  )  [static]

Definition at line 1431 of file functions.c.

References DatumGetPointer, F_EXEC_RUN, SQLFunctionCache::func_state, lfirst, execution_state::next, PopActiveSnapshot(), postquel_end(), PushActiveSnapshot(), execution_state::qd, SQLFunctionCache::readonly_func, SQLFunctionCache::shutdown_reg, QueryDesc::snapshot, execution_state::status, SQLFunctionCache::tstore, and tuplestore_end().

Referenced by fmgr_sql().

{
    SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
    execution_state *es;
    ListCell   *lc;

    foreach(lc, fcache->func_state)
    {
        es = (execution_state *) lfirst(lc);
        while (es)
        {
            /* Shut down anything still running */
            if (es->status == F_EXEC_RUN)
            {
                /* Re-establish active snapshot for any called functions */
                if (!fcache->readonly_func)
                    PushActiveSnapshot(es->qd->snapshot);

                postquel_end(es);

                if (!fcache->readonly_func)
                    PopActiveSnapshot();
            }

            /* Reset states to START in case we're called again */
            es->status = F_EXEC_START;
            es = es->next;
        }
    }

    /* Release tuplestore if we have one */
    if (fcache->tstore)
        tuplestore_end(fcache->tstore);
    fcache->tstore = NULL;

    /* execUtils will deregister the callback... */
    fcache->shutdown_reg = false;
}

static void sql_exec_error_callback ( void *  arg  )  [static]

Definition at line 1349 of file functions.c.

References errcontext, errposition(), FmgrInfo::fn_extra, SQLFunctionCache::fname, SQLFunctionCache::func_state, geterrposition(), internalerrposition(), internalerrquery(), lfirst, execution_state::next, NULL, execution_state::qd, and SQLFunctionCache::src.

{
    FmgrInfo   *flinfo = (FmgrInfo *) arg;
    SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;
    int         syntaxerrposition;

    /*
     * We can do nothing useful if init_sql_fcache() didn't get as far as
     * saving the function name
     */
    if (fcache == NULL || fcache->fname == NULL)
        return;

    /*
     * If there is a syntax error position, convert to internal syntax error
     */
    syntaxerrposition = geterrposition();
    if (syntaxerrposition > 0 && fcache->src != NULL)
    {
        errposition(0);
        internalerrposition(syntaxerrposition);
        internalerrquery(fcache->src);
    }

    /*
     * Try to determine where in the function we failed.  If there is a query
     * with non-null QueryDesc, finger it.  (We check this rather than looking
     * for F_EXEC_RUN state, so that errors during ExecutorStart or
     * ExecutorEnd are blamed on the appropriate query; see postquel_start and
     * postquel_end.)
     */
    if (fcache->func_state)
    {
        execution_state *es;
        int         query_num;
        ListCell   *lc;

        es = NULL;
        query_num = 1;
        foreach(lc, fcache->func_state)
        {
            es = (execution_state *) lfirst(lc);
            while (es)
            {
                if (es->qd)
                {
                    errcontext("SQL function \"%s\" statement %d",
                               fcache->fname, query_num);
                    break;
                }
                es = es->next;
            }
            if (es)
                break;
            query_num++;
        }
        if (es == NULL)
        {
            /*
             * couldn't identify a running query; might be function entry,
             * function exit, or between queries.
             */
            errcontext("SQL function \"%s\"", fcache->fname);
        }
    }
    else
    {
        /*
         * Assume we failed during init_sql_fcache().  (It's possible that the
         * function actually has an empty body, but in that case we may as
         * well report all errors as being "during startup".)
         */
        errcontext("SQL function \"%s\" during startup", fcache->fname);
    }
}

static Node * sql_fn_make_param ( SQLFunctionParseInfoPtr  pinfo,
int  paramno,
int  location 
) [static]

Definition at line 410 of file functions.c.

References SQLFunctionParseInfo::argtypes, SQLFunctionParseInfo::collation, get_typcollation(), Param::location, makeNode, OidIsValid, Param::paramcollid, Param::paramid, Param::paramkind, Param::paramtype, and Param::paramtypmod.

Referenced by sql_fn_param_ref(), and sql_fn_resolve_param_name().

{
    Param      *param;

    param = makeNode(Param);
    param->paramkind = PARAM_EXTERN;
    param->paramid = paramno;
    param->paramtype = pinfo->argtypes[paramno - 1];
    param->paramtypmod = -1;
    param->paramcollid = get_typcollation(param->paramtype);
    param->location = location;

    /*
     * If we have a function input collation, allow it to override the
     * type-derived collation for parameter symbols.  (XXX perhaps this should
     * not happen if the type collation is not default?)
     */
    if (OidIsValid(pinfo->collation) && OidIsValid(param->paramcollid))
        param->paramcollid = pinfo->collation;

    return (Node *) param;
}

static Node * sql_fn_param_ref ( ParseState pstate,
ParamRef pref 
) [static]

Definition at line 394 of file functions.c.

References ParamRef::location, SQLFunctionParseInfo::nargs, ParamRef::number, ParseState::p_ref_hook_state, and sql_fn_make_param().

{
    SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
    int         paramno = pref->number;

    /* Check parameter number is valid */
    if (paramno <= 0 || paramno > pinfo->nargs)
        return NULL;            /* unknown parameter number */

    return sql_fn_make_param(pinfo, paramno, pref->location);
}

void sql_fn_parser_setup ( struct ParseState pstate,
SQLFunctionParseInfoPtr  pinfo 
)

Definition at line 273 of file functions.c.

References ParseState::p_paramref_hook, ParseState::p_post_columnref_hook, ParseState::p_pre_columnref_hook, and ParseState::p_ref_hook_state.

Referenced by fmgr_sql_validator(), init_sql_fcache(), inline_function(), and inline_set_returning_function().

{
    pstate->p_pre_columnref_hook = NULL;
    pstate->p_post_columnref_hook = sql_fn_post_column_ref;
    pstate->p_paramref_hook = sql_fn_param_ref;
    /* no need to use p_coerce_param_hook */
    pstate->p_ref_hook_state = (void *) pinfo;
}

static Node * sql_fn_post_column_ref ( ParseState pstate,
ColumnRef cref,
Node var 
) [static]

Definition at line 286 of file functions.c.

References Assert, ColumnRef::fields, SQLFunctionParseInfo::fname, IsA, linitial, list_length(), list_make1, ColumnRef::location, lsecond, lthird, NIL, NULL, ParseState::p_ref_hook_state, ParseFuncOrColumn(), sql_fn_resolve_param_name(), and strVal.

{
    SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
    int         nnames;
    Node       *field1;
    Node       *subfield = NULL;
    const char *name1;
    const char *name2 = NULL;
    Node       *param;

    /*
     * Never override a table-column reference.  This corresponds to
     * considering the parameter names to appear in a scope outside the
     * individual SQL commands, which is what we want.
     */
    if (var != NULL)
        return NULL;

    /*----------
     * The allowed syntaxes are:
     *
     * A        A = parameter name
     * A.B      A = function name, B = parameter name
     *          OR: A = record-typed parameter name, B = field name
     *          (the first possibility takes precedence)
     * A.B.C    A = function name, B = record-typed parameter name,
     *          C = field name
     *----------
     */
    nnames = list_length(cref->fields);

    if (nnames > 3)
        return NULL;

    field1 = (Node *) linitial(cref->fields);
    Assert(IsA(field1, String));
    name1 = strVal(field1);
    if (nnames > 1)
    {
        subfield = (Node *) lsecond(cref->fields);
        Assert(IsA(subfield, String));
        name2 = strVal(subfield);
    }

    if (nnames == 3)
    {
        /*
         * Three-part name: if the first part doesn't match the function name,
         * we can fail immediately. Otherwise, look up the second part, and
         * take the third part to be a field reference.
         */
        if (strcmp(name1, pinfo->fname) != 0)
            return NULL;

        param = sql_fn_resolve_param_name(pinfo, name2, cref->location);

        subfield = (Node *) lthird(cref->fields);
        Assert(IsA(subfield, String));
    }
    else if (nnames == 2 && strcmp(name1, pinfo->fname) == 0)
    {
        /*
         * Two-part name with first part matching function name: first see if
         * second part matches any parameter name.
         */
        param = sql_fn_resolve_param_name(pinfo, name2, cref->location);

        if (param)
        {
            /* Yes, so this is a parameter reference, no subfield */
            subfield = NULL;
        }
        else
        {
            /* No, so try to match as parameter name and subfield */
            param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
        }
    }
    else
    {
        /* Single name, or parameter name followed by subfield */
        param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
    }

    if (!param)
        return NULL;            /* No match */

    if (subfield)
    {
        /*
         * Must be a reference to a field of a composite parameter; otherwise
         * ParseFuncOrColumn will return NULL, and we'll fail back at the
         * caller.
         */
        param = ParseFuncOrColumn(pstate,
                                  list_make1(subfield),
                                  list_make1(param),
                                  NIL, false, false, false,
                                  NULL, true, cref->location);
    }

    return param;
}

static Node * sql_fn_resolve_param_name ( SQLFunctionParseInfoPtr  pinfo,
const char *  paramname,
int  location 
) [static]

Definition at line 440 of file functions.c.

References SQLFunctionParseInfo::argnames, i, SQLFunctionParseInfo::nargs, NULL, and sql_fn_make_param().

Referenced by sql_fn_post_column_ref().

{
    int         i;

    if (pinfo->argnames == NULL)
        return NULL;

    for (i = 0; i < pinfo->nargs; i++)
    {
        if (pinfo->argnames[i] && strcmp(pinfo->argnames[i], paramname) == 0)
            return sql_fn_make_param(pinfo, i + 1, location);
    }

    return NULL;
}

static void sqlfunction_destroy ( DestReceiver self  )  [static]

Definition at line 1919 of file functions.c.

References pfree().

{
    pfree(self);
}

static void sqlfunction_receive ( TupleTableSlot slot,
DestReceiver self 
) [static]

Definition at line 1895 of file functions.c.

References ExecFilterJunk(), DR_sqlfunction::filter, DR_sqlfunction::tstore, and tuplestore_puttupleslot().

{
    DR_sqlfunction *myState = (DR_sqlfunction *) self;

    /* Filter tuple as needed */
    slot = ExecFilterJunk(myState->filter, slot);

    /* Store the filtered tuple into the tuplestore */
    tuplestore_puttupleslot(myState->tstore, slot);
}

static void sqlfunction_shutdown ( DestReceiver self  )  [static]

Definition at line 1910 of file functions.c.

{
    /* no-op */
}

static void sqlfunction_startup ( DestReceiver self,
int  operation,
TupleDesc  typeinfo 
) [static]

Definition at line 1886 of file functions.c.

{
    /* no-op */
}