From 79e6648649ad4bf6ef7112e9d97e35270397fb01 Mon Sep 17 00:00:00 2001 From: Jan Wieck Date: Wed, 19 Jul 2000 11:53:02 +0000 Subject: [PATCH] Added untrusted PL/TclU (pltclu) language. Executes all procedures in a non-safe interpreter, so with full OS access! Language is restricted to be used by DB superusers. Added "argisnull n" and "return_null" commands to gain full control over NULL values from new FMGR capabilities. Jan --- src/bin/scripts/createlang.sh | 25 ++- src/pl/tcl/pltcl.c | 369 +++++++++++++++++++++++----------- 2 files changed, 269 insertions(+), 125 deletions(-) diff --git a/src/bin/scripts/createlang.sh b/src/bin/scripts/createlang.sh index f2e7f60e12..daace97a44 100644 --- a/src/bin/scripts/createlang.sh +++ b/src/bin/scripts/createlang.sh @@ -8,7 +8,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createlang.sh,v 1.12 2000/05/28 17:56:08 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createlang.sh,v 1.13 2000/07/19 11:53:02 wieck Exp $ # #------------------------------------------------------------------------- @@ -180,18 +180,29 @@ fi # ---------- case "$langname" in plpgsql) - lancomp="PL/pgSQL" + lancomp="PL/pgSQL" trusted="TRUSTED " handler="plpgsql_call_handler" - ;; + object="plpgsql" + ;; pltcl) lancomp="PL/Tcl" trusted="TRUSTED " - handler="pltcl_call_handler";; + handler="pltcl_call_handler" + object="pltcl" + ;; + pltclu) + lancomp="PL/Tcl (untrusted)" + trusted="" + handler="pltclu_call_handler" + object="pltcl" + ;; plperl) lancomp="PL/Perl" trusted="TRUSTED " - handler="plperl_call_handler";; + handler="plperl_call_handler" + object="plperl" + ;; *) echo "$CMDNAME: unsupported language '$langname'" echo "Supported languages are 'plpgsql', 'pltcl', and 'plperl'." @@ -204,7 +215,7 @@ esac # Check that the shared object for the call handler is installed # in PGLIB # ---------- -if [ ! -f $PGLIB/${langname}__DLSUFFIX__ ]; then +if [ ! -f $PGLIB/${object}__DLSUFFIX__ ]; then echo "$CMDNAME: cannot find the file $PGLIB/${langname}__DLSUFFIX__" echo "" echo "This file contains the call handler for $lancomp. By default," @@ -244,7 +255,7 @@ fi # ---------- # Create the call handler and the language # ---------- -$PSQL "CREATE FUNCTION $handler () RETURNS OPAQUE AS '$PGLIB/${langname}__DLSUFFIX__' LANGUAGE 'newC'" +$PSQL "CREATE FUNCTION $handler () RETURNS OPAQUE AS '$PGLIB/${object}__DLSUFFIX__' LANGUAGE 'newC'" if [ $? -ne 0 ]; then echo "$CMDNAME: language installation failed" exit 1 diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 6bdeec9be8..0c2ec992b7 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -1,6 +1,6 @@ /********************************************************************** * pltcl.c - PostgreSQL support for Tcl as - * procedural language (PL) + * procedural language (PL) * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -31,7 +31,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.27 2000/07/05 23:12:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.28 2000/07/19 11:53:02 wieck Exp $ * **********************************************************************/ @@ -54,6 +54,7 @@ #include "tcop/tcopprot.h" #include "utils/syscache.h" #include "catalog/pg_proc.h" +#include "catalog/pg_language.h" #include "catalog/pg_type.h" @@ -63,6 +64,7 @@ typedef struct pltcl_proc_desc { char *proname; + bool lanpltrusted; FmgrInfo result_in_func; Oid result_in_elem; int nargs; @@ -96,22 +98,26 @@ static int pltcl_firstcall = 1; static int pltcl_call_level = 0; static int pltcl_restart_in_progress = 0; static Tcl_Interp *pltcl_hold_interp = NULL; +static Tcl_Interp *pltcl_norm_interp = NULL; static Tcl_Interp *pltcl_safe_interp = NULL; static Tcl_HashTable *pltcl_proc_hash = NULL; -static Tcl_HashTable *pltcl_query_hash = NULL; +static Tcl_HashTable *pltcl_norm_query_hash = NULL; +static Tcl_HashTable *pltcl_safe_query_hash = NULL; +static FunctionCallInfo pltcl_actual_fcinfo = NULL; /********************************************************************** * Forward declarations **********************************************************************/ static void pltcl_init_all(void); -static void pltcl_init_safe_interp(void); +static void pltcl_init_interp(Tcl_Interp *interp); #ifdef PLTCL_UNKNOWN_SUPPORT -static void pltcl_init_load_unknown(void); +static void pltcl_init_load_unknown(Tcl_Interp *interp); #endif /* PLTCL_UNKNOWN_SUPPORT */ Datum pltcl_call_handler(PG_FUNCTION_ARGS); +Datum pltclu_call_handler(PG_FUNCTION_ARGS); static Datum pltcl_func_handler(PG_FUNCTION_ARGS); @@ -121,6 +127,10 @@ static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]); static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]); +static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, + int argc, char *argv[]); +static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, + int argc, char *argv[]); static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]); @@ -155,64 +165,40 @@ pltcl_init_all(void) * Create the dummy hold interpreter to prevent close of * stdout and stderr on DeleteInterp ************************************************************/ - if (pltcl_hold_interp == NULL) - { - if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL) - { - elog(ERROR, "pltcl: internal error - cannot create 'hold' " - "interpreter"); - } - } - - /************************************************************ - * Destroy the existing safe interpreter - ************************************************************/ - if (pltcl_safe_interp != NULL) + if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL) { - Tcl_DeleteInterp(pltcl_safe_interp); - pltcl_safe_interp = NULL; + elog(ERROR, "pltcl: internal error - cannot create 'hold' " + "interpreter"); } /************************************************************ - * Free the proc hash table + * Create the two interpreters ************************************************************/ - if (pltcl_proc_hash != NULL) + if ((pltcl_norm_interp = + Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL) { - hashent = Tcl_FirstHashEntry(pltcl_proc_hash, &hashsearch); - while (hashent != NULL) - { - prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); - free(prodesc->proname); - free(prodesc); - hashent = Tcl_NextHashEntry(&hashsearch); - } - Tcl_DeleteHashTable(pltcl_proc_hash); - free(pltcl_proc_hash); - pltcl_proc_hash = NULL; + elog(ERROR, + "pltcl: internal error - cannot create 'normal' interpreter"); } + pltcl_init_interp(pltcl_norm_interp); - /************************************************************ - * Free the prepared query hash table - ************************************************************/ - if (pltcl_query_hash != NULL) + if ((pltcl_safe_interp = + Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL) { - hashent = Tcl_FirstHashEntry(pltcl_query_hash, &hashsearch); - while (hashent != NULL) - { - querydesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent); - free(querydesc->argtypes); - free(querydesc); - hashent = Tcl_NextHashEntry(&hashsearch); - } - Tcl_DeleteHashTable(pltcl_query_hash); - free(pltcl_query_hash); - pltcl_query_hash = NULL; + elog(ERROR, + "pltcl: internal error - cannot create 'safe' interpreter"); } + pltcl_init_interp(pltcl_safe_interp); /************************************************************ - * Now recreate a new safe interpreter + * Initialize the proc and query hash tables ************************************************************/ - pltcl_init_safe_interp(); + pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); + pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); + pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS); + Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS); + Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS); pltcl_firstcall = 0; return; @@ -220,52 +206,28 @@ pltcl_init_all(void) /********************************************************************** - * pltcl_init_safe_interp() - Create the safe Tcl interpreter + * pltcl_init_interp() - initialize a Tcl interpreter **********************************************************************/ static void -pltcl_init_safe_interp(void) +pltcl_init_interp(Tcl_Interp *interp) { /************************************************************ - * Create the interpreter as a safe slave of the hold interp. - ************************************************************/ - if ((pltcl_safe_interp = - Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL) - { - elog(ERROR, - "pltcl: internal error - cannot create 'safe' interpreter"); - } - - /************************************************************ - * Enable debugging output from the Tcl bytecode compiler - * To see the trace, the interpreter must be created unsafe - * USE ONLY FOR DEBUGGING!!! - ************************************************************/ - - /* - * Tcl_SetVar(pltcl_safe_interp, "tcl_traceCompile", "1", 0); - */ - - /************************************************************ - * Initialize the proc and query hash tables - ************************************************************/ - pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); - pltcl_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS); - Tcl_InitHashTable(pltcl_query_hash, TCL_STRING_KEYS); - - /************************************************************ - * Install the commands for SPI support in the safe interpreter + * Install the commands for SPI support in the interpreter ************************************************************/ - Tcl_CreateCommand(pltcl_safe_interp, "elog", + Tcl_CreateCommand(interp, "elog", pltcl_elog, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "quote", + 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(pltcl_safe_interp, "spi_exec", + Tcl_CreateCommand(interp, "spi_exec", pltcl_SPI_exec, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "spi_prepare", + Tcl_CreateCommand(interp, "spi_prepare", pltcl_SPI_prepare, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "spi_execp", + Tcl_CreateCommand(interp, "spi_execp", pltcl_SPI_execp, NULL, NULL); #ifdef PLTCL_UNKNOWN_SUPPORT @@ -273,10 +235,10 @@ pltcl_init_safe_interp(void) * Try to load the unknown procedure from pltcl_modules ************************************************************/ if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "pltcl_init_safe_interp(): SPI_connect failed"); - pltcl_init_load_unknown(); + elog(ERROR, "pltcl_init_interp(): SPI_connect failed"); + pltcl_init_load_unknown(interp); if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "pltcl_init_safe_interp(): SPI_finish failed"); + elog(ERROR, "pltcl_init_interp(): SPI_finish failed"); #endif /* PLTCL_UNKNOWN_SUPPORT */ } @@ -349,7 +311,7 @@ pltcl_init_load_unknown(void) pfree(part); } } - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&unknown_src)); + tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src)); Tcl_DStringFree(&unknown_src); } @@ -369,6 +331,7 @@ Datum pltcl_call_handler(PG_FUNCTION_ARGS) { Datum retval; + FunctionCallInfo save_fcinfo; /************************************************************ * Initialize interpreters on first call @@ -390,22 +353,38 @@ pltcl_call_handler(PG_FUNCTION_ARGS) * Determine if called as function or trigger and * call appropriate subhandler ************************************************************/ + save_fcinfo = pltcl_actual_fcinfo; + if (CALLED_AS_TRIGGER(fcinfo)) + { + pltcl_actual_fcinfo = NULL; retval = PointerGetDatum(pltcl_trigger_handler(fcinfo)); - else + } else { + pltcl_actual_fcinfo = fcinfo; retval = pltcl_func_handler(fcinfo); + } + + pltcl_actual_fcinfo = save_fcinfo; pltcl_call_level--; return retval; } +/* keep non-static */ +Datum +pltclu_call_handler(PG_FUNCTION_ARGS) +{ + return pltcl_call_handler(fcinfo); +} + /********************************************************************** * pltcl_func_handler() - Handler for regular function calls **********************************************************************/ static Datum pltcl_func_handler(PG_FUNCTION_ARGS) { + Tcl_Interp *interp; int i; char internal_proname[512]; Tcl_HashEntry *hashent; @@ -436,15 +415,17 @@ pltcl_func_handler(PG_FUNCTION_ARGS) * * Then we load the procedure into the safe interpreter. ************************************************************/ - HeapTuple procTup; - HeapTuple typeTup; - Form_pg_proc procStruct; - Form_pg_type typeStruct; - Tcl_DString proc_internal_def; - Tcl_DString proc_internal_body; - char proc_internal_args[4096]; - char *proc_source; - char buf[512]; + HeapTuple procTup; + HeapTuple langTup; + HeapTuple typeTup; + Form_pg_proc procStruct; + Form_pg_language langStruct; + Form_pg_type typeStruct; + Tcl_DString proc_internal_def; + Tcl_DString proc_internal_body; + char proc_internal_args[4096]; + char *proc_source; + char buf[512]; /************************************************************ * Allocate a new procedure description block @@ -468,6 +449,27 @@ pltcl_func_handler(PG_FUNCTION_ARGS) } procStruct = (Form_pg_proc) GETSTRUCT(procTup); + /************************************************************ + * Lookup the pg_language tuple by Oid + ************************************************************/ + langTup = SearchSysCacheTuple(LANGOID, + ObjectIdGetDatum(procStruct->prolang), + 0, 0, 0); + if (!HeapTupleIsValid(langTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup for language %u failed", + procStruct->prolang); + } + langStruct = (Form_pg_language) GETSTRUCT(langTup); + + prodesc->lanpltrusted = langStruct->lanpltrusted; + if (prodesc->lanpltrusted) + interp = pltcl_safe_interp; + else + interp = pltcl_norm_interp; + /************************************************************ * Get the required information for input conversion of the * return value. @@ -570,9 +572,9 @@ pltcl_func_handler(PG_FUNCTION_ARGS) Tcl_DStringFree(&proc_internal_body); /************************************************************ - * Create the procedure in the safe interpreter + * Create the procedure in the interpreter ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, + tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&proc_internal_def)); Tcl_DStringFree(&proc_internal_def); if (tcl_rc != TCL_OK) @@ -580,7 +582,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cannot create internal procedure %s - %s", - internal_proname, pltcl_safe_interp->result); + internal_proname, interp->result); } /************************************************************ @@ -596,6 +598,11 @@ pltcl_func_handler(PG_FUNCTION_ARGS) * Found the proc description block in the hashtable ************************************************************/ prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); + + if (prodesc->lanpltrusted) + interp = pltcl_safe_interp; + else + interp = pltcl_norm_interp; } /************************************************************ @@ -671,7 +678,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) /************************************************************ * Call the Tcl function ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); + tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ @@ -687,7 +694,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; - elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); + elog(ERROR, "pltcl: %s", interp->result); } if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; @@ -720,7 +727,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) elog(ERROR, "pltcl: SPI_finish() failed"); retval = FunctionCall3(&prodesc->result_in_func, - PointerGetDatum(pltcl_safe_interp->result), + PointerGetDatum(interp->result), ObjectIdGetDatum(prodesc->result_in_elem), Int32GetDatum(-1)); @@ -735,6 +742,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS) { + Tcl_Interp *interp; TriggerData *trigdata = (TriggerData *) fcinfo->context; char internal_proname[512]; char *stroid; @@ -776,7 +784,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) Tcl_DString proc_internal_def; Tcl_DString proc_internal_body; HeapTuple procTup; + HeapTuple langTup; Form_pg_proc procStruct; + Form_pg_language langStruct; char *proc_source; /************************************************************ @@ -802,6 +812,27 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) } procStruct = (Form_pg_proc) GETSTRUCT(procTup); + /************************************************************ + * Lookup the pg_language tuple by Oid + ************************************************************/ + langTup = SearchSysCacheTuple(LANGOID, + ObjectIdGetDatum(procStruct->prolang), + 0, 0, 0); + if (!HeapTupleIsValid(langTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup for language %u failed", + procStruct->prolang); + } + langStruct = (Form_pg_language) GETSTRUCT(langTup); + + prodesc->lanpltrusted = langStruct->lanpltrusted; + if (prodesc->lanpltrusted) + interp = pltcl_safe_interp; + else + interp = pltcl_norm_interp; + /************************************************************ * Create the tcl command to define the internal * procedure @@ -846,9 +877,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) Tcl_DStringFree(&proc_internal_body); /************************************************************ - * Create the procedure in the safe interpreter + * Create the procedure in the interpreter ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, + tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&proc_internal_def)); Tcl_DStringFree(&proc_internal_def); if (tcl_rc != TCL_OK) @@ -856,7 +887,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cannot create internal procedure %s - %s", - internal_proname, pltcl_safe_interp->result); + internal_proname, interp->result); } /************************************************************ @@ -872,13 +903,18 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) * Found the proc description block in the hashtable ************************************************************/ prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); + + if (prodesc->lanpltrusted) + interp = pltcl_safe_interp; + else + interp = pltcl_norm_interp; } tupdesc = trigdata->tg_relation->rd_att; /************************************************************ * Create the tcl command to call the internal - * proc in the safe interpreter + * proc in the interpreter ************************************************************/ Tcl_DStringInit(&tcl_cmd); Tcl_DStringInit(&tcl_trigtup); @@ -998,7 +1034,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) /************************************************************ * Call the Tcl function ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); + tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ @@ -1014,7 +1050,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; - elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); + elog(ERROR, "pltcl: %s", interp->result); } if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; @@ -1037,9 +1073,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "pltcl: SPI_finish() failed"); - if (strcmp(pltcl_safe_interp->result, "OK") == 0) + if (strcmp(interp->result, "OK") == 0) return rettup; - if (strcmp(pltcl_safe_interp->result, "SKIP") == 0) + if (strcmp(interp->result, "SKIP") == 0) { return (HeapTuple) NULL;; } @@ -1048,11 +1084,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) * Convert the result value from the safe interpreter * and setup structures for SPI_modifytuple(); ************************************************************/ - if (Tcl_SplitList(pltcl_safe_interp, pltcl_safe_interp->result, + if (Tcl_SplitList(interp, interp->result, &ret_numvals, &ret_values) != TCL_OK) { elog(NOTICE, "pltcl: cannot split return value from trigger"); - elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); + elog(ERROR, "pltcl: %s", interp->result); } if (ret_numvals % 2 != 0) @@ -1275,6 +1311,91 @@ pltcl_quote(ClientData cdata, Tcl_Interp *interp, } +/********************************************************************** + * pltcl_argisnull() - determine if a specific argument is NULL + **********************************************************************/ +static int +pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int argno; + FunctionCallInfo fcinfo = pltcl_actual_fcinfo; + + /************************************************************ + * Check call syntax + ************************************************************/ + if (argc != 2) + { + Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Get the argument number + ************************************************************/ + if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK) + 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_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Check that the argno is valid + ************************************************************/ + argno--; + if (argno < 0 || argno >= fcinfo->nargs) + { + Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Get the requested NULL state + ************************************************************/ + if (PG_ARGISNULL(argno)) + Tcl_SetResult(interp, "1", TCL_VOLATILE); + else + Tcl_SetResult(interp, "0", TCL_VOLATILE); + + return TCL_OK; +} + + +/********************************************************************** + * pltcl_returnnull() - Cause a NULL return from a function + **********************************************************************/ +static int +pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int argno; + FunctionCallInfo fcinfo = pltcl_actual_fcinfo; + + /************************************************************ + * Check call syntax + ************************************************************/ + if (argc != 1) + { + Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Set the NULL return flag and cause Tcl to return from the + * procedure. + ************************************************************/ + fcinfo->isnull = true; + return TCL_RETURN; +} + + /********************************************************************** * pltcl_SPI_exec() - The builtin SPI_exec command * for the safe interpreter @@ -1524,6 +1645,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, Tcl_HashEntry *hashent; int hashnew; sigjmp_buf save_restart; + Tcl_HashTable *query_hash; /************************************************************ * Don't do anything if we are already in restart mode @@ -1680,8 +1802,13 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, * Insert a hashtable entry for the plan and return * the key to the caller ************************************************************/ + if (interp == pltcl_norm_interp) + query_hash = pltcl_norm_query_hash; + else + query_hash = pltcl_safe_query_hash; + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - hashent = Tcl_CreateHashEntry(pltcl_query_hash, qdesc->qname, &hashnew); + hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew); Tcl_SetHashValue(hashent, (ClientData) qdesc); Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE); @@ -1713,6 +1840,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, HeapTuple *volatile tuples = NULL; volatile TupleDesc tupdesc = NULL; sigjmp_buf save_restart; + Tcl_HashTable *query_hash; char *usage = "syntax error - 'SPI_execp " "?-nulls string? ?-count n? " @@ -1786,7 +1914,12 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, /************************************************************ * Get the prepared plan descriptor by its key ************************************************************/ - hashent = Tcl_FindHashEntry(pltcl_query_hash, argv[i++]); + if (interp == pltcl_norm_interp) + query_hash = pltcl_norm_query_hash; + else + query_hash = pltcl_safe_query_hash; + + hashent = Tcl_FindHashEntry(query_hash, argv[i++]); if (hashent == NULL) { Tcl_AppendResult(interp, "invalid queryid '", argv[--i], "'", NULL); -- 2.40.0