Header And Logo

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

Data Structures | Defines | Typedefs | Functions | Variables

pltcl.c File Reference

#include "postgres.h"
#include <tcl.h>
#include <unistd.h>
#include <fcntl.h>
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "fmgr.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for pltcl.c:

Go to the source code of this file.

Data Structures

struct  pltcl_interp_desc
struct  pltcl_proc_desc
struct  pltcl_query_desc
struct  pltcl_proc_key
struct  pltcl_proc_ptr

Defines

#define CONST86
#define HAVE_TCL_VERSION(maj, min)
#define Tcl_GetStringResult(interp)   ((interp)->result)
#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")
#define UTF_BEGIN
#define UTF_END
#define UTF_U2E(x)   (x)
#define UTF_E2U(x)   (x)

Typedefs

typedef struct pltcl_interp_desc pltcl_interp_desc
typedef struct pltcl_proc_desc pltcl_proc_desc
typedef struct pltcl_query_desc pltcl_query_desc
typedef struct pltcl_proc_key pltcl_proc_key
typedef struct pltcl_proc_ptr pltcl_proc_ptr

Functions

Datum pltcl_call_handler (PG_FUNCTION_ARGS)
Datum pltclu_call_handler (PG_FUNCTION_ARGS)
void _PG_init (void)
static void pltcl_init_interp (pltcl_interp_desc *interp_desc, bool pltrusted)
static pltcl_interp_descpltcl_fetch_interp (bool pltrusted)
static void pltcl_init_load_unknown (Tcl_Interp *interp)
static Datum pltcl_handler (PG_FUNCTION_ARGS, bool pltrusted)
static Datum pltcl_func_handler (PG_FUNCTION_ARGS, bool pltrusted)
static HeapTuple pltcl_trigger_handler (PG_FUNCTION_ARGS, bool pltrusted)
static void throw_tcl_error (Tcl_Interp *interp, const char *proname)
static pltcl_proc_desccompile_pltcl_function (Oid fn_oid, Oid tgreloid, bool pltrusted)
static int pltcl_elog (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_quote (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_argisnull (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_returnnull (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_SPI_execute (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_process_SPI_result (Tcl_Interp *interp, CONST84 char *arrayname, CONST84 char *loop_body, int spi_rc, SPITupleTable *tuptable, int ntuples)
static int pltcl_SPI_prepare (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_SPI_execute_plan (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static int pltcl_SPI_lastoid (ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
static void pltcl_set_tuple_values (Tcl_Interp *interp, CONST84 char *arrayname, int tupno, HeapTuple tuple, TupleDesc tupdesc)
static void pltcl_build_tuple_argument (HeapTuple tuple, TupleDesc tupdesc, Tcl_DString *retval)
static void perm_fmgr_info (Oid functionId, FmgrInfo *finfo)
 PG_FUNCTION_INFO_V1 (pltcl_call_handler)
 PG_FUNCTION_INFO_V1 (pltclu_call_handler)
static void pltcl_subtrans_begin (MemoryContext oldcontext, ResourceOwner oldowner)
static void pltcl_subtrans_commit (MemoryContext oldcontext, ResourceOwner oldowner)
static void pltcl_subtrans_abort (Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)

Variables

 PG_MODULE_MAGIC
static bool pltcl_pm_init_done = false
static Tcl_Interp * pltcl_hold_interp = NULL
static HTABpltcl_interp_htab = NULL
static HTABpltcl_proc_htab = NULL
static FunctionCallInfo pltcl_current_fcinfo = NULL
static pltcl_proc_descpltcl_current_prodesc = NULL

Define Documentation

#define CONST86

Definition at line 23 of file pltcl.c.

#define HAVE_TCL_VERSION (   maj,
  min 
)
Value:
((TCL_MAJOR_VERSION > maj) || \
     (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))

Definition at line 45 of file pltcl.c.

#define Tcl_GetStringResult (   interp  )     ((interp)->result)
#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")

Definition at line 56 of file pltcl.c.

Referenced by _PG_init().

#define UTF_BEGIN

Definition at line 84 of file pltcl.c.

#define UTF_E2U (   x  )     (x)
#define UTF_END

Definition at line 85 of file pltcl.c.

#define UTF_U2E (   x  )     (x)

Typedef Documentation


Function Documentation

void _PG_init ( void   ) 

Definition at line 326 of file pltcl.c.

References auth_delay_milliseconds, ClientAuthentication_hook, DefineCustomIntVariable(), elog, HASHCTL::entrysize, ERROR, GUC_UNIT_MS, HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, HASHCTL::keysize, NULL, original_client_auth_hook, pg_bindtextdomain(), PGC_SIGHUP, and TEXTDOMAIN.

{
    HASHCTL     hash_ctl;

    /* Be sure we do initialization only once (should be redundant now) */
    if (pltcl_pm_init_done)
        return;

    pg_bindtextdomain(TEXTDOMAIN);

#ifdef WIN32
    /* Required on win32 to prevent error loading init.tcl */
    Tcl_FindExecutable("");
#endif

#if HAVE_TCL_VERSION(8,4)

    /*
     * Override the functions in the Notifier subsystem.  See comments above.
     */
    {
        Tcl_NotifierProcs notifier;

        notifier.setTimerProc = pltcl_SetTimer;
        notifier.waitForEventProc = pltcl_WaitForEvent;
        notifier.createFileHandlerProc = pltcl_CreateFileHandler;
        notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
        notifier.initNotifierProc = pltcl_InitNotifier;
        notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
        notifier.alertNotifierProc = pltcl_AlertNotifier;
        notifier.serviceModeHookProc = pltcl_ServiceModeHook;
        Tcl_SetNotifier(&notifier);
    }
#endif

    /************************************************************
     * Create the dummy hold interpreter to prevent close of
     * stdout and stderr on DeleteInterp
     ************************************************************/
    if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
        elog(ERROR, "could not create master Tcl interpreter");
    if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
        elog(ERROR, "could not initialize master Tcl interpreter");

    /************************************************************
     * Create the hash table for working interpreters
     ************************************************************/
    memset(&hash_ctl, 0, sizeof(hash_ctl));
    hash_ctl.keysize = sizeof(Oid);
    hash_ctl.entrysize = sizeof(pltcl_interp_desc);
    hash_ctl.hash = oid_hash;
    pltcl_interp_htab = hash_create("PL/Tcl interpreters",
                                    8,
                                    &hash_ctl,
                                    HASH_ELEM | HASH_FUNCTION);

    /************************************************************
     * Create the hash table for function lookup
     ************************************************************/
    memset(&hash_ctl, 0, sizeof(hash_ctl));
    hash_ctl.keysize = sizeof(pltcl_proc_key);
    hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
    hash_ctl.hash = tag_hash;
    pltcl_proc_htab = hash_create("PL/Tcl functions",
                                  100,
                                  &hash_ctl,
                                  HASH_ELEM | HASH_FUNCTION);

    pltcl_pm_init_done = true;
}

static pltcl_proc_desc * compile_pltcl_function ( Oid  fn_oid,
Oid  tgreloid,
bool  pltrusted 
) [static]

Definition at line 1171 of file pltcl.c.

References Anum_pg_proc_prosrc, pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, buf, elog, ereport, errcode(), errmsg(), ERROR, pltcl_proc_desc::fn_readonly, pltcl_proc_desc::fn_tid, pltcl_proc_desc::fn_xmin, format_type_be(), free, GETSTRUCT, getTypeIOParam(), GetUserId(), HASH_ENTER, hash_search(), HeapTupleHeaderGetXmin, HeapTupleIsValid, i, pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, pltcl_proc_key::is_trigger, ItemPointerEquals(), pltcl_proc_desc::lanpltrusted, malloc, MemSet, NameStr, pltcl_proc_desc::nargs, NULL, ObjectIdGetDatum, OidIsValid, perm_fmgr_info(), pfree(), pltcl_fetch_interp(), pltcl_proc_key::proc_id, pltcl_proc_ptr::proc_ptr, PROCOID, ReleaseSysCache(), pltcl_proc_desc::result_in_func, pltcl_proc_desc::result_typioparam, SearchSysCache1, snprintf(), SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_self, Tcl_GetStringResult, TextDatumGetCString, TRIGGEROID, TYPEOID, TYPTYPE_COMPOSITE, TYPTYPE_PSEUDO, pltcl_proc_key::user_id, pltcl_proc_desc::user_proname, UTF_E2U, UTF_U2E, and VOIDOID.

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

{
    HeapTuple   procTup;
    Form_pg_proc procStruct;
    pltcl_proc_key proc_key;
    pltcl_proc_ptr *proc_ptr;
    bool        found;
    pltcl_proc_desc *prodesc;

    /* We'll need the pg_proc tuple in any case... */
    procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
    if (!HeapTupleIsValid(procTup))
        elog(ERROR, "cache lookup failed for function %u", fn_oid);
    procStruct = (Form_pg_proc) GETSTRUCT(procTup);

    /* Try to find function in pltcl_proc_htab */
    proc_key.proc_id = fn_oid;
    proc_key.is_trigger = OidIsValid(tgreloid);
    proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;

    proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
                           HASH_ENTER,
                           &found);
    if (!found)
        proc_ptr->proc_ptr = NULL;

    prodesc = proc_ptr->proc_ptr;

    /************************************************************
     * If it's present, must check whether it's still up to date.
     * This is needed because CREATE OR REPLACE FUNCTION can modify the
     * function's pg_proc entry without changing its OID.
     ************************************************************/
    if (prodesc != NULL)
    {
        bool        uptodate;

        uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
                    ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));

        if (!uptodate)
        {
            proc_ptr->proc_ptr = NULL;
            prodesc = NULL;
        }
    }

    /************************************************************
     * If we haven't found it in the hashtable, we analyze
     * the functions arguments and returntype and store
     * the in-/out-functions in the prodesc block and create
     * a new hashtable entry for it.
     *
     * Then we load the procedure into the Tcl interpreter.
     ************************************************************/
    if (prodesc == NULL)
    {
        bool        is_trigger = OidIsValid(tgreloid);
        char        internal_proname[128];
        HeapTuple   typeTup;
        Form_pg_type typeStruct;
        Tcl_DString proc_internal_def;
        Tcl_DString proc_internal_body;
        char        proc_internal_args[33 * FUNC_MAX_ARGS];
        Datum       prosrcdatum;
        bool        isnull;
        char       *proc_source;
        char        buf[32];
        Tcl_Interp *interp;
        int         i;
        int         tcl_rc;

        /************************************************************
         * Build our internal proc name from the function's Oid.  Append
         * "_trigger" when appropriate to ensure the normal and trigger
         * cases are kept separate.
         ************************************************************/
        if (!is_trigger)
            snprintf(internal_proname, sizeof(internal_proname),
                     "__PLTcl_proc_%u", fn_oid);
        else
            snprintf(internal_proname, sizeof(internal_proname),
                     "__PLTcl_proc_%u_trigger", fn_oid);

        /************************************************************
         * Allocate a new procedure description block
         ************************************************************/
        prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
        if (prodesc == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
        MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
        prodesc->user_proname = strdup(NameStr(procStruct->proname));
        prodesc->internal_proname = strdup(internal_proname);
        if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
        prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
        prodesc->fn_tid = procTup->t_self;

        /* Remember if function is STABLE/IMMUTABLE */
        prodesc->fn_readonly =
            (procStruct->provolatile != PROVOLATILE_VOLATILE);
        /* And whether it is trusted */
        prodesc->lanpltrusted = pltrusted;

        /************************************************************
         * Identify the interpreter to use for the function
         ************************************************************/
        prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
        interp = prodesc->interp_desc->interp;

        /************************************************************
         * Get the required information for input conversion of the
         * return value.
         ************************************************************/
        if (!is_trigger)
        {
            typeTup =
                SearchSysCache1(TYPEOID,
                                ObjectIdGetDatum(procStruct->prorettype));
            if (!HeapTupleIsValid(typeTup))
            {
                free(prodesc->user_proname);
                free(prodesc->internal_proname);
                free(prodesc);
                elog(ERROR, "cache lookup failed for type %u",
                     procStruct->prorettype);
            }
            typeStruct = (Form_pg_type) GETSTRUCT(typeTup);

            /* Disallow pseudotype result, except VOID */
            if (typeStruct->typtype == TYPTYPE_PSEUDO)
            {
                if (procStruct->prorettype == VOIDOID)
                     /* okay */ ;
                else if (procStruct->prorettype == TRIGGEROID)
                {
                    free(prodesc->user_proname);
                    free(prodesc->internal_proname);
                    free(prodesc);
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("trigger functions can only be called as triggers")));
                }
                else
                {
                    free(prodesc->user_proname);
                    free(prodesc->internal_proname);
                    free(prodesc);
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("PL/Tcl functions cannot return type %s",
                                    format_type_be(procStruct->prorettype))));
                }
            }

            if (typeStruct->typtype == TYPTYPE_COMPOSITE)
            {
                free(prodesc->user_proname);
                free(prodesc->internal_proname);
                free(prodesc);
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("PL/Tcl functions cannot return composite types")));
            }

            perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
            prodesc->result_typioparam = getTypeIOParam(typeTup);

            ReleaseSysCache(typeTup);
        }

        /************************************************************
         * Get the required information for output conversion
         * of all procedure arguments
         ************************************************************/
        if (!is_trigger)
        {
            prodesc->nargs = procStruct->pronargs;
            proc_internal_args[0] = '\0';
            for (i = 0; i < prodesc->nargs; i++)
            {
                typeTup = SearchSysCache1(TYPEOID,
                        ObjectIdGetDatum(procStruct->proargtypes.values[i]));
                if (!HeapTupleIsValid(typeTup))
                {
                    free(prodesc->user_proname);
                    free(prodesc->internal_proname);
                    free(prodesc);
                    elog(ERROR, "cache lookup failed for type %u",
                         procStruct->proargtypes.values[i]);
                }
                typeStruct = (Form_pg_type) GETSTRUCT(typeTup);

                /* Disallow pseudotype argument */
                if (typeStruct->typtype == TYPTYPE_PSEUDO)
                {
                    free(prodesc->user_proname);
                    free(prodesc->internal_proname);
                    free(prodesc);
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("PL/Tcl functions cannot accept type %s",
                        format_type_be(procStruct->proargtypes.values[i]))));
                }

                if (typeStruct->typtype == TYPTYPE_COMPOSITE)
                {
                    prodesc->arg_is_rowtype[i] = true;
                    snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
                }
                else
                {
                    prodesc->arg_is_rowtype[i] = false;
                    perm_fmgr_info(typeStruct->typoutput,
                                   &(prodesc->arg_out_func[i]));
                    snprintf(buf, sizeof(buf), "%d", i + 1);
                }

                if (i > 0)
                    strcat(proc_internal_args, " ");
                strcat(proc_internal_args, buf);

                ReleaseSysCache(typeTup);
            }
        }
        else
        {
            /* trigger procedure has fixed args */
            strcpy(proc_internal_args,
                   "TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
        }

        /************************************************************
         * Create the tcl command to define the internal
         * procedure
         ************************************************************/
        Tcl_DStringInit(&proc_internal_def);
        Tcl_DStringInit(&proc_internal_body);
        Tcl_DStringAppendElement(&proc_internal_def, "proc");
        Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
        Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);

        /************************************************************
         * prefix procedure body with
         * upvar #0 <internal_procname> GD
         * and with appropriate setting of arguments
         ************************************************************/
        Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
        Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
        Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
        if (!is_trigger)
        {
            for (i = 0; i < prodesc->nargs; i++)
            {
                if (prodesc->arg_is_rowtype[i])
                {
                    snprintf(buf, sizeof(buf),
                             "array set %d $__PLTcl_Tup_%d\n",
                             i + 1, i + 1);
                    Tcl_DStringAppend(&proc_internal_body, buf, -1);
                }
            }
        }
        else
        {
            Tcl_DStringAppend(&proc_internal_body,
                              "array set NEW $__PLTcl_Tup_NEW\n", -1);
            Tcl_DStringAppend(&proc_internal_body,
                              "array set OLD $__PLTcl_Tup_OLD\n", -1);

            Tcl_DStringAppend(&proc_internal_body,
                              "set i 0\n"
                              "set v 0\n"
                              "foreach v $args {\n"
                              "  incr i\n"
                              "  set $i $v\n"
                              "}\n"
                              "unset i v\n\n", -1);
        }

        /************************************************************
         * Add user's function definition to proc body
         ************************************************************/
        prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
                                      Anum_pg_proc_prosrc, &isnull);
        if (isnull)
            elog(ERROR, "null prosrc");
        proc_source = TextDatumGetCString(prosrcdatum);
        UTF_BEGIN;
        Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
        UTF_END;
        pfree(proc_source);
        Tcl_DStringAppendElement(&proc_internal_def,
                                 Tcl_DStringValue(&proc_internal_body));
        Tcl_DStringFree(&proc_internal_body);

        /************************************************************
         * Create the procedure in the interpreter
         ************************************************************/
        tcl_rc = Tcl_GlobalEval(interp,
                                Tcl_DStringValue(&proc_internal_def));
        Tcl_DStringFree(&proc_internal_def);
        if (tcl_rc != TCL_OK)
        {
            free(prodesc->user_proname);
            free(prodesc->internal_proname);
            free(prodesc);
            UTF_BEGIN;
            elog(ERROR, "could not create internal procedure \"%s\": %s",
                 internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
            UTF_END;
        }

        /************************************************************
         * Add the proc description block to the hashtable.  Note we do not
         * attempt to free any previously existing prodesc block.  This is
         * annoying, but necessary since there could be active calls using
         * the old prodesc.
         ************************************************************/
        proc_ptr->proc_ptr = prodesc;
    }

    ReleaseSysCache(procTup);

    return prodesc;
}

