Header And Logo

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

Functions | Variables

plpy_procedure.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/transam.h"
#include "funcapi.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/syscache.h"
#include "plpython.h"
#include "plpy_procedure.h"
#include "plpy_elog.h"
#include "plpy_main.h"
Include dependency graph for plpy_procedure.c:

Go to the source code of this file.

Functions

static PLyProcedurePLy_procedure_create (HeapTuple procTup, Oid fn_oid, bool is_trigger)
static bool PLy_procedure_argument_valid (PLyTypeInfo *arg)
static bool PLy_procedure_valid (PLyProcedure *proc, HeapTuple procTup)
static char * PLy_procedure_munge_source (const char *name, const char *src)
void init_procedure_caches (void)
char * PLy_procedure_name (PLyProcedure *proc)
PLyProcedurePLy_procedure_get (Oid fn_oid, Oid fn_rel, bool is_trigger)
void PLy_procedure_compile (PLyProcedure *proc, const char *src)
void PLy_procedure_delete (PLyProcedure *proc)

Variables

static HTABPLy_procedure_cache = NULL

Function Documentation

void init_procedure_caches ( void   ) 

Definition at line 35 of file plpy_procedure.c.

References HASHCTL::entrysize, HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, and HASHCTL::keysize.

Referenced by _PG_init().

{
    HASHCTL     hash_ctl;

    memset(&hash_ctl, 0, sizeof(hash_ctl));
    hash_ctl.keysize = sizeof(PLyProcedureKey);
    hash_ctl.entrysize = sizeof(PLyProcedureEntry);
    hash_ctl.hash = tag_hash;
    PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
                                      HASH_ELEM | HASH_FUNCTION);
}

static bool PLy_procedure_argument_valid ( PLyTypeInfo arg  )  [static]

Definition at line 423 of file plpy_procedure.c.

References Assert, elog, ERROR, HeapTupleHeaderGetXmin, HeapTupleIsValid, PLyTypeInfo::is_rowtype, ItemPointerEquals(), ItemPointerIsValid, ObjectIdGetDatum, OidIsValid, ReleaseSysCache(), RELOID, SearchSysCache1, HeapTupleData::t_data, HeapTupleData::t_self, TransactionIdIsValid, PLyTypeInfo::typ_relid, PLyTypeInfo::typrel_tid, and PLyTypeInfo::typrel_xmin.

Referenced by PLy_procedure_valid().

{
    HeapTuple   relTup;
    bool        valid;

    /* Nothing to cache unless type is composite */
    if (arg->is_rowtype != 1)
        return true;

    /*
     * Zero typ_relid means that we got called on an output argument of a
     * function returning a unnamed record type; the info for it can't change.
     */
    if (!OidIsValid(arg->typ_relid))
        return true;

    /* Else we should have some cached data */
    Assert(TransactionIdIsValid(arg->typrel_xmin));
    Assert(ItemPointerIsValid(&arg->typrel_tid));

    /* Get the pg_class tuple for the data type */
    relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid));
    if (!HeapTupleIsValid(relTup))
        elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);

    /* If it has changed, the cached data is not valid */
    valid = (arg->typrel_xmin == HeapTupleHeaderGetXmin(relTup->t_data) &&
             ItemPointerEquals(&arg->typrel_tid, &relTup->t_self));

    ReleaseSysCache(relTup);

    return valid;
}

void PLy_procedure_compile ( PLyProcedure proc,
const char *  src 
)

Definition at line 341 of file plpy_procedure.c.

References PLyProcedure::code, elog, ERROR, PLyProcedure::globals, NAMEDATALEN, NULL, pfree(), PLy_elog(), PLy_interp_globals, PLy_procedure_munge_source(), PLy_strdup(), PLyProcedure::proname, PLyProcedure::pyname, snprintf(), PLyProcedure::src, and PLyProcedure::statics.

Referenced by plpython_inline_handler(), and PLy_procedure_create().

{
    PyObject   *crv = NULL;
    char       *msrc;

    proc->globals = PyDict_Copy(PLy_interp_globals);

    /*
     * SD is private preserved data between calls. GD is global data shared by
     * all functions
     */
    proc->statics = PyDict_New();
    PyDict_SetItemString(proc->globals, "SD", proc->statics);

    /*
     * insert the function code into the interpreter
     */
    msrc = PLy_procedure_munge_source(proc->pyname, src);
    /* Save the mangled source for later inclusion in tracebacks */
    proc->src = PLy_strdup(msrc);
    crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
    pfree(msrc);

    if (crv != NULL)
    {
        int         clen;
        char        call[NAMEDATALEN + 256];

        Py_DECREF(crv);

        /*
         * compile a call to the function
         */
        clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
        if (clen < 0 || clen >= sizeof(call))
            elog(ERROR, "string would overflow buffer");
        proc->code = Py_CompileString(call, "<string>", Py_eval_input);
        if (proc->code != NULL)
            return;
    }

    if (proc->proname)
        PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
                 proc->proname);
    else
        PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
}

