Header And Logo

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

Typedefs | Functions

functions.h File Reference

#include "nodes/execnodes.h"
#include "tcop/dest.h"
Include dependency graph for functions.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Typedefs

typedef struct
SQLFunctionParseInfo
SQLFunctionParseInfoPtr

Functions

Datum fmgr_sql (PG_FUNCTION_ARGS)
SQLFunctionParseInfoPtr prepare_sql_fn_parse_info (HeapTuple procedureTuple, Node *call_expr, Oid inputCollation)
void sql_fn_parser_setup (struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
bool check_sql_fn_retval (Oid func_id, Oid rettype, List *queryTreeList, bool *modifyTargetList, JunkFilter **junkFilter)
DestReceiverCreateSQLFunctionDestReceiver (void)

Typedef Documentation

Definition at line 21 of file functions.h.


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;
}

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;
}

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;
}