static void perm_fmgr_info ( Oid  functionId,
FmgrInfo finfo 
) [static]

Definition at line 312 of file pltcl.c.

References fmgr_info_cxt(), and TopMemoryContext.

Referenced by compile_pltcl_function(), and pltcl_SPI_prepare().

{
    fmgr_info_cxt(functionId, finfo, TopMemoryContext);
}

PG_FUNCTION_INFO_V1 ( pltcl_call_handler   ) 
PG_FUNCTION_INFO_V1 ( pltclu_call_handler   ) 
static int pltcl_argisnull ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 1648 of file pltcl.c.

References FunctionCallInfoData::nargs, NULL, and PG_ARGISNULL.

Referenced by pltcl_init_interp().

{
    int         argno;
    FunctionCallInfo fcinfo = pltcl_current_fcinfo;

    /************************************************************
     * Check call syntax
     ************************************************************/
    if (argc != 2)
    {
        Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
                      TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Check that we're called as a normal function
     ************************************************************/
    if (fcinfo == NULL)
    {
        Tcl_SetResult(interp, "argisnull cannot be used in triggers",
                      TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Get the argument number
     ************************************************************/
    if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
        return TCL_ERROR;

    /************************************************************
     * Check that the argno is valid
     ************************************************************/
    argno--;
    if (argno < 0 || argno >= fcinfo->nargs)
    {
        Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Get the requested NULL state
     ************************************************************/
    if (PG_ARGISNULL(argno))
        Tcl_SetResult(interp, "1", TCL_STATIC);
    else
        Tcl_SetResult(interp, "0", TCL_STATIC);

    return TCL_OK;
}

static void pltcl_build_tuple_argument ( HeapTuple  tuple,
TupleDesc  tupdesc,
Tcl_DString *  retval 
) [static]

Definition at line 2486 of file pltcl.c.

References tupleDesc::attrs, elog, ERROR, GETSTRUCT, heap_getattr, HeapTupleIsValid, i, NameStr, tupleDesc::natts, ObjectIdGetDatum, OidIsValid, OidOutputFunctionCall(), pfree(), ReleaseSysCache(), SearchSysCache1, TYPEOID, and UTF_E2U.

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

{
    int         i;
    char       *outputstr;
    Datum       attr;
    bool        isnull;

    char       *attname;
    HeapTuple   typeTup;
    Oid         typoutput;

    for (i = 0; i < tupdesc->natts; i++)
    {
        /* ignore dropped attributes */
        if (tupdesc->attrs[i]->attisdropped)
            continue;

        /************************************************************
         * Get the attribute name
         ************************************************************/
        attname = NameStr(tupdesc->attrs[i]->attname);

        /************************************************************
         * Get the attributes value
         ************************************************************/
        attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);

        /************************************************************
         * Lookup the attribute type in the syscache
         * for the output function
         ************************************************************/
        typeTup = SearchSysCache1(TYPEOID,
                              ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
        if (!HeapTupleIsValid(typeTup))
            elog(ERROR, "cache lookup failed for type %u",
                 tupdesc->attrs[i]->atttypid);

        typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
        ReleaseSysCache(typeTup);

        /************************************************************
         * If there is a value, append the attribute name and the
         * value to the list
         *
         * Hmmm - Null attributes will cause functions to
         *        crash if they don't expect them - need something
         *        smarter here.
         ************************************************************/
        if (!isnull && OidIsValid(typoutput))
        {
            outputstr = OidOutputFunctionCall(typoutput, attr);
            Tcl_DStringAppendElement(retval, attname);
            UTF_BEGIN;
            Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
            UTF_END;
            pfree(outputstr);
        }
    }
}

Datum pltcl_call_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 605 of file pltcl.c.

References pltcl_handler().

{
    return pltcl_handler(fcinfo, true);
}

static int pltcl_elog ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 1507 of file pltcl.c.

References CopyErrorData(), CurrentMemoryContext, elog, ERROR, FlushErrorState(), FreeErrorData(), MemoryContextSwitchTo(), ErrorData::message, NULL, PG_CATCH, PG_END_TRY, PG_TRY, UTF_E2U, and UTF_U2E.

Referenced by pltcl_init_interp().

{
    volatile int level;
    MemoryContext oldcontext;

    if (argc != 3)
    {
        Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
        return TCL_ERROR;
    }

    if (strcmp(argv[1], "DEBUG") == 0)
        level = DEBUG2;
    else if (strcmp(argv[1], "LOG") == 0)
        level = LOG;
    else if (strcmp(argv[1], "INFO") == 0)
        level = INFO;
    else if (strcmp(argv[1], "NOTICE") == 0)
        level = NOTICE;
    else if (strcmp(argv[1], "WARNING") == 0)
        level = WARNING;
    else if (strcmp(argv[1], "ERROR") == 0)
        level = ERROR;
    else if (strcmp(argv[1], "FATAL") == 0)
        level = FATAL;
    else
    {
        Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
                         "'", NULL);
        return TCL_ERROR;
    }

    if (level == ERROR)
    {
        /*
         * We just pass the error back to Tcl.  If it's not caught, it'll
         * eventually get converted to a PG error when we reach the call
         * handler.
         */
        Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
        return TCL_ERROR;
    }

    /*
     * For non-error messages, just pass 'em to elog().  We do not expect that
     * this will fail, but just on the off chance it does, report the error
     * back to Tcl.  Note we are assuming that elog() can't have any internal
     * failures that are so bad as to require a transaction abort.
     *
     * This path is also used for FATAL errors, which aren't going to come
     * back to us at all.
     */
    oldcontext = CurrentMemoryContext;
    PG_TRY();
    {
        UTF_BEGIN;
        elog(level, "%s", UTF_U2E(argv[2]));
        UTF_END;
    }
    PG_CATCH();
    {
        ErrorData  *edata;

        /* Must reset elog.c's state */
        MemoryContextSwitchTo(oldcontext);
        edata = CopyErrorData();
        FlushErrorState();

        /* Pass the error message to Tcl */
        UTF_BEGIN;
        Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
        UTF_END;
        FreeErrorData(edata);

        return TCL_ERROR;
    }
    PG_END_TRY();

    return TCL_OK;
}

static pltcl_interp_desc * pltcl_fetch_interp ( bool  pltrusted  )  [static]

Definition at line 456 of file pltcl.c.

References GetUserId(), HASH_ENTER, hash_search(), and pltcl_init_interp().

Referenced by compile_pltcl_function().

{
    Oid         user_id;
    pltcl_interp_desc *interp_desc;
    bool        found;

    /* Find or create the interpreter hashtable entry for this userid */
    if (pltrusted)
        user_id = GetUserId();
    else
        user_id = InvalidOid;

    interp_desc = hash_search(pltcl_interp_htab, &user_id,
                              HASH_ENTER,
                              &found);
    if (!found)
        pltcl_init_interp(interp_desc, pltrusted);

    return interp_desc;
}

static Datum pltcl_func_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
) [static]

Definition at line 672 of file pltcl.c.

References pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, compile_pltcl_function(), DatumGetHeapTupleHeader, elog, ERROR, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, InputFunctionCall(), pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, InvalidOid, lookup_rowtype_tupdesc(), pltcl_proc_desc::nargs, NULL, OutputFunctionCall(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_build_tuple_argument(), ReleaseTupleDesc, pltcl_proc_desc::result_in_func, pltcl_proc_desc::result_typioparam, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, HeapTupleData::t_data, HeapTupleData::t_len, Tcl_GetStringResult, throw_tcl_error(), pltcl_proc_desc::user_proname, UTF_E2U, and UTF_U2E.

Referenced by pltcl_handler().

{
    pltcl_proc_desc *prodesc;
    Tcl_Interp *volatile interp;
    Tcl_DString tcl_cmd;
    Tcl_DString list_tmp;
    int         i;
    int         tcl_rc;
    Datum       retval;

    /* Connect to SPI manager */
    if (SPI_connect() != SPI_OK_CONNECT)
        elog(ERROR, "could not connect to SPI manager");

    /* Find or compile the function */
    prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
                                     pltrusted);

    pltcl_current_prodesc = prodesc;

    interp = prodesc->interp_desc->interp;

    /************************************************************
     * Create the tcl command to call the internal
     * proc in the Tcl interpreter
     ************************************************************/
    Tcl_DStringInit(&tcl_cmd);
    Tcl_DStringInit(&list_tmp);
    Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);

    /************************************************************
     * Add all call arguments to the command
     ************************************************************/
    PG_TRY();
    {
        for (i = 0; i < prodesc->nargs; i++)
        {
            if (prodesc->arg_is_rowtype[i])
            {
                /**************************************************
                 * For tuple values, add a list for 'array set ...'
                 **************************************************/
                if (fcinfo->argnull[i])
                    Tcl_DStringAppendElement(&tcl_cmd, "");
                else
                {
                    HeapTupleHeader td;
                    Oid         tupType;
                    int32       tupTypmod;
                    TupleDesc   tupdesc;
                    HeapTupleData tmptup;

                    td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
                    /* Extract rowtype info and find a tupdesc */
                    tupType = HeapTupleHeaderGetTypeId(td);
                    tupTypmod = HeapTupleHeaderGetTypMod(td);
                    tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
                    /* Build a temporary HeapTuple control structure */
                    tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
                    tmptup.t_data = td;

                    Tcl_DStringSetLength(&list_tmp, 0);
                    pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
                    Tcl_DStringAppendElement(&tcl_cmd,
                                             Tcl_DStringValue(&list_tmp));
                    ReleaseTupleDesc(tupdesc);
                }
            }
            else
            {
                /**************************************************
                 * Single values are added as string element
                 * of their external representation
                 **************************************************/
                if (fcinfo->argnull[i])
                    Tcl_DStringAppendElement(&tcl_cmd, "");
                else
                {
                    char       *tmp;

                    tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
                                             fcinfo->arg[i]);
                    UTF_BEGIN;
                    Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
                    UTF_END;
                    pfree(tmp);
                }
            }
        }
    }
    PG_CATCH();
    {
        Tcl_DStringFree(&tcl_cmd);
        Tcl_DStringFree(&list_tmp);
        PG_RE_THROW();
    }
    PG_END_TRY();
    Tcl_DStringFree(&list_tmp);

    /************************************************************
     * Call the Tcl function
     *
     * We assume no PG error can be thrown directly from this call.
     ************************************************************/
    tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
    Tcl_DStringFree(&tcl_cmd);

    /************************************************************
     * Check for errors reported by Tcl.
     ************************************************************/
    if (tcl_rc != TCL_OK)
        throw_tcl_error(interp, prodesc->user_proname);

    /************************************************************
     * Disconnect from SPI manager and then create the return
     * value datum (if the input function does a palloc for it
     * this must not be allocated in the SPI memory context
     * because SPI_finish would free it).  But don't try to call
     * the result_in_func if we've been told to return a NULL;
     * the Tcl result may not be a valid value of the result type
     * in that case.
     ************************************************************/
    if (SPI_finish() != SPI_OK_FINISH)
        elog(ERROR, "SPI_finish() failed");

    if (fcinfo->isnull)
        retval = InputFunctionCall(&prodesc->result_in_func,
                                   NULL,
                                   prodesc->result_typioparam,
                                   -1);
    else
    {
        UTF_BEGIN;
        retval = InputFunctionCall(&prodesc->result_in_func,
                               UTF_U2E((char *) Tcl_GetStringResult(interp)),
                                   prodesc->result_typioparam,
                                   -1);
        UTF_END;
    }

    return retval;
}