static PLyProcedure * PLy_procedure_create ( HeapTuple  procTup,
Oid  fn_oid,
bool  is_trigger 
) [static]

Definition at line 138 of file plpy_procedure.c.

References Anum_pg_proc_prosrc, PLyProcedure::argnames, PLyProcedure::args, Assert, PLyProcedure::code, PLyTypeOutput::d, elog, ereport, errcode(), errmsg(), ERROR, PLyProcedure::fn_readonly, PLyProcedure::fn_tid, PLyProcedure::fn_xmin, format_type_be(), get_func_arg_info(), GETSTRUCT, PLyProcedure::globals, HeapTupleHeaderGetXmin, HeapTupleIsValid, i, PLyTypeInfo::is_rowtype, PLyProcedure::is_setof, NAMEDATALEN, NameStr, PLyProcedure::nargs, NULL, ObjectIdGetDatum, PLyTypeInfo::out, pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_input_datum_func(), PLy_malloc(), PLy_malloc0(), PLy_output_datum_func(), PLy_procedure_compile(), PLy_procedure_delete(), PLy_strdup(), PLy_typeinfo_init(), PROARGMODE_OUT, PROARGMODE_TABLE, PROCOID, PLyProcedure::proname, PLyProcedure::pyname, RECORDOID, ReleaseSysCache(), PLyProcedure::result, SearchSysCache1, PLyProcedure::setof, snprintf(), PLyProcedure::src, PLyProcedure::statics, SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_self, TextDatumGetCString, TRIGGEROID, TYPEOID, types, PLyObToDatum::typmod, PLyObToDatum::typoid, TYPTYPE_COMPOSITE, TYPTYPE_PSEUDO, and VOIDOID.

Referenced by PLy_procedure_get().

