#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"
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_desc * | pltcl_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_desc * | compile_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 HTAB * | pltcl_interp_htab = NULL |
static HTAB * | pltcl_proc_htab = NULL |
static FunctionCallInfo | pltcl_current_fcinfo = NULL |
static pltcl_proc_desc * | pltcl_current_prodesc = NULL |
#define HAVE_TCL_VERSION | ( | maj, | ||
min | ||||
) |
#define Tcl_GetStringResult | ( | interp | ) | ((interp)->result) |
Definition at line 51 of file pltcl.c.
Referenced by compile_pltcl_function(), pltcl_func_handler(), pltcl_init_load_unknown(), pltcl_trigger_handler(), and throw_tcl_error().
#define TEXTDOMAIN PG_TEXTDOMAIN("pltcl") |
Definition at line 56 of file pltcl.c.
Referenced by _PG_init().
#define UTF_E2U | ( | x | ) | (x) |
Definition at line 87 of file pltcl.c.
Referenced by compile_pltcl_function(), pltcl_build_tuple_argument(), pltcl_elog(), pltcl_func_handler(), pltcl_init_load_unknown(), pltcl_set_tuple_values(), and pltcl_subtrans_abort().
#define UTF_U2E | ( | x | ) | (x) |
Definition at line 86 of file pltcl.c.
Referenced by compile_pltcl_function(), pltcl_elog(), pltcl_func_handler(), pltcl_init_load_unknown(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_prepare(), pltcl_trigger_handler(), and throw_tcl_error().
typedef struct pltcl_interp_desc pltcl_interp_desc |
typedef struct pltcl_proc_desc pltcl_proc_desc |
typedef struct pltcl_proc_key pltcl_proc_key |
typedef struct pltcl_proc_ptr pltcl_proc_ptr |
typedef struct pltcl_query_desc pltcl_query_desc |
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(¬ifier); } #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; }
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; }
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; }
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(); }
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; }
FunctionCallInfo pltcl_current_fcinfo = NULL [static] |
pltcl_proc_desc* pltcl_current_prodesc = NULL [static] |
Tcl_Interp* pltcl_hold_interp = NULL [static] |
HTAB* pltcl_interp_htab = NULL [static] |
bool pltcl_pm_init_done = false [static] |
HTAB* pltcl_proc_htab = NULL [static] |