static Datum pltcl_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
) [static]

Definition at line 624 of file pltcl.c.

References CALLED_AS_TRIGGER, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_func_handler(), pltcl_trigger_handler(), and PointerGetDatum.

Referenced by pltcl_call_handler(), and pltclu_call_handler().

{
    Datum       retval;
    FunctionCallInfo save_fcinfo;
    pltcl_proc_desc *save_prodesc;

    /*
     * Ensure that static pointers are saved/restored properly
     */
    save_fcinfo = pltcl_current_fcinfo;
    save_prodesc = pltcl_current_prodesc;

    PG_TRY();
    {
        /*
         * Determine if called as function or trigger and call appropriate
         * subhandler
         */
        if (CALLED_AS_TRIGGER(fcinfo))
        {
            pltcl_current_fcinfo = NULL;
            retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
        }
        else
        {
            pltcl_current_fcinfo = fcinfo;
            retval = pltcl_func_handler(fcinfo, pltrusted);
        }
    }
    PG_CATCH();
    {
        pltcl_current_fcinfo = save_fcinfo;
        pltcl_current_prodesc = save_prodesc;
        PG_RE_THROW();
    }
    PG_END_TRY();

    pltcl_current_fcinfo = save_fcinfo;
    pltcl_current_prodesc = save_prodesc;

    return retval;
}