{
    char        procName[NAMEDATALEN + 256];
    Form_pg_proc procStruct;
    PLyProcedure *volatile proc;
    char       *volatile procSource = NULL;
    Datum       prosrcdatum;
    bool        isnull;
    int         i,
                rv;

    procStruct = (Form_pg_proc) GETSTRUCT(procTup);
    rv = snprintf(procName, sizeof(procName),
                  "__plpython_procedure_%s_%u",
                  NameStr(procStruct->proname),
                  fn_oid);
    if (rv >= sizeof(procName) || rv < 0)
        elog(ERROR, "procedure name would overrun buffer");

    proc = PLy_malloc(sizeof(PLyProcedure));
    proc->proname = PLy_strdup(NameStr(procStruct->proname));
    proc->pyname = PLy_strdup(procName);
    proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
    proc->fn_tid = procTup->t_self;
    /* Remember if function is STABLE/IMMUTABLE */
    proc->fn_readonly =
        (procStruct->provolatile != PROVOLATILE_VOLATILE);
    PLy_typeinfo_init(&proc->result);
    for (i = 0; i < FUNC_MAX_ARGS; i++)
        PLy_typeinfo_init(&proc->args[i]);
    proc->nargs = 0;
    proc->code = proc->statics = NULL;
    proc->globals = NULL;
    proc->is_setof = procStruct->proretset;
    proc->setof = NULL;
    proc->src = NULL;
    proc->argnames = NULL;

    PG_TRY();
    {
        /*
         * get information required for output conversion of the return value,
         * but only if this isn't a trigger.
         */
        if (!is_trigger)
        {
            HeapTuple   rvTypeTup;
            Form_pg_type rvTypeStruct;

            rvTypeTup = SearchSysCache1(TYPEOID,
                                   ObjectIdGetDatum(procStruct->prorettype));
            if (!HeapTupleIsValid(rvTypeTup))
                elog(ERROR, "cache lookup failed for type %u",
                     procStruct->prorettype);
            rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);

            /* Disallow pseudotype result, except for void or record */
            if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
            {
                if (procStruct->prorettype == TRIGGEROID)
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("trigger functions can only be called as triggers")));
                else if (procStruct->prorettype != VOIDOID &&
                         procStruct->prorettype != RECORDOID)
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("PL/Python functions cannot return type %s",
                                 format_type_be(procStruct->prorettype))));
            }

            if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE ||
                procStruct->prorettype == RECORDOID)
            {
                /*
                 * Tuple: set up later, during first call to
                 * PLy_function_handler
                 */
                proc->result.out.d.typoid = procStruct->prorettype;
                proc->result.out.d.typmod = -1;
                proc->result.is_rowtype = 2;
            }
            else
            {
                /* do the real work */
                PLy_output_datum_func(&proc->result, rvTypeTup);
            }

            ReleaseSysCache(rvTypeTup);
        }

        /*
         * Now get information required for input conversion of the
         * procedure's arguments.  Note that we ignore output arguments here.
         * If the function returns record, those I/O functions will be set up
         * when the function is first called.
         */
        if (procStruct->pronargs)
        {
            Oid        *types;
            char      **names,
                       *modes;
            int         i,
                        pos,
                        total;

            /* extract argument type info from the pg_proc tuple */
            total = get_func_arg_info(procTup, &types, &names, &modes);

            /* count number of in+inout args into proc->nargs */
            if (modes == NULL)
                proc->nargs = total;
            else
            {
                /* proc->nargs was initialized to 0 above */
                for (i = 0; i < total; i++)
                {
                    if (modes[i] != PROARGMODE_OUT &&
                        modes[i] != PROARGMODE_TABLE)
                        (proc->nargs)++;
                }
            }

            proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
            for (i = pos = 0; i < total; i++)
            {
                HeapTuple   argTypeTup;
                Form_pg_type argTypeStruct;

                if (modes &&
                    (modes[i] == PROARGMODE_OUT ||
                     modes[i] == PROARGMODE_TABLE))
                    continue;   /* skip OUT arguments */

                Assert(types[i] == procStruct->proargtypes.values[pos]);

                argTypeTup = SearchSysCache1(TYPEOID,
                                             ObjectIdGetDatum(types[i]));
                if (!HeapTupleIsValid(argTypeTup))
                    elog(ERROR, "cache lookup failed for type %u", types[i]);
                argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);

                /* check argument type is OK, set up I/O function info */
                switch (argTypeStruct->typtype)
                {
                    case TYPTYPE_PSEUDO:
                        /* Disallow pseudotype argument */
                        ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("PL/Python functions cannot accept type %s",
                                 format_type_be(types[i]))));
                        break;
                    case TYPTYPE_COMPOSITE:
                        /* we'll set IO funcs at first call */
                        proc->args[pos].is_rowtype = 2;
                        break;
                    default:
                        PLy_input_datum_func(&(proc->args[pos]),
                                             types[i],
                                             argTypeTup);
                        break;
                }

                /* get argument name */
                proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;

                ReleaseSysCache(argTypeTup);

                pos++;
            }
        }

        /*
         * get the text of the function.
         */
        prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
                                      Anum_pg_proc_prosrc, &isnull);
        if (isnull)
            elog(ERROR, "null prosrc");
        procSource = TextDatumGetCString(prosrcdatum);

        PLy_procedure_compile(proc, procSource);

        pfree(procSource);
        procSource = NULL;
    }
    PG_CATCH();
    {
        PLy_procedure_delete(proc);
        if (procSource)
            pfree(procSource);

        PG_RE_THROW();
    }
    PG_END_TRY();

    return proc;
}

void PLy_procedure_delete ( PLyProcedure proc  ) 

Definition at line 390 of file plpy_procedure.c.

References PLyProcedure::argnames, PLyProcedure::args, PLyObToTuple::atts, PLyTupleToOb::atts, PLyProcedure::code, PLyProcedure::globals, i, PLyTypeInfo::in, PLyTypeInfo::is_rowtype, PLyProcedure::nargs, PLyTypeInfo::out, PLy_free(), PLyProcedure::proname, PLyProcedure::pyname, PLyTypeOutput::r, PLyTypeInput::r, PLyProcedure::src, and PLyProcedure::statics.

Referenced by plpython_inline_handler(), PLy_procedure_create(), and PLy_procedure_get().

{
    int         i;

    Py_XDECREF(proc->code);
    Py_XDECREF(proc->statics);
    Py_XDECREF(proc->globals);
    if (proc->proname)
        PLy_free(proc->proname);
    if (proc->pyname)
        PLy_free(proc->pyname);
    for (i = 0; i < proc->nargs; i++)
    {
        if (proc->args[i].is_rowtype == 1)
        {
            if (proc->args[i].in.r.atts)
                PLy_free(proc->args[i].in.r.atts);
            if (proc->args[i].out.r.atts)
                PLy_free(proc->args[i].out.r.atts);
        }
        if (proc->argnames && proc->argnames[i])
            PLy_free(proc->argnames[i]);
    }
    if (proc->src)
        PLy_free(proc->src);
    if (proc->argnames)
        PLy_free(proc->argnames);
}

PLyProcedure* PLy_procedure_get ( Oid  fn_oid,
Oid  fn_rel,
bool  is_trigger 
)

Definition at line 75 of file plpy_procedure.c.