static void pltcl_init_interp ( pltcl_interp_desc interp_desc,
bool  pltrusted 
) [static]

Definition at line 401 of file pltcl.c.

References elog, ERROR, pltcl_interp_desc::interp, NULL, pltcl_argisnull(), pltcl_elog(), pltcl_init_load_unknown(), pltcl_quote(), pltcl_returnnull(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_lastoid(), pltcl_SPI_prepare(), pltcl_interp_desc::query_hash, snprintf(), and pltcl_interp_desc::user_id.

Referenced by pltcl_fetch_interp().

{
    Tcl_Interp *interp;
    char        interpname[32];

    /************************************************************
     * Create the Tcl interpreter as a slave of pltcl_hold_interp.
     * Note: Tcl automatically does Tcl_Init in the untrusted case,
     * and it's not wanted in the trusted case.
     ************************************************************/
    snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
    if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
                                  pltrusted ? 1 : 0)) == NULL)
        elog(ERROR, "could not create slave Tcl interpreter");
    interp_desc->interp = interp;

    /************************************************************
     * Initialize the query hash table associated with interpreter
     ************************************************************/
    Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);

    /************************************************************
     * Install the commands for SPI support in the interpreter
     ************************************************************/
    Tcl_CreateCommand(interp, "elog",
                      pltcl_elog, NULL, NULL);
    Tcl_CreateCommand(interp, "quote",
                      pltcl_quote, NULL, NULL);
    Tcl_CreateCommand(interp, "argisnull",
                      pltcl_argisnull, NULL, NULL);
    Tcl_CreateCommand(interp, "return_null",
                      pltcl_returnnull, NULL, NULL);

    Tcl_CreateCommand(interp, "spi_exec",
                      pltcl_SPI_execute, NULL, NULL);
    Tcl_CreateCommand(interp, "spi_prepare",
                      pltcl_SPI_prepare, NULL, NULL);
    Tcl_CreateCommand(interp, "spi_execp",
                      pltcl_SPI_execute_plan, NULL, NULL);
    Tcl_CreateCommand(interp, "spi_lastoid",
                      pltcl_SPI_lastoid, NULL, NULL);

    /************************************************************
     * Try to load the unknown procedure from pltcl_modules
     ************************************************************/
    pltcl_init_load_unknown(interp);
}

static void pltcl_init_load_unknown ( Tcl_Interp *  interp  )  [static]

Definition at line 482 of file pltcl.c.

References AccessShareLock, buf, elog, ERROR, get_namespace_name(), i, makeRangeVar(), NULL, palloc(), pfree(), quote_qualified_identifier(), RelationData::rd_rel, relation_close(), relation_openrv_extended(), RelationGetNamespace, RelationGetRelationName, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_VIEW, snprintf(), SPI_execute(), SPI_fnumber(), SPI_freetuptable(), SPI_getvalue(), SPI_OK_SELECT, SPI_processed, SPI_tuptable, superuser_arg(), Tcl_GetStringResult, SPITupleTable::tupdesc, UTF_E2U, UTF_U2E, SPITupleTable::vals, and WARNING.

Referenced by pltcl_init_interp().

{
    Relation    pmrel;
    char       *pmrelname,
               *nspname;
    char       *buf;
    int         buflen;
    int         spi_rc;
    int         tcl_rc;
    Tcl_DString unknown_src;
    char       *part;
    int         i;
    int         fno;

    /************************************************************
     * Check if table pltcl_modules exists
     *
     * We allow the table to be found anywhere in the search_path.
     * This is for backwards compatibility.  To ensure that the table
     * is trustworthy, we require it to be owned by a superuser.
     ************************************************************/
    pmrel = relation_openrv_extended(makeRangeVar(NULL, "pltcl_modules", -1),
                                     AccessShareLock, true);
    if (pmrel == NULL)
        return;
    /* must be table or view, else ignore */
    if (!(pmrel->rd_rel->relkind == RELKIND_RELATION ||
          pmrel->rd_rel->relkind == RELKIND_MATVIEW ||
          pmrel->rd_rel->relkind == RELKIND_VIEW))
    {
        relation_close(pmrel, AccessShareLock);
        return;
    }
    /* must be owned by superuser, else ignore */
    if (!superuser_arg(pmrel->rd_rel->relowner))
    {
        relation_close(pmrel, AccessShareLock);
        return;
    }
    /* get fully qualified table name for use in select command */
    nspname = get_namespace_name(RelationGetNamespace(pmrel));
    if (!nspname)
        elog(ERROR, "cache lookup failed for namespace %u",
             RelationGetNamespace(pmrel));
    pmrelname = quote_qualified_identifier(nspname,
                                           RelationGetRelationName(pmrel));

    /************************************************************
     * Read all the rows from it where modname = 'unknown',
     * in the order of modseq
     ************************************************************/
    buflen = strlen(pmrelname) + 100;
    buf = (char *) palloc(buflen);
    snprintf(buf, buflen,
           "select modsrc from %s where modname = 'unknown' order by modseq",
             pmrelname);

    spi_rc = SPI_execute(buf, false, 0);
    if (spi_rc != SPI_OK_SELECT)
        elog(ERROR, "select from pltcl_modules failed");

    pfree(buf);

    /************************************************************
     * If there's nothing, module unknown doesn't exist
     ************************************************************/
    if (SPI_processed == 0)
    {
        SPI_freetuptable(SPI_tuptable);
        elog(WARNING, "module \"unknown\" not found in pltcl_modules");
        relation_close(pmrel, AccessShareLock);
        return;
    }

    /************************************************************
     * There is a module named unknown. Reassemble the
     * source from the modsrc attributes and evaluate
     * it in the Tcl interpreter
     ************************************************************/
    fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");

    Tcl_DStringInit(&unknown_src);

    for (i = 0; i < SPI_processed; i++)
    {
        part = SPI_getvalue(SPI_tuptable->vals[i],
                            SPI_tuptable->tupdesc, fno);
        if (part != NULL)
        {
            UTF_BEGIN;
            Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
            UTF_END;
            pfree(part);
        }
    }
    tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));

    Tcl_DStringFree(&unknown_src);
    SPI_freetuptable(SPI_tuptable);

    if (tcl_rc != TCL_OK)
    {
        UTF_BEGIN;
        elog(ERROR, "could not load module \"unknown\": %s",
             UTF_U2E(Tcl_GetStringResult(interp)));
        UTF_END;
    }

    relation_close(pmrel, AccessShareLock);
}

static int pltcl_process_SPI_result ( Tcl_Interp *  interp,
CONST84 char *  arrayname,
CONST84 char *  loop_body,
int  spi_rc,
SPITupleTable tuptable,
int  ntuples 
) [static]

Definition at line 1927 of file pltcl.c.

References buf, i, NULL, pltcl_set_tuple_values(), snprintf(), SPI_freetuptable(), SPI_OK_DELETE, SPI_OK_DELETE_RETURNING, SPI_OK_INSERT, SPI_OK_INSERT_RETURNING, SPI_OK_REWRITTEN, SPI_OK_SELECT, SPI_OK_SELINTO, SPI_OK_UPDATE, SPI_OK_UPDATE_RETURNING, SPI_OK_UTILITY, SPI_result_code_string(), SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by pltcl_SPI_execute(), and pltcl_SPI_execute_plan().

{
    int         my_rc = TCL_OK;
    char        buf[64];
    int         i;
    int         loop_rc;
    HeapTuple  *tuples;
    TupleDesc   tupdesc;

    switch (spi_rc)
    {
        case SPI_OK_SELINTO:
        case SPI_OK_INSERT:
        case SPI_OK_DELETE:
        case SPI_OK_UPDATE:
            snprintf(buf, sizeof(buf), "%d", ntuples);
            Tcl_SetResult(interp, buf, TCL_VOLATILE);
            break;

        case SPI_OK_UTILITY:
        case SPI_OK_REWRITTEN:
            if (tuptable == NULL)
            {
                Tcl_SetResult(interp, "0", TCL_STATIC);
                break;
            }
            /* FALL THRU for utility returning tuples */

        case SPI_OK_SELECT:
        case SPI_OK_INSERT_RETURNING:
        case SPI_OK_DELETE_RETURNING:
        case SPI_OK_UPDATE_RETURNING:

            /*
             * Process the tuples we got
             */
            tuples = tuptable->vals;
            tupdesc = tuptable->tupdesc;

            if (loop_body == NULL)
            {
                /*
                 * If there is no loop body given, just set the variables from
                 * the first tuple (if any)
                 */
                if (ntuples > 0)
                    pltcl_set_tuple_values(interp, arrayname, 0,
                                           tuples[0], tupdesc);
            }
            else
            {
                /*
                 * There is a loop body - process all tuples and evaluate the
                 * body on each
                 */
                for (i = 0; i < ntuples; i++)
                {
                    pltcl_set_tuple_values(interp, arrayname, i,
                                           tuples[i], tupdesc);

                    loop_rc = Tcl_Eval(interp, loop_body);

                    if (loop_rc == TCL_OK)
                        continue;
                    if (loop_rc == TCL_CONTINUE)
                        continue;
                    if (loop_rc == TCL_RETURN)
                    {
                        my_rc = TCL_RETURN;
                        break;
                    }
                    if (loop_rc == TCL_BREAK)
                        break;
                    my_rc = TCL_ERROR;
                    break;
                }
            }

            if (my_rc == TCL_OK)
            {
                snprintf(buf, sizeof(buf), "%d", ntuples);
                Tcl_SetResult(interp, buf, TCL_VOLATILE);
            }
            break;

        default:
            Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
                             SPI_result_code_string(spi_rc), NULL);
            my_rc = TCL_ERROR;
            break;
    }

    SPI_freetuptable(tuptable);

    return my_rc;
}

static int pltcl_quote ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 1595 of file pltcl.c.

References palloc(), and pfree().

Referenced by pltcl_init_interp().