References elog, ERROR, PLyProcedureKey::fn_oid, PLyProcedureKey::fn_rel, HASH_ENTER, HASH_REMOVE, hash_search(), HeapTupleIsValid, NULL, ObjectIdGetDatum, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_free(), PLy_procedure_create(), PLy_procedure_delete(), PLy_procedure_valid(), PLyProcedureEntry::proc, PROCOID, ReleaseSysCache(), and SearchSysCache1.

Referenced by plpython_call_handler(), and plpython_validator().

{
    bool        use_cache = !(is_trigger && fn_rel == InvalidOid);
    HeapTuple   procTup;
    PLyProcedureKey key;
    PLyProcedureEntry *volatile entry = NULL;
    PLyProcedure *volatile proc = NULL;
    bool        found = false;

    procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
    if (!HeapTupleIsValid(procTup))
        elog(ERROR, "cache lookup failed for function %u", fn_oid);

    /*
     * Look for the function in the cache, unless we don't have the necessary
     * information (e.g. during validation). In that case we just don't cache
     * anything.
     */
    if (use_cache)
    {
        key.fn_oid = fn_oid;
        key.fn_rel = fn_rel;
        entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
        proc = entry->proc;
    }

    PG_TRY();
    {
        if (!found)
        {
            /* Haven't found it, create a new procedure */
            proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
            if (use_cache)
                entry->proc = proc;
        }
        else if (!PLy_procedure_valid(proc, procTup))
        {
            /* Found it, but it's invalid, free and reuse the cache entry */
            PLy_procedure_delete(proc);
            PLy_free(proc);
            proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
            entry->proc = proc;
        }
        /* Found it and it's valid, it's fine to use it */
    }
    PG_CATCH();
    {
        /* Do not leave an uninitialised entry in the cache */
        if (use_cache)
            hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
        PG_RE_THROW();
    }
    PG_END_TRY();

    ReleaseSysCache(procTup);

    return proc;
}

static char * PLy_procedure_munge_source ( const char *  name,
const char *  src 
) [static]

Definition at line 492 of file plpy_procedure.c.

References Assert, elog, FATAL, palloc(), and snprintf().

Referenced by PLy_procedure_compile().

{
    char       *mrc,
               *mp;
    const char *sp;
    size_t      mlen,
                plen;

    /*
     * room for function source and the def statement
     */
    mlen = (strlen(src) * 2) + strlen(name) + 16;

    mrc = palloc(mlen);
    plen = snprintf(mrc, mlen, "def %s():\n\t", name);
    Assert(plen >= 0 && plen < mlen);

    sp = src;
    mp = mrc + plen;

    while (*sp != '\0')
    {
        if (*sp == '\r' && *(sp + 1) == '\n')
            sp++;

        if (*sp == '\n' || *sp == '\r')
        {
            *mp++ = '\n';
            *mp++ = '\t';
            sp++;
        }
        else
            *mp++ = *sp++;
    }
    *mp++ = '\n';
    *mp++ = '\n';
    *mp = '\0';

    if (mp > (mrc + mlen))
        elog(FATAL, "buffer overrun in PLy_munge_source");

    return mrc;
}

char* PLy_procedure_name ( PLyProcedure proc  ) 

Definition at line 55 of file plpy_procedure.c.

References NULL, and PLyProcedure::proname.

Referenced by plpython_error_callback(), and PLy_traceback().

{
    if (proc == NULL)
        return "<unknown procedure>";
    return proc->proname;
}

static bool PLy_procedure_valid ( PLyProcedure proc,
HeapTuple  procTup 
) [static]

Definition at line 461 of file plpy_procedure.c.

References PLyProcedure::args, Assert, PLyProcedure::fn_tid, PLyProcedure::fn_xmin, HeapTupleHeaderGetXmin, i, ItemPointerEquals(), PLyProcedure::nargs, NULL, PLy_procedure_argument_valid(), PLyProcedure::result, HeapTupleData::t_data, and HeapTupleData::t_self.

Referenced by PLy_procedure_get().

{
    int         i;
    bool        valid;

    Assert(proc != NULL);

    /* If the pg_proc tuple has changed, it's not valid */
    if (!(proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
          ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
        return false;

    /* Else check the input argument datatypes */
    valid = true;
    for (i = 0; i < proc->nargs; i++)
    {
        valid = PLy_procedure_argument_valid(&proc->args[i]);

        /* Short-circuit on first changed argument */
        if (!valid)
            break;
    }

    /* if the output type is composite, it might have changed */
    if (valid)
        valid = PLy_procedure_argument_valid(&proc->result);

    return valid;
}


Variable Documentation

HTAB* PLy_procedure_cache = NULL [static]

Definition at line 26 of file plpy_procedure.c.