{
    char       *tmp;
    const char *cp1;
    char       *cp2;

    /************************************************************
     * Check call syntax
     ************************************************************/
    if (argc != 2)
    {
        Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Allocate space for the maximum the string can
     * grow to and initialize pointers
     ************************************************************/
    tmp = palloc(strlen(argv[1]) * 2 + 1);
    cp1 = argv[1];
    cp2 = tmp;

    /************************************************************
     * Walk through string and double every quote and backslash
     ************************************************************/
    while (*cp1)
    {
        if (*cp1 == '\'')
            *cp2++ = '\'';
        else
        {
            if (*cp1 == '\\')
                *cp2++ = '\\';
        }
        *cp2++ = *cp1++;
    }

    /************************************************************
     * Terminate the string and set it as result
     ************************************************************/
    *cp2 = '\0';
    Tcl_SetResult(interp, tmp, TCL_VOLATILE);
    pfree(tmp);
    return TCL_OK;
}

static int pltcl_returnnull ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 1706 of file pltcl.c.

References FunctionCallInfoData::isnull, and NULL.

Referenced by pltcl_init_interp().

{
    FunctionCallInfo fcinfo = pltcl_current_fcinfo;

    /************************************************************
     * Check call syntax
     ************************************************************/
    if (argc != 1)
    {
        Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Check that we're called as a normal function
     ************************************************************/
    if (fcinfo == NULL)
    {
        Tcl_SetResult(interp, "return_null cannot be used in triggers",
                      TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Set the NULL return flag and cause Tcl to return from the
     * procedure.
     ************************************************************/
    fcinfo->isnull = true;

    return TCL_RETURN;
}

static void pltcl_set_tuple_values ( Tcl_Interp *  interp,
CONST84 char *  arrayname,
int  tupno,
HeapTuple  tuple,
TupleDesc  tupdesc 
) [static]

Definition at line 2396 of file pltcl.c.

References tupleDesc::attrs, buf, elog, ERROR, GETSTRUCT, heap_getattr, HeapTupleIsValid, i, NameStr, tupleDesc::natts, NULL, ObjectIdGetDatum, OidIsValid, OidOutputFunctionCall(), pfree(), ReleaseSysCache(), SearchSysCache1, snprintf(), TYPEOID, and UTF_E2U.

Referenced by pltcl_process_SPI_result().

{
    int         i;
    char       *outputstr;
    char        buf[64];
    Datum       attr;
    bool        isnull;

    CONST84 char *attname;
    HeapTuple   typeTup;
    Oid         typoutput;

    CONST84 char **arrptr;
    CONST84 char **nameptr;
    CONST84 char *nullname = NULL;

    /************************************************************
     * Prepare pointers for Tcl_SetVar2() below and in array
     * mode set the .tupno element
     ************************************************************/
    if (arrayname == NULL)
    {
        arrptr = &attname;
        nameptr = &nullname;
    }
    else
    {
        arrptr = &arrayname;
        nameptr = &attname;
        snprintf(buf, sizeof(buf), "%d", tupno);
        Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
    }

    for (i = 0; i < tupdesc->natts; i++)
    {
        /* ignore dropped attributes */
        if (tupdesc->attrs[i]->attisdropped)
            continue;

        /************************************************************
         * Get the attribute name
         ************************************************************/
        attname = NameStr(tupdesc->attrs[i]->attname);

        /************************************************************
         * Get the attributes value
         ************************************************************/
        attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);

        /************************************************************
         * Lookup the attribute type in the syscache
         * for the output function
         ************************************************************/
        typeTup = SearchSysCache1(TYPEOID,
                              ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
        if (!HeapTupleIsValid(typeTup))
            elog(ERROR, "cache lookup failed for type %u",
                 tupdesc->attrs[i]->atttypid);

        typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
        ReleaseSysCache(typeTup);

        /************************************************************
         * If there is a value, set the variable
         * If not, unset it
         *
         * Hmmm - Null attributes will cause functions to
         *        crash if they don't expect them - need something
         *        smarter here.
         ************************************************************/
        if (!isnull && OidIsValid(typoutput))
        {
            outputstr = OidOutputFunctionCall(typoutput, attr);
            UTF_BEGIN;
            Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
            UTF_END;
            pfree(outputstr);
        }
        else
            Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
    }
}

static int pltcl_SPI_execute ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 1824 of file pltcl.c.

References CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_process_SPI_result(), pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), SPI_execute(), SPI_processed, SPI_tuptable, usage(), and UTF_U2E.

Referenced by pltcl_init_interp().

{
    int         my_rc;
    int         spi_rc;
    int         query_idx;
    int         i;
    int         count = 0;
    CONST84 char *volatile arrayname = NULL;
    CONST84 char *volatile loop_body = NULL;
    MemoryContext oldcontext = CurrentMemoryContext;
    ResourceOwner oldowner = CurrentResourceOwner;

    char       *usage = "syntax error - 'SPI_exec "
    "?-count n? "
    "?-array name? query ?loop body?";

    /************************************************************
     * Check the call syntax and get the options
     ************************************************************/
    if (argc < 2)
    {
        Tcl_SetResult(interp, usage, TCL_STATIC);
        return TCL_ERROR;
    }

    i = 1;
    while (i < argc)
    {
        if (strcmp(argv[i], "-array") == 0)
        {
            if (++i >= argc)
            {
                Tcl_SetResult(interp, usage, TCL_STATIC);
                return TCL_ERROR;
            }
            arrayname = argv[i++];
            continue;
        }

        if (strcmp(argv[i], "-count") == 0)
        {
            if (++i >= argc)
            {
                Tcl_SetResult(interp, usage, TCL_STATIC);
                return TCL_ERROR;
            }
            if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
                return TCL_ERROR;
            continue;
        }

        break;
    }

    query_idx = i;
    if (query_idx >= argc || query_idx + 2 < argc)
    {
        Tcl_SetResult(interp, usage, TCL_STATIC);
        return TCL_ERROR;
    }
    if (query_idx + 1 < argc)
        loop_body = argv[query_idx + 1];

    /************************************************************
     * Execute the query inside a sub-transaction, so we can cope with
     * errors sanely
     ************************************************************/

    pltcl_subtrans_begin(oldcontext, oldowner);

    PG_TRY();
    {
        UTF_BEGIN;
        spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
                             pltcl_current_prodesc->fn_readonly, count);
        UTF_END;

        my_rc = pltcl_process_SPI_result(interp,
                                         arrayname,
                                         loop_body,
                                         spi_rc,
                                         SPI_tuptable,
                                         SPI_processed);

        pltcl_subtrans_commit(oldcontext, oldowner);
    }
    PG_CATCH();
    {
        pltcl_subtrans_abort(interp, oldcontext, oldowner);
        return TCL_ERROR;
    }
    PG_END_TRY();

    return my_rc;
}

static int pltcl_SPI_execute_plan ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 2162 of file pltcl.c.

References pltcl_query_desc::arginfuncs, pltcl_query_desc::argtypioparams, CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, InputFunctionCall(), pltcl_proc_desc::interp_desc, pltcl_query_desc::nargs, NULL, palloc(), PG_CATCH, PG_END_TRY, PG_TRY, pltcl_query_desc::plan, pltcl_process_SPI_result(), pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), pltcl_interp_desc::query_hash, SPI_execute_plan(), SPI_processed, SPI_tuptable, usage(), and UTF_U2E.

Referenced by pltcl_init_interp().

{
    int         my_rc;
    int         spi_rc;
    int         i;
    int         j;
    Tcl_HashEntry *hashent;
    pltcl_query_desc *qdesc;
    const char *volatile nulls = NULL;
    CONST84 char *volatile arrayname = NULL;
    CONST84 char *volatile loop_body = NULL;
    int         count = 0;
    int         callnargs;
    CONST84 char **callargs = NULL;
    Datum      *argvalues;
    MemoryContext oldcontext = CurrentMemoryContext;
    ResourceOwner oldowner = CurrentResourceOwner;
    Tcl_HashTable *query_hash;

    char       *usage = "syntax error - 'SPI_execp "
    "?-nulls string? ?-count n? "
    "?-array name? query ?args? ?loop body?";

    /************************************************************
     * Get the options and check syntax
     ************************************************************/
    i = 1;
    while (i < argc)
    {
        if (strcmp(argv[i], "-array") == 0)
        {
            if (++i >= argc)
            {
                Tcl_SetResult(interp, usage, TCL_STATIC);
                return TCL_ERROR;
            }
            arrayname = argv[i++];
            continue;
        }
        if (strcmp(argv[i], "-nulls") == 0)
        {
            if (++i >= argc)
            {
                Tcl_SetResult(interp, usage, TCL_STATIC);
                return TCL_ERROR;
            }
            nulls = argv[i++];
            continue;
        }
        if (strcmp(argv[i], "-count") == 0)
        {
            if (++i >= argc)
            {
                Tcl_SetResult(interp, usage, TCL_STATIC);
                return TCL_ERROR;
            }
            if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
                return TCL_ERROR;
            continue;
        }

        break;
    }

    /************************************************************
     * Get the prepared plan descriptor by its key
     ************************************************************/
    if (i >= argc)
    {
        Tcl_SetResult(interp, usage, TCL_STATIC);
        return TCL_ERROR;
    }

    query_hash = &pltcl_current_prodesc->interp_desc->query_hash;

    hashent = Tcl_FindHashEntry(query_hash, argv[i]);
    if (hashent == NULL)
    {
        Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL);
        return TCL_ERROR;
    }
    qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
    i++;

    /************************************************************
     * If a nulls string is given, check for correct length
     ************************************************************/
    if (nulls != NULL)
    {
        if (strlen(nulls) != qdesc->nargs)
        {
            Tcl_SetResult(interp,
                       "length of nulls string doesn't match # of arguments",
                          TCL_STATIC);
            return TCL_ERROR;
        }
    }

    /************************************************************
     * If there was a argtype list on preparation, we need
     * an argument value list now
     ************************************************************/
    if (qdesc->nargs > 0)
    {
        if (i >= argc)
        {
            Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
            return TCL_ERROR;
        }

        /************************************************************
         * Split the argument values
         ************************************************************/
        if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
            return TCL_ERROR;

        /************************************************************
         * Check that the # of arguments matches
         ************************************************************/
        if (callnargs != qdesc->nargs)
        {
            Tcl_SetResult(interp,
               "argument list length doesn't match # of arguments for query",
                          TCL_STATIC);
            ckfree((char *) callargs);
            return TCL_ERROR;
        }
    }
    else
        callnargs = 0;

    /************************************************************
     * Get loop body if present
     ************************************************************/
    if (i < argc)
        loop_body = argv[i++];

    if (i != argc)
    {
        Tcl_SetResult(interp, usage, TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Execute the plan inside a sub-transaction, so we can cope with
     * errors sanely
     ************************************************************/

    pltcl_subtrans_begin(oldcontext, oldowner);

    PG_TRY();
    {
        /************************************************************
         * Setup the value array for SPI_execute_plan() using
         * the type specific input functions
         ************************************************************/
        argvalues = (Datum *) palloc(callnargs * sizeof(Datum));

        for (j = 0; j < callnargs; j++)
        {
            if (nulls && nulls[j] == 'n')
            {
                argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
                                                 NULL,
                                                 qdesc->argtypioparams[j],
                                                 -1);
            }
            else
            {
                UTF_BEGIN;
                argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
                                               (char *) UTF_U2E(callargs[j]),
                                                 qdesc->argtypioparams[j],
                                                 -1);
                UTF_END;
            }
        }

        if (callargs)
            ckfree((char *) callargs);
        callargs = NULL;

        /************************************************************
         * Execute the plan
         ************************************************************/
        spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
                                  pltcl_current_prodesc->fn_readonly, count);

        my_rc = pltcl_process_SPI_result(interp,
                                         arrayname,
                                         loop_body,
                                         spi_rc,
                                         SPI_tuptable,
                                         SPI_processed);

        pltcl_subtrans_commit(oldcontext, oldowner);
    }
    PG_CATCH();
    {
        pltcl_subtrans_abort(interp, oldcontext, oldowner);

        if (callargs)
            ckfree((char *) callargs);

        return TCL_ERROR;
    }
    PG_END_TRY();

    return my_rc;
}

static int pltcl_SPI_lastoid ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 2380 of file pltcl.c.

References buf, snprintf(), and SPI_lastoid.

Referenced by pltcl_init_interp().

{
    char        buf[64];

    snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
    Tcl_SetResult(interp, buf, TCL_VOLATILE);
    return TCL_OK;
}

static int pltcl_SPI_prepare ( ClientData  cdata,
Tcl_Interp *  interp,
int  argc,
CONST84 char *  argv[] 
) [static]

Definition at line 2039 of file pltcl.c.

References pltcl_query_desc::arginfuncs, pltcl_query_desc::argtypes, pltcl_query_desc::argtypioparams, CurrentMemoryContext, CurrentResourceOwner, elog, ERROR, free, getTypeInputInfo(), i, pltcl_proc_desc::interp_desc, malloc, pltcl_query_desc::nargs, NULL, parseTypeString(), perm_fmgr_info(), PG_CATCH, PG_END_TRY, PG_TRY, pltcl_query_desc::plan, pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), pltcl_query_desc::qname, pltcl_interp_desc::query_hash, snprintf(), SPI_keepplan(), SPI_prepare(), and UTF_U2E.

Referenced by pltcl_init_interp().

{
    int         nargs;
    CONST84 char **args;
    pltcl_query_desc *qdesc;
    int         i;
    Tcl_HashEntry *hashent;
    int         hashnew;
    Tcl_HashTable *query_hash;
    MemoryContext oldcontext = CurrentMemoryContext;
    ResourceOwner oldowner = CurrentResourceOwner;

    /************************************************************
     * Check the call syntax
     ************************************************************/
    if (argc != 3)
    {
        Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
                      TCL_STATIC);
        return TCL_ERROR;
    }

    /************************************************************
     * Split the argument type list
     ************************************************************/
    if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
        return TCL_ERROR;

    /************************************************************
     * Allocate the new querydesc structure
     ************************************************************/
    qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
    snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
    qdesc->nargs = nargs;
    qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
    qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
    qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));

    /************************************************************
     * Execute the prepare inside a sub-transaction, so we can cope with
     * errors sanely
     ************************************************************/

    pltcl_subtrans_begin(oldcontext, oldowner);

    PG_TRY();
    {
        /************************************************************
         * Resolve argument type names and then look them up by oid
         * in the system cache, and remember the required information
         * for input conversion.
         ************************************************************/
        for (i = 0; i < nargs; i++)
        {
            Oid         typId,
                        typInput,
                        typIOParam;
            int32       typmod;

            parseTypeString(args[i], &typId, &typmod);

            getTypeInputInfo(typId, &typInput, &typIOParam);

            qdesc->argtypes[i] = typId;
            perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
            qdesc->argtypioparams[i] = typIOParam;
        }

        /************************************************************
         * Prepare the plan and check for errors
         ************************************************************/
        UTF_BEGIN;
        qdesc->plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
        UTF_END;

        if (qdesc->plan == NULL)
            elog(ERROR, "SPI_prepare() failed");

        /************************************************************
         * Save the plan into permanent memory (right now it's in the
         * SPI procCxt, which will go away at function end).
         ************************************************************/
        if (SPI_keepplan(qdesc->plan))
            elog(ERROR, "SPI_keepplan() failed");

        pltcl_subtrans_commit(oldcontext, oldowner);
    }
    PG_CATCH();
    {
        pltcl_subtrans_abort(interp, oldcontext, oldowner);

        free(qdesc->argtypes);
        free(qdesc->arginfuncs);
        free(qdesc->argtypioparams);
        free(qdesc);
        ckfree((char *) args);

        return TCL_ERROR;
    }
    PG_END_TRY();

    /************************************************************
     * Insert a hashtable entry for the plan and return
     * the key to the caller
     ************************************************************/
    query_hash = &pltcl_current_prodesc->interp_desc->query_hash;

    hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
    Tcl_SetHashValue(hashent, (ClientData) qdesc);

    ckfree((char *) args);

    /* qname is ASCII, so no need for encoding conversion */
    Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
    return TCL_OK;
}

static void pltcl_subtrans_abort ( Tcl_Interp *  interp,
MemoryContext  oldcontext,
ResourceOwner  oldowner 
) [static]

Definition at line 1789 of file pltcl.c.

References CopyErrorData(), CurrentResourceOwner, FlushErrorState(), FreeErrorData(), MemoryContextSwitchTo(), ErrorData::message, RollbackAndReleaseCurrentSubTransaction(), SPI_restore_connection(), and UTF_E2U.

Referenced by pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

{
    ErrorData  *edata;

    /* Save error info */
    MemoryContextSwitchTo(oldcontext);
    edata = CopyErrorData();
    FlushErrorState();

    /* Abort the inner transaction */
    RollbackAndReleaseCurrentSubTransaction();
    MemoryContextSwitchTo(oldcontext);
    CurrentResourceOwner = oldowner;

    /*
     * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
     * have left us in a disconnected state.  We need this hack to return to
     * connected state.
     */
    SPI_restore_connection();

    /* Pass the error message to Tcl */
    UTF_BEGIN;
    Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
    UTF_END;
    FreeErrorData(edata);
}

static void pltcl_subtrans_begin ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
) [static]

Definition at line 1765 of file pltcl.c.

References BeginInternalSubTransaction(), MemoryContextSwitchTo(), and NULL.

Referenced by pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

{
    BeginInternalSubTransaction(NULL);

    /* Want to run inside function's memory context */
    MemoryContextSwitchTo(oldcontext);
}

static void pltcl_subtrans_commit ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
) [static]

Definition at line 1774 of file pltcl.c.

References CurrentResourceOwner, MemoryContextSwitchTo(), ReleaseCurrentSubTransaction(), and SPI_restore_connection().

Referenced by pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

{
    /* Commit the inner transaction, return to outer xact context */
    ReleaseCurrentSubTransaction();
    MemoryContextSwitchTo(oldcontext);
    CurrentResourceOwner = oldowner;

    /*
     * AtEOSubXact_SPI() should not have popped any SPI context, but just in
     * case it did, make sure we remain connected.
     */
    SPI_restore_connection();
}

static HeapTuple pltcl_trigger_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
) [static]

Definition at line 820 of file pltcl.c.

References compile_pltcl_function(), DatumGetCString, DirectFunctionCall1, elog, ERROR, fmgr_info(), GETSTRUCT, getTypeIOParam(), HeapTupleIsValid, i, InputFunctionCall(), pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, NameStr, NULL, ObjectIdGetDatum, oidout(), palloc(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_build_tuple_argument(), RelationData::rd_att, RelationData::rd_id, RelationGetRelid, ReleaseSysCache(), ret_value, SearchSysCache1, SPI_connect(), SPI_ERROR_NOATTRIBUTE, SPI_finish(), SPI_fnumber(), SPI_getnspname(), SPI_getrelname(), SPI_modifytuple(), SPI_OK_FINISH, SPI_result, Tcl_GetStringResult, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgname, Trigger::tgnargs, throw_tcl_error(), TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_TRUNCATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TRIGGER_FIRED_FOR_STATEMENT, TRIGGER_FIRED_INSTEAD, TYPEOID, pltcl_proc_desc::user_proname, and UTF_U2E.

Referenced by pltcl_handler().

{
    pltcl_proc_desc *prodesc;
    Tcl_Interp *volatile interp;
    TriggerData *trigdata = (TriggerData *) fcinfo->context;
    char       *stroid;
    TupleDesc   tupdesc;
    volatile HeapTuple rettup;
    Tcl_DString tcl_cmd;
    Tcl_DString tcl_trigtup;
    Tcl_DString tcl_newtup;
    int         tcl_rc;
    int         i;
    int        *modattrs;
    Datum      *modvalues;
    char       *modnulls;
    int         ret_numvals;
    CONST84 char *result;
    CONST84 char **ret_values;

    /* Connect to SPI manager */
    if (SPI_connect() != SPI_OK_CONNECT)
        elog(ERROR, "could not connect to SPI manager");

    /* Find or compile the function */
    prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
                                     RelationGetRelid(trigdata->tg_relation),
                                     pltrusted);

    pltcl_current_prodesc = prodesc;

    interp = prodesc->interp_desc->interp;

    tupdesc = trigdata->tg_relation->rd_att;

    /************************************************************
     * Create the tcl command to call the internal
     * proc in the interpreter
     ************************************************************/
    Tcl_DStringInit(&tcl_cmd);
    Tcl_DStringInit(&tcl_trigtup);
    Tcl_DStringInit(&tcl_newtup);
    PG_TRY();
    {
        /* The procedure name */
        Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);

        /* The trigger name for argument TG_name */
        Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);

        /* The oid of the trigger relation for argument TG_relid */
        stroid = DatumGetCString(DirectFunctionCall1(oidout,
                            ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
        Tcl_DStringAppendElement(&tcl_cmd, stroid);
        pfree(stroid);

        /* The name of the table the trigger is acting on: TG_table_name */
        stroid = SPI_getrelname(trigdata->tg_relation);
        Tcl_DStringAppendElement(&tcl_cmd, stroid);
        pfree(stroid);

        /* The schema of the table the trigger is acting on: TG_table_schema */
        stroid = SPI_getnspname(trigdata->tg_relation);
        Tcl_DStringAppendElement(&tcl_cmd, stroid);
        pfree(stroid);

        /* A list of attribute names for argument TG_relatts */
        Tcl_DStringAppendElement(&tcl_trigtup, "");
        for (i = 0; i < tupdesc->natts; i++)
        {
            if (tupdesc->attrs[i]->attisdropped)
                Tcl_DStringAppendElement(&tcl_trigtup, "");
            else
                Tcl_DStringAppendElement(&tcl_trigtup,
                                         NameStr(tupdesc->attrs[i]->attname));
        }
        Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
        Tcl_DStringFree(&tcl_trigtup);
        Tcl_DStringInit(&tcl_trigtup);

        /* The when part of the event for TG_when */
        if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
            Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
        else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
            Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
        else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
            Tcl_DStringAppendElement(&tcl_cmd, "INSTEAD OF");
        else
            elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);

        /* The level part of the event for TG_level */
        if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
        {
            Tcl_DStringAppendElement(&tcl_cmd, "ROW");

            /* Build the data list for the trigtuple */
            pltcl_build_tuple_argument(trigdata->tg_trigtuple,
                                       tupdesc, &tcl_trigtup);

            /*
             * Now the command part of the event for TG_op and data for NEW
             * and OLD
             */
            if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
            {
                Tcl_DStringAppendElement(&tcl_cmd, "INSERT");

                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
                Tcl_DStringAppendElement(&tcl_cmd, "");

                rettup = trigdata->tg_trigtuple;
            }
            else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
            {
                Tcl_DStringAppendElement(&tcl_cmd, "DELETE");

                Tcl_DStringAppendElement(&tcl_cmd, "");
                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));

                rettup = trigdata->tg_trigtuple;
            }
            else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
            {
                Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");

                pltcl_build_tuple_argument(trigdata->tg_newtuple,
                                           tupdesc, &tcl_newtup);

                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));

                rettup = trigdata->tg_newtuple;
            }
            else
                elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
        }
        else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
        {
            Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");

            if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
                Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
            else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
                Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
            else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
                Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
            else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
                Tcl_DStringAppendElement(&tcl_cmd, "TRUNCATE");
            else
                elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);

            Tcl_DStringAppendElement(&tcl_cmd, "");
            Tcl_DStringAppendElement(&tcl_cmd, "");

            rettup = (HeapTuple) NULL;
        }
        else
            elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);

        /* Finally append the arguments from CREATE TRIGGER */
        for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
            Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);

    }
    PG_CATCH();
    {
        Tcl_DStringFree(&tcl_cmd);
        Tcl_DStringFree(&tcl_trigtup);
        Tcl_DStringFree(&tcl_newtup);
        PG_RE_THROW();
    }
    PG_END_TRY();
    Tcl_DStringFree(&tcl_trigtup);
    Tcl_DStringFree(&tcl_newtup);

    /************************************************************
     * Call the Tcl function
     *
     * We assume no PG error can be thrown directly from this call.
     ************************************************************/
    tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
    Tcl_DStringFree(&tcl_cmd);

    /************************************************************
     * Check for errors reported by Tcl.
     ************************************************************/
    if (tcl_rc != TCL_OK)
        throw_tcl_error(interp, prodesc->user_proname);

    /************************************************************
     * The return value from the procedure might be one of
     * the magic strings OK or SKIP or a list from array get.
     * We can check for OK or SKIP without worrying about encoding.
     ************************************************************/
    if (SPI_finish() != SPI_OK_FINISH)
        elog(ERROR, "SPI_finish() failed");

    result = Tcl_GetStringResult(interp);

    if (strcmp(result, "OK") == 0)
        return rettup;
    if (strcmp(result, "SKIP") == 0)
        return (HeapTuple) NULL;

    /************************************************************
     * Convert the result value from the Tcl interpreter
     * and setup structures for SPI_modifytuple();
     ************************************************************/
    if (Tcl_SplitList(interp, result,
                      &ret_numvals, &ret_values) != TCL_OK)
    {
        UTF_BEGIN;
        elog(ERROR, "could not split return value from trigger: %s",
             UTF_U2E(Tcl_GetStringResult(interp)));
        UTF_END;
    }

    /* Use a TRY to ensure ret_values will get freed */
    PG_TRY();
    {
        if (ret_numvals % 2 != 0)
            elog(ERROR, "invalid return list from trigger - must have even # of elements");

        modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
        modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
        for (i = 0; i < tupdesc->natts; i++)
        {
            modattrs[i] = i + 1;
            modvalues[i] = (Datum) NULL;
        }

        modnulls = palloc(tupdesc->natts);
        memset(modnulls, 'n', tupdesc->natts);

        for (i = 0; i < ret_numvals; i += 2)
        {
            CONST84 char *ret_name = ret_values[i];
            CONST84 char *ret_value = ret_values[i + 1];
            int         attnum;
            HeapTuple   typeTup;
            Oid         typinput;
            Oid         typioparam;
            FmgrInfo    finfo;

            /************************************************************
             * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
             ************************************************************/
            if (strcmp(ret_name, ".tupno") == 0)
                continue;

            /************************************************************
             * Get the attribute number
             ************************************************************/
            attnum = SPI_fnumber(tupdesc, ret_name);
            if (attnum == SPI_ERROR_NOATTRIBUTE)
                elog(ERROR, "invalid attribute \"%s\"", ret_name);
            if (attnum <= 0)
                elog(ERROR, "cannot set system attribute \"%s\"", ret_name);

            /************************************************************
             * Ignore dropped columns
             ************************************************************/
            if (tupdesc->attrs[attnum - 1]->attisdropped)
                continue;

            /************************************************************
             * Lookup the attribute type in the syscache
             * for the input function
             ************************************************************/
            typeTup = SearchSysCache1(TYPEOID,
                     ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid));
            if (!HeapTupleIsValid(typeTup))
                elog(ERROR, "cache lookup failed for type %u",
                     tupdesc->attrs[attnum - 1]->atttypid);
            typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
            typioparam = getTypeIOParam(typeTup);
            ReleaseSysCache(typeTup);

            /************************************************************
             * Set the attribute to NOT NULL and convert the contents
             ************************************************************/
            modnulls[attnum - 1] = ' ';
            fmgr_info(typinput, &finfo);
            UTF_BEGIN;
            modvalues[attnum - 1] = InputFunctionCall(&finfo,
                                                 (char *) UTF_U2E(ret_value),
                                                      typioparam,
                                      tupdesc->attrs[attnum - 1]->atttypmod);
            UTF_END;
        }

        rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
                                 modattrs, modvalues, modnulls);

        pfree(modattrs);
        pfree(modvalues);
        pfree(modnulls);

        if (rettup == NULL)
            elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);

    }
    PG_CATCH();
    {
        ckfree((char *) ret_values);
        PG_RE_THROW();
    }
    PG_END_TRY();
    ckfree((char *) ret_values);

    return rettup;
}

Datum pltclu_call_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 617 of file pltcl.c.

References pltcl_handler().

{
    return pltcl_handler(fcinfo, false);
}

static void throw_tcl_error ( Tcl_Interp *  interp,
const char *  proname 
) [static]

Definition at line 1138 of file pltcl.c.

References ereport, errcontext, errmsg(), ERROR, pstrdup(), Tcl_GetStringResult, and UTF_U2E.

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

{
    /*
     * Caution is needed here because Tcl_GetVar could overwrite the
     * interpreter result (even though it's not really supposed to), and we
     * can't control the order of evaluation of ereport arguments. Hence, make
     * real sure we have our own copy of the result string before invoking
     * Tcl_GetVar.
     */
    char       *emsg;
    char       *econtext;

    UTF_BEGIN;
    emsg = pstrdup(UTF_U2E(Tcl_GetStringResult(interp)));
    UTF_END;
    UTF_BEGIN;
    econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
                                           TCL_GLOBAL_ONLY));
    ereport(ERROR,
            (errmsg("%s", emsg),
             errcontext("%s\nin PL/Tcl function \"%s\"",
                        econtext, proname)));
    UTF_END;
}


Variable Documentation

Definition at line 90 of file pltcl.c.

Definition at line 184 of file pltcl.c.

Definition at line 185 of file pltcl.c.

Tcl_Interp* pltcl_hold_interp = NULL [static]

Definition at line 179 of file pltcl.c.

HTAB* pltcl_interp_htab = NULL [static]

Definition at line 180 of file pltcl.c.

bool pltcl_pm_init_done = false [static]

Definition at line 178 of file pltcl.c.

HTAB* pltcl_proc_htab = NULL [static]

Definition at line 181 of file pltcl.c.