diff options
author | Bruce Momjian | 1998-02-26 04:46:47 +0000 |
---|---|---|
committer | Bruce Momjian | 1998-02-26 04:46:47 +0000 |
commit | a32450a5855eed4bfd756ef292ee45d3c754665b (patch) | |
tree | 26735c3406d9f46d0f39accbe6ff1fb5cc5beedc /src/pl | |
parent | 757bf69a2e259c76baed94fa06e792664ab5ed67 (diff) |
pgindent run before 6.3 release, with Thomas' requested changes.
Diffstat (limited to 'src/pl')
-rw-r--r-- | src/pl/tcl/pltcl.c | 3915 |
1 files changed, 2059 insertions, 1856 deletions
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 9c5d6a85c7..8a01e6509a 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -3,35 +3,35 @@ * procedural language (PL) * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.1 1998/02/11 14:07:59 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.2 1998/02/26 04:46:10 momjian Exp $ * - * This software is copyrighted by Jan Wieck - Hamburg. + * This software is copyrighted by Jan Wieck - Hamburg. * - * The author hereby grants permission to use, copy, modify, - * distribute, and license this software and its documentation - * for any purpose, provided that existing copyright notices are - * retained in all copies and that this notice is included - * verbatim in any distributions. No written agreement, license, - * or royalty fee is required for any of the authorized uses. - * Modifications to this software may be copyrighted by their - * author and need not follow the licensing terms described - * here, provided that the new terms are clearly indicated on - * the first page of each file where they apply. + * The author hereby grants permission to use, copy, modify, + * distribute, and license this software and its documentation + * for any purpose, provided that existing copyright notices are + * retained in all copies and that this notice is included + * verbatim in any distributions. No written agreement, license, + * or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their + * author and need not follow the licensing terms described + * here, provided that the new terms are clearly indicated on + * the first page of each file where they apply. * - * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS - * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN - * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS + * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN + * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. * - * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON - * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, - * ENHANCEMENTS, OR MODIFICATIONS. + * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON + * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + * ENHANCEMENTS, OR MODIFICATIONS. * **********************************************************************/ @@ -60,32 +60,34 @@ /********************************************************************** * The information we cache about loaded procedures **********************************************************************/ -typedef struct pltcl_proc_desc { - char *proname; - FmgrInfo result_in_func; - Oid result_in_elem; - int result_in_len; - int nargs; - FmgrInfo arg_out_func[MAXFMGRARGS]; - Oid arg_out_elem[MAXFMGRARGS]; - int arg_out_len[MAXFMGRARGS]; - int arg_is_rel[MAXFMGRARGS]; -} pltcl_proc_desc; +typedef struct pltcl_proc_desc +{ + char *proname; + FmgrInfo result_in_func; + Oid result_in_elem; + int result_in_len; + int nargs; + FmgrInfo arg_out_func[MAXFMGRARGS]; + Oid arg_out_elem[MAXFMGRARGS]; + int arg_out_len[MAXFMGRARGS]; + int arg_is_rel[MAXFMGRARGS]; +} pltcl_proc_desc; /********************************************************************** * The information we cache about prepared and saved plans **********************************************************************/ -typedef struct pltcl_query_desc { - char qname[20]; - void *plan; - int nargs; - Oid *argtypes; - FmgrInfo *arginfuncs; - Oid *argtypelems; - Datum *argvalues; - int *arglen; -} pltcl_query_desc; +typedef struct pltcl_query_desc +{ + char qname[20]; + void *plan; + int nargs; + Oid *argtypes; + FmgrInfo *arginfuncs; + Oid *argtypelems; + Datum *argvalues; + int *arglen; +} pltcl_query_desc; /************************************************************ @@ -98,23 +100,23 @@ typedef struct pltcl_query_desc { * It's ugly - Jan ************************************************************/ #if defined(nextstep) -#define sigjmp_buf jmp_buf +#define sigjmp_buf jmp_buf #define sigsetjmp(x,y) setjmp(x) #define siglongjmp longjmp #endif -extern sigjmp_buf Warn_restart; /* in tcop/postgres.c */ +extern sigjmp_buf Warn_restart; /* in tcop/postgres.c */ /********************************************************************** * Global data **********************************************************************/ -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_safe_interp = NULL; -static Tcl_HashTable *pltcl_proc_hash = NULL; -static Tcl_HashTable *pltcl_query_hash = NULL; +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_safe_interp = NULL; +static Tcl_HashTable *pltcl_proc_hash = NULL; +static Tcl_HashTable *pltcl_query_hash = NULL; /********************************************************************** * Forward declarations @@ -124,169 +126,191 @@ static void pltcl_init_safe_interp(void); #ifdef PLTCL_UNKNOWN_SUPPORT static void pltcl_init_load_unknown(void); -#endif /* PLTCL_UNKNOWN_SUPPORT */ -Datum pltcl_call_handler(FmgrInfo *proinfo, - FmgrValues *proargs, bool *isNull); +#endif /* PLTCL_UNKNOWN_SUPPORT */ + +Datum +pltcl_call_handler(FmgrInfo *proinfo, + FmgrValues *proargs, bool *isNull); -static Datum pltcl_func_handler(FmgrInfo *proinfo, - FmgrValues *proargs, bool *isNull); +static Datum +pltcl_func_handler(FmgrInfo *proinfo, + FmgrValues *proargs, bool *isNull); static HeapTuple pltcl_trigger_handler(FmgrInfo *proinfo); -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_SPI_exec(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]); -static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]); -static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]); - -static void pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname, - int tupno, HeapTuple tuple, TupleDesc tupdesc); -static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, - Tcl_DString *retval); - +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_SPI_exec(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]); +static int +pltcl_SPI_prepare(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]); +static int +pltcl_SPI_execp(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]); + +static void +pltcl_set_tuple_values(Tcl_Interp * interp, char *arrayname, + int tupno, HeapTuple tuple, TupleDesc tupdesc); +static void +pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, + Tcl_DString * retval); + /********************************************************************** * pltcl_init_all() - Initialize all **********************************************************************/ static void pltcl_init_all(void) { - Tcl_HashEntry *hashent; - Tcl_HashSearch hashsearch; - pltcl_proc_desc *prodesc; - pltcl_query_desc *querydesc; - - /************************************************************ - * Do initialization only once - ************************************************************/ - if (!pltcl_firstcall) return; - - /************************************************************ - * 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) { - Tcl_DeleteInterp(pltcl_safe_interp); - pltcl_safe_interp = NULL; - } - - /************************************************************ - * Free the proc hash table - ************************************************************/ - if (pltcl_proc_hash != 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; - } - - /************************************************************ - * Free the prepared query hash table - ************************************************************/ - if (pltcl_query_hash != 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; - } - - /************************************************************ - * Now recreate a new safe interpreter - ************************************************************/ - pltcl_init_safe_interp(); - - pltcl_firstcall = 0; - return; + Tcl_HashEntry *hashent; + Tcl_HashSearch hashsearch; + pltcl_proc_desc *prodesc; + pltcl_query_desc *querydesc; + + /************************************************************ + * Do initialization only once + ************************************************************/ + if (!pltcl_firstcall) + return; + + /************************************************************ + * 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) + { + Tcl_DeleteInterp(pltcl_safe_interp); + pltcl_safe_interp = NULL; + } + + /************************************************************ + * Free the proc hash table + ************************************************************/ + if (pltcl_proc_hash != 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; + } + + /************************************************************ + * Free the prepared query hash table + ************************************************************/ + if (pltcl_query_hash != 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; + } + + /************************************************************ + * Now recreate a new safe interpreter + ************************************************************/ + pltcl_init_safe_interp(); + + pltcl_firstcall = 0; + return; } /********************************************************************** - * pltcl_init_safe_interp() - Create the safe Tcl interpreter + * pltcl_init_safe_interp() - Create the safe Tcl interpreter **********************************************************************/ static void pltcl_init_safe_interp(void) { - /************************************************************ - * 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 - ************************************************************/ - Tcl_CreateCommand(pltcl_safe_interp, "elog", - pltcl_elog, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "quote", - pltcl_quote, NULL, NULL); - - Tcl_CreateCommand(pltcl_safe_interp, "spi_exec", - pltcl_SPI_exec, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "spi_prepare", - pltcl_SPI_prepare, NULL, NULL); - Tcl_CreateCommand(pltcl_safe_interp, "spi_execp", - pltcl_SPI_execp, NULL, NULL); + /************************************************************ + * 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 + ************************************************************/ + Tcl_CreateCommand(pltcl_safe_interp, "elog", + pltcl_elog, NULL, NULL); + Tcl_CreateCommand(pltcl_safe_interp, "quote", + pltcl_quote, NULL, NULL); + + Tcl_CreateCommand(pltcl_safe_interp, "spi_exec", + pltcl_SPI_exec, NULL, NULL); + Tcl_CreateCommand(pltcl_safe_interp, "spi_prepare", + pltcl_SPI_prepare, NULL, NULL); + Tcl_CreateCommand(pltcl_safe_interp, "spi_execp", + pltcl_SPI_execp, NULL, NULL); #ifdef PLTCL_UNKNOWN_SUPPORT - /************************************************************ - * 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(); - if (SPI_finish() != SPI_OK_FINISH) { - elog(ERROR, "pltcl_init_safe_interp(): SPI_finish failed"); - } -#endif /* PLTCL_UNKNOWN_SUPPORT */ + /************************************************************ + * 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(); + if (SPI_finish() != SPI_OK_FINISH) + { + elog(ERROR, "pltcl_init_safe_interp(): SPI_finish failed"); + } +#endif /* PLTCL_UNKNOWN_SUPPORT */ } @@ -299,68 +323,74 @@ pltcl_init_safe_interp(void) static void pltcl_init_load_unknown(void) { - int spi_rc; - int tcl_rc; - Tcl_DString unknown_src; - char *part; - int i; - int fno; - - /************************************************************ - * Check if table pltcl_modules exists - ************************************************************/ - spi_rc = SPI_exec("select 1 from pg_class " - "where relname = 'pltcl_modules'", 1); - if (spi_rc != SPI_OK_SELECT) { - elog(ERROR, "pltcl_init_load_unknown(): select from pg_class failed"); - } - if (SPI_processed == 0) { - return; - } - - /************************************************************ - * Read all the row's from it where modname = 'unknown' in - * the order of modseq - ************************************************************/ - Tcl_DStringInit(&unknown_src); - - spi_rc = SPI_exec("select modseq, modsrc from pltcl_modules " - "where modname = 'unknown' " - "order by modseq", 0); - if (spi_rc != SPI_OK_SELECT) { - elog(ERROR, "pltcl_init_load_unknown(): select from pltcl_modules " - "failed"); - } - - /************************************************************ - * If there's nothing, module unknown doesn't exist - ************************************************************/ - if (SPI_processed == 0) { - Tcl_DStringFree(&unknown_src); - elog(NOTICE, "pltcl: Module unknown not found in pltcl_modules"); - return; - } - - /************************************************************ - * There is a module named unknown. Resemble the - * source from the modsrc attributes and evaluate - * it in the safe interpreter - ************************************************************/ - fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc"); - - for (i = 0; i < SPI_processed; i++) { - part = SPI_getvalue(SPI_tuptable->vals[i], - SPI_tuptable->tupdesc, fno); - if (part != NULL) { - Tcl_DStringAppend(&unknown_src, part, -1); - pfree(part); - } - } - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&unknown_src)); - Tcl_DStringFree(&unknown_src); + int spi_rc; + int tcl_rc; + Tcl_DString unknown_src; + char *part; + int i; + int fno; + + /************************************************************ + * Check if table pltcl_modules exists + ************************************************************/ + spi_rc = SPI_exec("select 1 from pg_class " + "where relname = 'pltcl_modules'", 1); + if (spi_rc != SPI_OK_SELECT) + { + elog(ERROR, "pltcl_init_load_unknown(): select from pg_class failed"); + } + if (SPI_processed == 0) + { + return; + } + + /************************************************************ + * Read all the row's from it where modname = 'unknown' in + * the order of modseq + ************************************************************/ + Tcl_DStringInit(&unknown_src); + + spi_rc = SPI_exec("select modseq, modsrc from pltcl_modules " + "where modname = 'unknown' " + "order by modseq", 0); + if (spi_rc != SPI_OK_SELECT) + { + elog(ERROR, "pltcl_init_load_unknown(): select from pltcl_modules " + "failed"); + } + + /************************************************************ + * If there's nothing, module unknown doesn't exist + ************************************************************/ + if (SPI_processed == 0) + { + Tcl_DStringFree(&unknown_src); + elog(NOTICE, "pltcl: Module unknown not found in pltcl_modules"); + return; + } + + /************************************************************ + * There is a module named unknown. Resemble the + * source from the modsrc attributes and evaluate + * it in the safe interpreter + ************************************************************/ + fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc"); + + for (i = 0; i < SPI_processed; i++) + { + part = SPI_getvalue(SPI_tuptable->vals[i], + SPI_tuptable->tupdesc, fno); + if (part != NULL) + { + Tcl_DStringAppend(&unknown_src, part, -1); + pfree(part); + } + } + tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&unknown_src)); + Tcl_DStringFree(&unknown_src); } -#endif /* PLTCL_UNKNOWN_SUPPORT */ +#endif /* PLTCL_UNKNOWN_SUPPORT */ /********************************************************************** @@ -371,50 +401,56 @@ pltcl_init_load_unknown(void) * PL/Tcl procedures. **********************************************************************/ Datum -pltcl_call_handler(FmgrInfo *proinfo, - FmgrValues *proargs, - bool *isNull) +pltcl_call_handler(FmgrInfo *proinfo, + FmgrValues *proargs, + bool *isNull) { - Datum retval; - - /************************************************************ - * Initialize interpreters on first call - ************************************************************/ - if (pltcl_firstcall) { - pltcl_init_all(); - } - - /************************************************************ - * Connect to SPI manager - ************************************************************/ - if (SPI_connect() != SPI_OK_CONNECT) { - elog(ERROR, "pltcl: cannot connect to SPI manager"); - } - /************************************************************ - * Keep track about the nesting of Tcl-SPI-Tcl-... calls - ************************************************************/ - pltcl_call_level++; - - /************************************************************ - * Determine if called as function or trigger and - * call appropriate subhandler - ************************************************************/ - if (CurrentTriggerData == NULL) { - retval = pltcl_func_handler(proinfo, proargs, isNull); - } else { - retval = (Datum)pltcl_trigger_handler(proinfo); - } - - pltcl_call_level--; - - /************************************************************ - * Disconnect from SPI manager - ************************************************************/ - if (SPI_finish() != SPI_OK_FINISH) { - elog(ERROR, "pltcl: SPI_finish() failed"); - } - - return retval; + Datum retval; + + /************************************************************ + * Initialize interpreters on first call + ************************************************************/ + if (pltcl_firstcall) + { + pltcl_init_all(); + } + + /************************************************************ + * Connect to SPI manager + ************************************************************/ + if (SPI_connect() != SPI_OK_CONNECT) + { + elog(ERROR, "pltcl: cannot connect to SPI manager"); + } + /************************************************************ + * Keep track about the nesting of Tcl-SPI-Tcl-... calls + ************************************************************/ + pltcl_call_level++; + + /************************************************************ + * Determine if called as function or trigger and + * call appropriate subhandler + ************************************************************/ + if (CurrentTriggerData == NULL) + { + retval = pltcl_func_handler(proinfo, proargs, isNull); + } + else + { + retval = (Datum) pltcl_trigger_handler(proinfo); + } + + pltcl_call_level--; + + /************************************************************ + * Disconnect from SPI manager + ************************************************************/ + if (SPI_finish() != SPI_OK_FINISH) + { + elog(ERROR, "pltcl: SPI_finish() failed"); + } + + return retval; } @@ -422,299 +458,327 @@ pltcl_call_handler(FmgrInfo *proinfo, * pltcl_func_handler() - Handler for regular function calls **********************************************************************/ static Datum -pltcl_func_handler(FmgrInfo *proinfo, - FmgrValues *proargs, - bool *isNull) +pltcl_func_handler(FmgrInfo *proinfo, + FmgrValues *proargs, + bool *isNull) { - int i; - char internal_proname[512]; - char *stroid; - Tcl_HashEntry *hashent; - int hashnew; - pltcl_proc_desc *prodesc; - Tcl_DString tcl_cmd; - Tcl_DString list_tmp; - int tcl_rc; - Datum retval; - sigjmp_buf save_restart; - - /************************************************************ - * Build our internal proc name from the functions Oid - ************************************************************/ - stroid = oidout(proinfo->fn_oid); - strcpy(internal_proname, "__PLTcl_proc_"); - strcat(internal_proname, stroid); - pfree(stroid); - - /************************************************************ - * Lookup the internal proc name in the hashtable - ************************************************************/ - hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); - if (hashent == 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 safe interpreter. - ************************************************************/ - HeapTuple procTup; - HeapTuple typeTup; - Form_pg_proc procStruct; - TypeTupleForm 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 - ************************************************************/ - prodesc = (pltcl_proc_desc *)malloc(sizeof(pltcl_proc_desc)); - prodesc->proname = malloc(strlen(internal_proname) + 1); - strcpy(prodesc->proname, internal_proname); - - /************************************************************ - * Lookup the pg_proc tuple by Oid - ************************************************************/ - procTup = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(proinfo->fn_oid), - 0, 0, 0); - if (!HeapTupleIsValid(procTup)) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cache lookup from pg_proc failed"); - } - procStruct = (Form_pg_proc) GETSTRUCT(procTup); - - /************************************************************ - * Get the required information for input conversion of the - * return value. - ************************************************************/ - typeTup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cache lookup for return type failed"); - } - typeStruct = (TypeTupleForm) GETSTRUCT(typeTup); - - if (typeStruct->typrelid != InvalidOid) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: return types of tuples not supported yet"); - } - - fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); - prodesc->result_in_elem = (Oid) (typeStruct->typelem); - prodesc->result_in_len = typeStruct->typlen; - - /************************************************************ - * Get the required information for output conversion - * of all procedure arguments - ************************************************************/ - prodesc->nargs = proinfo->fn_nargs; - proc_internal_args[0] = '\0'; - for (i = 0; i < proinfo->fn_nargs; i++) { - typeTup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(procStruct->proargtypes[i]), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cache lookup for argument type failed"); - } - typeStruct = (TypeTupleForm) GETSTRUCT(typeTup); - - if (typeStruct->typrelid != InvalidOid) { - prodesc->arg_is_rel[i] = 1; - if (i > 0) { - strcat(proc_internal_args, " "); + int i; + char internal_proname[512]; + char *stroid; + Tcl_HashEntry *hashent; + int hashnew; + pltcl_proc_desc *prodesc; + Tcl_DString tcl_cmd; + Tcl_DString list_tmp; + int tcl_rc; + Datum retval; + sigjmp_buf save_restart; + + /************************************************************ + * Build our internal proc name from the functions Oid + ************************************************************/ + stroid = oidout(proinfo->fn_oid); + strcpy(internal_proname, "__PLTcl_proc_"); + strcat(internal_proname, stroid); + pfree(stroid); + + /************************************************************ + * Lookup the internal proc name in the hashtable + ************************************************************/ + hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); + if (hashent == 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 safe interpreter. + ************************************************************/ + HeapTuple procTup; + HeapTuple typeTup; + Form_pg_proc procStruct; + TypeTupleForm 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 + ************************************************************/ + prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc)); + prodesc->proname = malloc(strlen(internal_proname) + 1); + strcpy(prodesc->proname, internal_proname); + + /************************************************************ + * Lookup the pg_proc tuple by Oid + ************************************************************/ + procTup = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(proinfo->fn_oid), + 0, 0, 0); + if (!HeapTupleIsValid(procTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup from pg_proc failed"); } - sprintf(buf, "__PLTcl_Tup_%d", i + 1); - strcat(proc_internal_args, buf); - continue; - } else { - prodesc->arg_is_rel[i] = 0; - } - - fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); - prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem); - prodesc->arg_out_len[i] = typeStruct->typlen; - - if (i > 0) { - strcat(proc_internal_args, " "); - } - sprintf(buf, "%d", i + 1); - strcat(proc_internal_args, buf); - } - - /************************************************************ - * 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 upvars for tuple 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); - for (i = 0; i < proinfo->fn_nargs; i++) { - if (!prodesc->arg_is_rel[i]) continue; - sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1); - Tcl_DStringAppend(&proc_internal_body, buf, -1); - } - proc_source = textout(&(procStruct->prosrc)); - Tcl_DStringAppend(&proc_internal_body, proc_source, -1); - pfree(proc_source); - Tcl_DStringAppendElement(&proc_internal_def, - Tcl_DStringValue(&proc_internal_body)); - Tcl_DStringFree(&proc_internal_body); - - /************************************************************ - * Create the procedure in the safe interpreter - ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, - Tcl_DStringValue(&proc_internal_def)); - Tcl_DStringFree(&proc_internal_def); - if (tcl_rc != TCL_OK) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cannot create internal procedure %s - %s", - internal_proname, pltcl_safe_interp->result); - } - - /************************************************************ - * Add the proc description block to the hashtable - ************************************************************/ - hashent = Tcl_CreateHashEntry(pltcl_proc_hash, - prodesc->proname, &hashnew); - Tcl_SetHashValue(hashent, (ClientData)prodesc); - } else { - /************************************************************ - * Found the proc description block in the hashtable - ************************************************************/ - prodesc = (pltcl_proc_desc *)Tcl_GetHashValue(hashent); - } - - /************************************************************ - * Create the tcl command to call the internal - * proc in the safe interpreter - ************************************************************/ - Tcl_DStringInit(&tcl_cmd); - Tcl_DStringInit(&list_tmp); - Tcl_DStringAppendElement(&tcl_cmd, internal_proname); - - /************************************************************ - * Catch elog(ERROR) during build of the Tcl command - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - Tcl_DStringFree(&tcl_cmd); + procStruct = (Form_pg_proc) GETSTRUCT(procTup); + + /************************************************************ + * Get the required information for input conversion of the + * return value. + ************************************************************/ + typeTup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(procStruct->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup for return type failed"); + } + typeStruct = (TypeTupleForm) GETSTRUCT(typeTup); + + if (typeStruct->typrelid != InvalidOid) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: return types of tuples not supported yet"); + } + + fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); + prodesc->result_in_elem = (Oid) (typeStruct->typelem); + prodesc->result_in_len = typeStruct->typlen; + + /************************************************************ + * Get the required information for output conversion + * of all procedure arguments + ************************************************************/ + prodesc->nargs = proinfo->fn_nargs; + proc_internal_args[0] = '\0'; + for (i = 0; i < proinfo->fn_nargs; i++) + { + typeTup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(procStruct->proargtypes[i]), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup for argument type failed"); + } + typeStruct = (TypeTupleForm) GETSTRUCT(typeTup); + + if (typeStruct->typrelid != InvalidOid) + { + prodesc->arg_is_rel[i] = 1; + if (i > 0) + { + strcat(proc_internal_args, " "); + } + sprintf(buf, "__PLTcl_Tup_%d", i + 1); + strcat(proc_internal_args, buf); + continue; + } + else + { + prodesc->arg_is_rel[i] = 0; + } + + fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); + prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem); + prodesc->arg_out_len[i] = typeStruct->typlen; + + if (i > 0) + { + strcat(proc_internal_args, " "); + } + sprintf(buf, "%d", i + 1); + strcat(proc_internal_args, buf); + } + + /************************************************************ + * 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 upvars for tuple 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); + for (i = 0; i < proinfo->fn_nargs; i++) + { + if (!prodesc->arg_is_rel[i]) + continue; + sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1); + Tcl_DStringAppend(&proc_internal_body, buf, -1); + } + proc_source = textout(&(procStruct->prosrc)); + Tcl_DStringAppend(&proc_internal_body, proc_source, -1); + pfree(proc_source); + Tcl_DStringAppendElement(&proc_internal_def, + Tcl_DStringValue(&proc_internal_body)); + Tcl_DStringFree(&proc_internal_body); + + /************************************************************ + * Create the procedure in the safe interpreter + ************************************************************/ + tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, + Tcl_DStringValue(&proc_internal_def)); + Tcl_DStringFree(&proc_internal_def); + if (tcl_rc != TCL_OK) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cannot create internal procedure %s - %s", + internal_proname, pltcl_safe_interp->result); + } + + /************************************************************ + * Add the proc description block to the hashtable + ************************************************************/ + hashent = Tcl_CreateHashEntry(pltcl_proc_hash, + prodesc->proname, &hashnew); + Tcl_SetHashValue(hashent, (ClientData) prodesc); + } + else + { + /************************************************************ + * Found the proc description block in the hashtable + ************************************************************/ + prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); + } + + /************************************************************ + * Create the tcl command to call the internal + * proc in the safe interpreter + ************************************************************/ + Tcl_DStringInit(&tcl_cmd); + Tcl_DStringInit(&list_tmp); + Tcl_DStringAppendElement(&tcl_cmd, internal_proname); + + /************************************************************ + * Catch elog(ERROR) during build of the Tcl command + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + Tcl_DStringFree(&tcl_cmd); + Tcl_DStringFree(&list_tmp); + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } + + /************************************************************ + * Add all call arguments to the command + ************************************************************/ + for (i = 0; i < prodesc->nargs; i++) + { + if (prodesc->arg_is_rel[i]) + { + /************************************************** + * For tuple values, add a list for 'array set ...' + **************************************************/ + Tcl_DStringInit(&list_tmp); + pltcl_build_tuple_argument( + ((TupleTableSlot *) (proargs->data[i]))->val, + ((TupleTableSlot *) (proargs->data[i]))->ttc_tupleDescriptor, + &list_tmp); + Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp)); + Tcl_DStringFree(&list_tmp); + Tcl_DStringInit(&list_tmp); + } + else + { + /************************************************** + * Single values are added as string element + * of their external representation + **************************************************/ + char *tmp; + + tmp = (*fmgr_faddr(&(prodesc->arg_out_func[i]))) + (proargs->data[i], + prodesc->arg_out_elem[i], + prodesc->arg_out_len[i]); + Tcl_DStringAppendElement(&tcl_cmd, tmp); + pfree(tmp); + } + } Tcl_DStringFree(&list_tmp); - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - siglongjmp(Warn_restart, 1); - } - - /************************************************************ - * Add all call arguments to the command - ************************************************************/ - for (i = 0; i < prodesc->nargs; i++) { - if (prodesc->arg_is_rel[i]) { - /************************************************** - * For tuple values, add a list for 'array set ...' - **************************************************/ - Tcl_DStringInit(&list_tmp); - pltcl_build_tuple_argument( - ((TupleTableSlot *)(proargs->data[i]))->val, - ((TupleTableSlot *)(proargs->data[i]))->ttc_tupleDescriptor, - &list_tmp); - Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp)); - Tcl_DStringFree(&list_tmp); - Tcl_DStringInit(&list_tmp); - } else { - /************************************************** - * Single values are added as string element - * of their external representation - **************************************************/ - char *tmp; - - tmp = (*fmgr_faddr(&(prodesc->arg_out_func[i]))) - (proargs->data[i], - prodesc->arg_out_elem[i], - prodesc->arg_out_len[i]); - Tcl_DStringAppendElement(&tcl_cmd, tmp); - pfree(tmp); - } - } - Tcl_DStringFree(&list_tmp); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - - /************************************************************ - * Call the Tcl function - ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); - Tcl_DStringFree(&tcl_cmd); - - /************************************************************ - * Check the return code from Tcl and handle - * our special restart mechanism to get rid - * of all nested call levels on transaction - * abort. - ************************************************************/ - if (tcl_rc != TCL_OK || pltcl_restart_in_progress) { - if (!pltcl_restart_in_progress) { - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); - } - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - siglongjmp(Warn_restart, 1); - } - - /************************************************************ - * Convert the result value from the safe interpreter - * into it's PostgreSQL data format and return it. - * Again, the call to fmgr() could fire an elog and we - * have to count for the current interpreter level we are - * on. The save_restart from above is still good. - ************************************************************/ - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - siglongjmp(Warn_restart, 1); - } - - retval = (Datum)(*fmgr_faddr(&prodesc->result_in_func)) - (pltcl_safe_interp->result, - prodesc->result_in_elem, - prodesc->result_in_len); - - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return retval; + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + + /************************************************************ + * Call the Tcl function + ************************************************************/ + tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); + Tcl_DStringFree(&tcl_cmd); + + /************************************************************ + * Check the return code from Tcl and handle + * our special restart mechanism to get rid + * of all nested call levels on transaction + * abort. + ************************************************************/ + if (tcl_rc != TCL_OK || pltcl_restart_in_progress) + { + if (!pltcl_restart_in_progress) + { + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); + } + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } + + /************************************************************ + * Convert the result value from the safe interpreter + * into it's PostgreSQL data format and return it. + * Again, the call to fmgr() could fire an elog and we + * have to count for the current interpreter level we are + * on. The save_restart from above is still good. + ************************************************************/ + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } + + retval = (Datum) (*fmgr_faddr(&prodesc->result_in_func)) + (pltcl_safe_interp->result, + prodesc->result_in_elem, + prodesc->result_in_len); + + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return retval; } @@ -724,486 +788,536 @@ pltcl_func_handler(FmgrInfo *proinfo, static HeapTuple pltcl_trigger_handler(FmgrInfo *proinfo) { - TriggerData *trigdata; - char internal_proname[512]; - char *stroid; - Tcl_HashEntry *hashent; - int hashnew; - pltcl_proc_desc *prodesc; - TupleDesc tupdesc; - 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; - char **ret_values; - - sigjmp_buf save_restart; - - /************************************************************ - * Save the current trigger data local - ************************************************************/ - trigdata = CurrentTriggerData; - CurrentTriggerData = NULL; - - /************************************************************ - * Build our internal proc name from the functions Oid - ************************************************************/ - stroid = oidout(proinfo->fn_oid); - strcpy(internal_proname, "__PLTcl_proc_"); - strcat(internal_proname, stroid); - pfree(stroid); - - /************************************************************ - * Lookup the internal proc name in the hashtable - ************************************************************/ - hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); - if (hashent == NULL) { + TriggerData *trigdata; + char internal_proname[512]; + char *stroid; + Tcl_HashEntry *hashent; + int hashnew; + pltcl_proc_desc *prodesc; + TupleDesc tupdesc; + 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; + char **ret_values; + + sigjmp_buf save_restart; + /************************************************************ - * If we haven't found it in the hashtable, - * we load the procedure into the safe interpreter. - ************************************************************/ - Tcl_DString proc_internal_def; - Tcl_DString proc_internal_body; - HeapTuple procTup; - Form_pg_proc procStruct; - char *proc_source; - - /************************************************************ - * Allocate a new procedure description block - ************************************************************/ - prodesc = (pltcl_proc_desc *)malloc(sizeof(pltcl_proc_desc)); - memset(prodesc, 0, sizeof(pltcl_proc_desc)); - prodesc->proname = malloc(strlen(internal_proname) + 1); - strcpy(prodesc->proname, internal_proname); - - /************************************************************ - * Lookup the pg_proc tuple by Oid - ************************************************************/ - procTup = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(proinfo->fn_oid), - 0, 0, 0); - if (!HeapTupleIsValid(procTup)) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cache lookup from pg_proc failed"); - } - procStruct = (Form_pg_proc) GETSTRUCT(procTup); - - /************************************************************ - * 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, - "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args"); - - /************************************************************ - * prefix procedure body with - * upvar #0 <internal_procname> GD - * and with appropriate setting of NEW, OLD, - * and the arguments as numerical variables. - ************************************************************/ - Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1); - Tcl_DStringAppend(&proc_internal_body, internal_proname, -1); - Tcl_DStringAppend(&proc_internal_body, " GD\n", -1); - - 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); - - proc_source = textout(&(procStruct->prosrc)); - Tcl_DStringAppend(&proc_internal_body, proc_source, -1); - pfree(proc_source); - Tcl_DStringAppendElement(&proc_internal_def, - Tcl_DStringValue(&proc_internal_body)); - Tcl_DStringFree(&proc_internal_body); + * Save the current trigger data local + ************************************************************/ + trigdata = CurrentTriggerData; + CurrentTriggerData = NULL; /************************************************************ - * Create the procedure in the safe interpreter + * Build our internal proc name from the functions Oid ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, - Tcl_DStringValue(&proc_internal_def)); - Tcl_DStringFree(&proc_internal_def); - if (tcl_rc != TCL_OK) { - free(prodesc->proname); - free(prodesc); - elog(ERROR, "pltcl: cannot create internal procedure %s - %s", - internal_proname, pltcl_safe_interp->result); + stroid = oidout(proinfo->fn_oid); + strcpy(internal_proname, "__PLTcl_proc_"); + strcat(internal_proname, stroid); + pfree(stroid); + + /************************************************************ + * Lookup the internal proc name in the hashtable + ************************************************************/ + hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); + if (hashent == NULL) + { + /************************************************************ + * If we haven't found it in the hashtable, + * we load the procedure into the safe interpreter. + ************************************************************/ + Tcl_DString proc_internal_def; + Tcl_DString proc_internal_body; + HeapTuple procTup; + Form_pg_proc procStruct; + char *proc_source; + + /************************************************************ + * Allocate a new procedure description block + ************************************************************/ + prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc)); + memset(prodesc, 0, sizeof(pltcl_proc_desc)); + prodesc->proname = malloc(strlen(internal_proname) + 1); + strcpy(prodesc->proname, internal_proname); + + /************************************************************ + * Lookup the pg_proc tuple by Oid + ************************************************************/ + procTup = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(proinfo->fn_oid), + 0, 0, 0); + if (!HeapTupleIsValid(procTup)) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cache lookup from pg_proc failed"); + } + procStruct = (Form_pg_proc) GETSTRUCT(procTup); + + /************************************************************ + * 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, + "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args"); + + /************************************************************ + * prefix procedure body with + * upvar #0 <internal_procname> GD + * and with appropriate setting of NEW, OLD, + * and the arguments as numerical variables. + ************************************************************/ + Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1); + Tcl_DStringAppend(&proc_internal_body, internal_proname, -1); + Tcl_DStringAppend(&proc_internal_body, " GD\n", -1); + + 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); + + proc_source = textout(&(procStruct->prosrc)); + Tcl_DStringAppend(&proc_internal_body, proc_source, -1); + pfree(proc_source); + Tcl_DStringAppendElement(&proc_internal_def, + Tcl_DStringValue(&proc_internal_body)); + Tcl_DStringFree(&proc_internal_body); + + /************************************************************ + * Create the procedure in the safe interpreter + ************************************************************/ + tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, + Tcl_DStringValue(&proc_internal_def)); + Tcl_DStringFree(&proc_internal_def); + if (tcl_rc != TCL_OK) + { + free(prodesc->proname); + free(prodesc); + elog(ERROR, "pltcl: cannot create internal procedure %s - %s", + internal_proname, pltcl_safe_interp->result); + } + + /************************************************************ + * Add the proc description block to the hashtable + ************************************************************/ + hashent = Tcl_CreateHashEntry(pltcl_proc_hash, + prodesc->proname, &hashnew); + Tcl_SetHashValue(hashent, (ClientData) prodesc); } + else + { + /************************************************************ + * Found the proc description block in the hashtable + ************************************************************/ + prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); + } + + tupdesc = trigdata->tg_relation->rd_att; /************************************************************ - * Add the proc description block to the hashtable + * Create the tcl command to call the internal + * proc in the safe interpreter ************************************************************/ - hashent = Tcl_CreateHashEntry(pltcl_proc_hash, - prodesc->proname, &hashnew); - Tcl_SetHashValue(hashent, (ClientData)prodesc); - } else { + Tcl_DStringInit(&tcl_cmd); + Tcl_DStringInit(&tcl_trigtup); + Tcl_DStringInit(&tcl_newtup); + /************************************************************ - * Found the proc description block in the hashtable + * We call external functions below - care for elog(ERROR) ************************************************************/ - prodesc = (pltcl_proc_desc *)Tcl_GetHashValue(hashent); - } + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + Tcl_DStringFree(&tcl_cmd); + Tcl_DStringFree(&tcl_trigtup); + Tcl_DStringFree(&tcl_newtup); + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } - tupdesc = trigdata->tg_relation->rd_att; + /* The procedure name */ + Tcl_DStringAppendElement(&tcl_cmd, internal_proname); - /************************************************************ - * Create the tcl command to call the internal - * proc in the safe interpreter - ************************************************************/ - Tcl_DStringInit(&tcl_cmd); - Tcl_DStringInit(&tcl_trigtup); - Tcl_DStringInit(&tcl_newtup); + /* The trigger name for argument TG_name */ + Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname); - /************************************************************ - * We call external functions below - care for elog(ERROR) - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - Tcl_DStringFree(&tcl_cmd); - Tcl_DStringFree(&tcl_trigtup); - Tcl_DStringFree(&tcl_newtup); - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - siglongjmp(Warn_restart, 1); - } - - /* The procedure name */ - Tcl_DStringAppendElement(&tcl_cmd, 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 = oidout(trigdata->tg_relation->rd_id); - 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++) { - Tcl_DStringAppendElement(&tcl_trigtup, tupdesc->attrs[i]->attname.data); - } - 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 { - Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); - } - - /* The level part of the event for TG_level */ - if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) { - Tcl_DStringAppendElement(&tcl_cmd, "ROW"); - } - else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) { - Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT"); - } - else { - Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); - } - - /* 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"); + /* The oid of the trigger relation for argument TG_relid */ + stroid = oidout(trigdata->tg_relation->rd_id); + 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++) + { + Tcl_DStringAppendElement(&tcl_trigtup, tupdesc->attrs[i]->attname.data); + } 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)); + 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 + { + Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); + } - rettup = trigdata->tg_trigtuple; - } - else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { - Tcl_DStringAppendElement(&tcl_cmd, "UPDATE"); + /* The level part of the event for TG_level */ + if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) + { + Tcl_DStringAppendElement(&tcl_cmd, "ROW"); + } + else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) + { + Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT"); + } + else + { + Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); + } - pltcl_build_tuple_argument(trigdata->tg_newtuple, - tupdesc, &tcl_newtup); + /* Build the data list for the trigtuple */ + pltcl_build_tuple_argument(trigdata->tg_trigtuple, + tupdesc, &tcl_trigtup); - Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup)); - Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&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"); - rettup = trigdata->tg_newtuple; - } - else { - Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); + Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); + Tcl_DStringAppendElement(&tcl_cmd, ""); - Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); - Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); + rettup = trigdata->tg_trigtuple; + } + else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) + { + Tcl_DStringAppendElement(&tcl_cmd, "DELETE"); - rettup = trigdata->tg_trigtuple; - } - - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - Tcl_DStringFree(&tcl_trigtup); - Tcl_DStringFree(&tcl_newtup); - - /************************************************************ - * 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]); - } - - /************************************************************ - * Call the Tcl function - ************************************************************/ - tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); - Tcl_DStringFree(&tcl_cmd); - - /************************************************************ - * Check the return code from Tcl and handle - * our special restart mechanism to get rid - * of all nested call levels on transaction - * abort. - ************************************************************/ - if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress) { - if (!pltcl_restart_in_progress) { - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); - } - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; - } - siglongjmp(Warn_restart, 1); - } - - switch (tcl_rc) { - case TCL_OK: - break; - - default: - elog(ERROR, "pltcl: unsupported TCL return code %d", tcl_rc); - } - - /************************************************************ - * The return value from the procedure might be one of - * the magic strings OK or SKIP or a list from array get - ************************************************************/ - if (strcmp(pltcl_safe_interp->result, "OK") == 0) { - return rettup; - } - if (strcmp(pltcl_safe_interp->result, "SKIP") == 0) { - return (HeapTuple)NULL;; - } - - /************************************************************ - * Convert the result value from the safe interpreter - * and setup structures for SPI_modifytuple(); - ************************************************************/ - if (Tcl_SplitList(pltcl_safe_interp, pltcl_safe_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); - } - - if (ret_numvals % 2 != 0) { - ckfree(ret_values); - elog(ERROR, "pltcl: 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 + 1); - memset(modnulls, 'n', tupdesc->natts); - modnulls[tupdesc->natts] = '\0'; - - /************************************************************ - * Care for possible elog(ERROR)'s below - ************************************************************/ - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - ckfree(ret_values); - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) { - pltcl_restart_in_progress = 0; + Tcl_DStringAppendElement(&tcl_cmd, ""); + Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); + + rettup = trigdata->tg_trigtuple; } - siglongjmp(Warn_restart, 1); - } + else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) + { + Tcl_DStringAppendElement(&tcl_cmd, "UPDATE"); - i = 0; - while(i < ret_numvals) { - int attnum; - HeapTuple typeTup; - Oid typinput; - Oid typelem; - FmgrInfo finfo; + 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 + { + Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN"); + + Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); + Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); + + rettup = trigdata->tg_trigtuple; + } + + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + Tcl_DStringFree(&tcl_trigtup); + Tcl_DStringFree(&tcl_newtup); /************************************************************ - * Ignore pseudo elements with a dot name + * Finally append the arguments from CREATE TRIGGER ************************************************************/ - if (*(ret_values[i]) == '.') { - i += 2; - continue; + for (i = 0; i < trigdata->tg_trigger->tgnargs; i++) + { + Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]); } /************************************************************ - * Get the attribute number + * Call the Tcl function + ************************************************************/ + tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); + Tcl_DStringFree(&tcl_cmd); + + /************************************************************ + * Check the return code from Tcl and handle + * our special restart mechanism to get rid + * of all nested call levels on transaction + * abort. ************************************************************/ - attnum = SPI_fnumber(tupdesc, ret_values[i++]); - if (attnum == SPI_ERROR_NOATTRIBUTE) { - elog(ERROR, "pltcl: invalid attribute '%s'", ret_values[--i]); + if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress) + { + if (!pltcl_restart_in_progress) + { + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); + } + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } + + switch (tcl_rc) + { + case TCL_OK: + break; + + default: + elog(ERROR, "pltcl: unsupported TCL return code %d", tcl_rc); } /************************************************************ - * Lookup the attribute type in the syscache - * for the input function + * The return value from the procedure might be one of + * the magic strings OK or SKIP or a list from array get ************************************************************/ - typeTup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", - ret_values[--i], - ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid)); + if (strcmp(pltcl_safe_interp->result, "OK") == 0) + { + return rettup; + } + if (strcmp(pltcl_safe_interp->result, "SKIP") == 0) + { + return (HeapTuple) NULL;; } - typinput = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typinput); - typelem = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typelem); /************************************************************ - * Set the attribute to NOT NULL and convert the contents + * Convert the result value from the safe interpreter + * and setup structures for SPI_modifytuple(); ************************************************************/ - modnulls[attnum - 1] = ' '; - fmgr_info(typinput, &finfo); - modvalues[attnum - 1] = (Datum)(*fmgr_faddr(&finfo)) - (ret_values[i++], - typelem, - (!VARLENA_FIXED_SIZE(tupdesc->attrs[attnum - 1])) - ? tupdesc->attrs[attnum - 1]->attlen - : tupdesc->attrs[attnum - 1]->atttypmod - ); - } + if (Tcl_SplitList(pltcl_safe_interp, pltcl_safe_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); + } + if (ret_numvals % 2 != 0) + { + ckfree(ret_values); + elog(ERROR, "pltcl: invalid return list from trigger - must have even # of elements"); + } - rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts, - modattrs, modvalues, modnulls); + 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; + } - pfree(modattrs); - pfree(modvalues); - pfree(modnulls); + modnulls = palloc(tupdesc->natts + 1); + memset(modnulls, 'n', tupdesc->natts); + modnulls[tupdesc->natts] = '\0'; - if (rettup == NULL) { - elog(ERROR, "pltcl: SPI_modifytuple() failed - RC = %d\n", SPI_result); - } + /************************************************************ + * Care for possible elog(ERROR)'s below + ************************************************************/ + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + ckfree(ret_values); + pltcl_restart_in_progress = 1; + if (--pltcl_call_level == 0) + { + pltcl_restart_in_progress = 0; + } + siglongjmp(Warn_restart, 1); + } - ckfree(ret_values); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + i = 0; + while (i < ret_numvals) + { + int attnum; + HeapTuple typeTup; + Oid typinput; + Oid typelem; + FmgrInfo finfo; + + /************************************************************ + * Ignore pseudo elements with a dot name + ************************************************************/ + if (*(ret_values[i]) == '.') + { + i += 2; + continue; + } - return rettup; + /************************************************************ + * Get the attribute number + ************************************************************/ + attnum = SPI_fnumber(tupdesc, ret_values[i++]); + if (attnum == SPI_ERROR_NOATTRIBUTE) + { + elog(ERROR, "pltcl: invalid attribute '%s'", ret_values[--i]); + } + + /************************************************************ + * Lookup the attribute type in the syscache + * for the input function + ************************************************************/ + typeTup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", + ret_values[--i], + ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid)); + } + typinput = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typinput); + typelem = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typelem); + + /************************************************************ + * Set the attribute to NOT NULL and convert the contents + ************************************************************/ + modnulls[attnum - 1] = ' '; + fmgr_info(typinput, &finfo); + modvalues[attnum - 1] = (Datum) (*fmgr_faddr(&finfo)) + (ret_values[i++], + typelem, + (!VARLENA_FIXED_SIZE(tupdesc->attrs[attnum - 1])) + ? tupdesc->attrs[attnum - 1]->attlen + : tupdesc->attrs[attnum - 1]->atttypmod + ); + } + + + rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts, + modattrs, modvalues, modnulls); + + pfree(modattrs); + pfree(modvalues); + pfree(modnulls); + + if (rettup == NULL) + { + elog(ERROR, "pltcl: SPI_modifytuple() failed - RC = %d\n", SPI_result); + } + + ckfree(ret_values); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + + return rettup; } /********************************************************************** * pltcl_elog() - elog() support for PLTcl **********************************************************************/ -static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]) +static int +pltcl_elog(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]) { - int level; - sigjmp_buf save_restart; - - /************************************************************ - * Suppress messages during the restart process - ************************************************************/ - if (pltcl_restart_in_progress) - return TCL_ERROR; - - /************************************************************ - * Catch the restart longjmp and begin a controlled - * return though all interpreter levels if it happens - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - return TCL_ERROR; - } - - if (argc != 3) { - Tcl_SetResult(interp, "syntax error - 'elog level msg'", - TCL_VOLATILE); - return TCL_ERROR; - } - - if (strcmp(argv[1], "NOTICE") == 0) { - level = NOTICE; - } else - if (strcmp(argv[1], "WARN") == 0) { - level = ERROR; - } else - if (strcmp(argv[1], "ERROR") == 0) { - level = ERROR; - } else - if (strcmp(argv[1], "FATAL") == 0) { - level = FATAL; - } else - if (strcmp(argv[1], "DEBUG") == 0) { - level = DEBUG; - } else - if (strcmp(argv[1], "NOIND") == 0) { - level = NOIND; - } else { - Tcl_AppendResult(interp, "Unknown elog level '", argv[1], - "'", NULL); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_ERROR; - } - - /************************************************************ - * Call elog(), restore the original restart address - * and return to the caller (if not catched) - ************************************************************/ - elog(level, argv[2]); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_OK; + int level; + sigjmp_buf save_restart; + + /************************************************************ + * Suppress messages during the restart process + ************************************************************/ + if (pltcl_restart_in_progress) + return TCL_ERROR; + + /************************************************************ + * Catch the restart longjmp and begin a controlled + * return though all interpreter levels if it happens + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + return TCL_ERROR; + } + + if (argc != 3) + { + Tcl_SetResult(interp, "syntax error - 'elog level msg'", + TCL_VOLATILE); + return TCL_ERROR; + } + + if (strcmp(argv[1], "NOTICE") == 0) + { + level = NOTICE; + } + else if (strcmp(argv[1], "WARN") == 0) + { + level = ERROR; + } + else if (strcmp(argv[1], "ERROR") == 0) + { + level = ERROR; + } + else if (strcmp(argv[1], "FATAL") == 0) + { + level = FATAL; + } + else if (strcmp(argv[1], "DEBUG") == 0) + { + level = DEBUG; + } + else if (strcmp(argv[1], "NOIND") == 0) + { + level = NOIND; + } + else + { + Tcl_AppendResult(interp, "Unknown elog level '", argv[1], + "'", NULL); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_ERROR; + } + + /************************************************************ + * Call elog(), restore the original restart address + * and return to the caller (if not catched) + ************************************************************/ + elog(level, argv[2]); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_OK; } @@ -1211,50 +1325,57 @@ static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, * pltcl_quote() - quote literal strings that are to * be used in SPI_exec query strings **********************************************************************/ -static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]) +static int +pltcl_quote(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]) { - char *tmp; - char *cp1; - char *cp2; - - /************************************************************ - * Check call syntax - ************************************************************/ - if (argc != 2) { - Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE); - 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; + char *tmp; + char *cp1; + char *cp2; + + /************************************************************ + * Check call syntax + ************************************************************/ + if (argc != 2) + { + Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE); + 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; } @@ -1262,211 +1383,231 @@ static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, * pltcl_SPI_exec() - The builtin SPI_exec command * for the safe interpreter **********************************************************************/ -static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]) +static int +pltcl_SPI_exec(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]) { - int spi_rc; - char buf[64]; - int count = 0; - char *arrayname = NULL; - int query_idx; - int i; - int loop_rc; - int ntuples; - HeapTuple *tuples; - TupleDesc tupdesc; - sigjmp_buf save_restart; - - char *usage = "syntax error - 'SPI_exec " - "?-count n? " - "?-array name? query ?loop body?"; - - /************************************************************ - * Don't do anything if we are already in restart mode - ************************************************************/ - if (pltcl_restart_in_progress) - return TCL_ERROR; - - /************************************************************ - * Check the call syntax and get the count option - ************************************************************/ - if (argc < 2) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); - return TCL_ERROR; - } - - i = 1; - while (i < argc) { - if (strcmp(argv[i], "-array") == 0) { - if (++i >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); + int spi_rc; + char buf[64]; + int count = 0; + char *arrayname = NULL; + int query_idx; + int i; + int loop_rc; + int ntuples; + HeapTuple *tuples; + TupleDesc tupdesc; + sigjmp_buf save_restart; + + char *usage = "syntax error - 'SPI_exec " + "?-count n? " + "?-array name? query ?loop body?"; + + /************************************************************ + * Don't do anything if we are already in restart mode + ************************************************************/ + if (pltcl_restart_in_progress) + return TCL_ERROR; + + /************************************************************ + * Check the call syntax and get the count option + ************************************************************/ + if (argc < 2) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + + i = 1; + while (i < argc) + { + if (strcmp(argv[i], "-array") == 0) + { + if (++i >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + arrayname = argv[i++]; + continue; + } + + if (strcmp(argv[i], "-count") == 0) + { + if (++i >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK) + { + return TCL_ERROR; + } + continue; + } + + break; + } + + query_idx = i; + if (query_idx >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Prepare to start a controlled return through all + * interpreter levels on transaction abort + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Execute the query and handle return codes + ************************************************************/ + spi_rc = SPI_exec(argv[query_idx], count); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + + switch (spi_rc) + { + case SPI_OK_UTILITY: + Tcl_SetResult(interp, "0", TCL_VOLATILE); + return TCL_OK; + + case SPI_OK_SELINTO: + case SPI_OK_INSERT: + case SPI_OK_DELETE: + case SPI_OK_UPDATE: + sprintf(buf, "%d", SPI_processed); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + return TCL_OK; + + case SPI_OK_SELECT: + break; + + case SPI_ERROR_ARGUMENT: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_UNCONNECTED: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_COPY: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_COPY", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_CURSOR: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_TRANSACTION: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_OPUNKNOWN: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN", + TCL_VOLATILE); + return TCL_ERROR; + + default: + sprintf(buf, "%d", spi_rc); + Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ", + "unknown RC ", buf, NULL); + return TCL_ERROR; + } + + /************************************************************ + * Only SELECT queries fall through to here - remember the + * tuples we got + ************************************************************/ + + ntuples = SPI_processed; + if (ntuples > 0) + { + tuples = SPI_tuptable->vals; + tupdesc = SPI_tuptable->tupdesc; + } + + /************************************************************ + * Again prepare for elog(ERROR) + ************************************************************/ + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); return TCL_ERROR; - } - arrayname = argv[i++]; - continue; } - if (strcmp(argv[i], "-count") == 0) { - if (++i >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); + /************************************************************ + * If there is no loop body given, just set the variables + * from the first tuple (if any) and return the number of + * tuples selected + ************************************************************/ + if (argc == query_idx + 1) + { + if (ntuples > 0) + { + pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); + } + sprintf(buf, "%d", ntuples); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_OK; + } + + /************************************************************ + * There is a loop body - process all tuples and evaluate + * the body on each + ************************************************************/ + query_idx++; + for (i = 0; i < ntuples; i++) + { + pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc); + + loop_rc = Tcl_Eval(interp, argv[query_idx]); + + if (loop_rc == TCL_OK) + continue; + if (loop_rc == TCL_CONTINUE) + continue; + if (loop_rc == TCL_RETURN) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_RETURN; + } + if (loop_rc == TCL_BREAK) + break; + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_ERROR; - } - if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK) { - return TCL_ERROR; - } - continue; - } - - break; - } - - query_idx = i; - if (query_idx >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * Execute the query and handle return codes - ************************************************************/ - spi_rc = SPI_exec(argv[query_idx], count); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - - switch (spi_rc) { - case SPI_OK_UTILITY: - Tcl_SetResult(interp, "0", TCL_VOLATILE); - return TCL_OK; - - case SPI_OK_SELINTO: - case SPI_OK_INSERT: - case SPI_OK_DELETE: - case SPI_OK_UPDATE: - sprintf(buf, "%d", SPI_processed); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; - - case SPI_OK_SELECT: - break; - - case SPI_ERROR_ARGUMENT: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_UNCONNECTED: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_COPY: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_COPY", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_CURSOR: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_TRANSACTION: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_OPUNKNOWN: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN", - TCL_VOLATILE); - return TCL_ERROR; - - default: - sprintf(buf, "%d", spi_rc); - Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ", - "unknown RC ", buf, NULL); - return TCL_ERROR; - } - - /************************************************************ - * Only SELECT queries fall through to here - remember the - * tuples we got - ************************************************************/ - - ntuples = SPI_processed; - if (ntuples > 0) { - tuples = SPI_tuptable->vals; - tupdesc = SPI_tuptable->tupdesc; - } - - /************************************************************ - * Again prepare for elog(ERROR) - ************************************************************/ - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * If there is no loop body given, just set the variables - * from the first tuple (if any) and return the number of - * tuples selected - ************************************************************/ - if (argc == query_idx + 1) { - if (ntuples > 0) { - pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); - } - sprintf(buf, "%d", ntuples); + } + + /************************************************************ + * Finally return the number of tuples + ************************************************************/ + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + sprintf(buf, "%d", ntuples); Tcl_SetResult(interp, buf, TCL_VOLATILE); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_OK; - } - - /************************************************************ - * There is a loop body - process all tuples and evaluate - * the body on each - ************************************************************/ - query_idx++; - for (i = 0; i < ntuples; i++) { - pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc); - - loop_rc = Tcl_Eval(interp, argv[query_idx]); - - if (loop_rc == TCL_OK) continue; - if (loop_rc == TCL_CONTINUE) continue; - if (loop_rc == TCL_RETURN) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_RETURN; - } - if (loop_rc == TCL_BREAK) break; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_ERROR; - } - - /************************************************************ - * Finally return the number of tuples - ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - sprintf(buf, "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; } @@ -1478,682 +1619,744 @@ static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, * access. There is no chance to prepare * and not save the plan currently. **********************************************************************/ -static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]) +static int +pltcl_SPI_prepare(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]) { - int nargs; - char **args; - pltcl_query_desc *qdesc; - void *plan; - int i; - HeapTuple typeTup; - Tcl_HashEntry *hashent; - int hashnew; - sigjmp_buf save_restart; - - /************************************************************ - * Don't do anything if we are already in restart mode - ************************************************************/ - if (pltcl_restart_in_progress) - return TCL_ERROR; - - /************************************************************ - * Check the call syntax - ************************************************************/ - if (argc != 3) { - Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'", - TCL_VOLATILE); - 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)); - sprintf(qdesc->qname, "%lx", (long)qdesc); - qdesc->nargs = nargs; - qdesc->argtypes = (Oid *)malloc(nargs * sizeof(Oid)); - qdesc->arginfuncs = (FmgrInfo *)malloc(nargs * sizeof(FmgrInfo)); - qdesc->argtypelems = (Oid *)malloc(nargs * sizeof(Oid)); - qdesc->argvalues = (Datum *)malloc(nargs * sizeof(Datum)); - qdesc->arglen = (int *)malloc(nargs * sizeof(int)); - - /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - free(qdesc->argtypes); - free(qdesc->arginfuncs); - free(qdesc->argtypelems); - free(qdesc->argvalues); - free(qdesc->arglen); - free(qdesc); - ckfree(args); - return TCL_ERROR; - } - - /************************************************************ - * Lookup the argument types by name in the system cache - * and remember the required information for input conversion - ************************************************************/ - for (i = 0; i < nargs; i++) { - typeTup = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(args[i]), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - elog(ERROR, "pltcl: Cache lookup of type %s failed", args[i]); - } - qdesc->argtypes[i] = typeTup->t_oid; - fmgr_info(((TypeTupleForm) GETSTRUCT(typeTup))->typinput, - &(qdesc->arginfuncs[i])); - qdesc->argtypelems[i] = ((TypeTupleForm) GETSTRUCT(typeTup))->typelem; - qdesc->argvalues[i] = (Datum)NULL; - qdesc->arglen[i] = (int)(((TypeTupleForm) GETSTRUCT(typeTup))->typlen); - } - - /************************************************************ - * Prepare the plan and check for errors - ************************************************************/ - plan = SPI_prepare(argv[1], nargs, qdesc->argtypes); - - if (plan == NULL) { - char buf[128]; - char *reason; + int nargs; + char **args; + pltcl_query_desc *qdesc; + void *plan; + int i; + HeapTuple typeTup; + Tcl_HashEntry *hashent; + int hashnew; + sigjmp_buf save_restart; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + /************************************************************ + * Don't do anything if we are already in restart mode + ************************************************************/ + if (pltcl_restart_in_progress) + return TCL_ERROR; - switch(SPI_result) { - case SPI_ERROR_ARGUMENT: - reason = "SPI_ERROR_ARGUMENT"; - break; + /************************************************************ + * Check the call syntax + ************************************************************/ + if (argc != 3) + { + Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'", + TCL_VOLATILE); + return TCL_ERROR; + } - case SPI_ERROR_UNCONNECTED: - reason = "SPI_ERROR_UNCONNECTED"; - break; + /************************************************************ + * Split the argument type list + ************************************************************/ + if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK) + { + return TCL_ERROR; + } - case SPI_ERROR_COPY: - reason = "SPI_ERROR_COPY"; - break; + /************************************************************ + * Allocate the new querydesc structure + ************************************************************/ + qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc)); + sprintf(qdesc->qname, "%lx", (long) qdesc); + qdesc->nargs = nargs; + qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid)); + qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo)); + qdesc->argtypelems = (Oid *) malloc(nargs * sizeof(Oid)); + qdesc->argvalues = (Datum *) malloc(nargs * sizeof(Datum)); + qdesc->arglen = (int *) malloc(nargs * sizeof(int)); - case SPI_ERROR_CURSOR: - reason = "SPI_ERROR_CURSOR"; - break; + /************************************************************ + * Prepare to start a controlled return through all + * interpreter levels on transaction abort + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + free(qdesc->argtypes); + free(qdesc->arginfuncs); + free(qdesc->argtypelems); + free(qdesc->argvalues); + free(qdesc->arglen); + free(qdesc); + ckfree(args); + return TCL_ERROR; + } - case SPI_ERROR_TRANSACTION: - reason = "SPI_ERROR_TRANSACTION"; - break; + /************************************************************ + * Lookup the argument types by name in the system cache + * and remember the required information for input conversion + ************************************************************/ + for (i = 0; i < nargs; i++) + { + typeTup = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(args[i]), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + elog(ERROR, "pltcl: Cache lookup of type %s failed", args[i]); + } + qdesc->argtypes[i] = typeTup->t_oid; + fmgr_info(((TypeTupleForm) GETSTRUCT(typeTup))->typinput, + &(qdesc->arginfuncs[i])); + qdesc->argtypelems[i] = ((TypeTupleForm) GETSTRUCT(typeTup))->typelem; + qdesc->argvalues[i] = (Datum) NULL; + qdesc->arglen[i] = (int) (((TypeTupleForm) GETSTRUCT(typeTup))->typlen); + } - case SPI_ERROR_OPUNKNOWN: - reason = "SPI_ERROR_OPUNKNOWN"; - break; + /************************************************************ + * Prepare the plan and check for errors + ************************************************************/ + plan = SPI_prepare(argv[1], nargs, qdesc->argtypes); - default: - sprintf(buf, "unknown RC %d", SPI_result); - reason = buf; - break; - - } + if (plan == NULL) + { + char buf[128]; + char *reason; - elog(ERROR, "pltcl: SPI_prepare() failed - %s", reason); - } + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - /************************************************************ - * Save the plan - ************************************************************/ - qdesc->plan = SPI_saveplan(plan); - if (qdesc->plan == NULL) { - char buf[128]; - char *reason; + switch (SPI_result) + { + case SPI_ERROR_ARGUMENT: + reason = "SPI_ERROR_ARGUMENT"; + break; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + case SPI_ERROR_UNCONNECTED: + reason = "SPI_ERROR_UNCONNECTED"; + break; - switch(SPI_result) { - case SPI_ERROR_ARGUMENT: - reason = "SPI_ERROR_ARGUMENT"; - break; + case SPI_ERROR_COPY: + reason = "SPI_ERROR_COPY"; + break; - case SPI_ERROR_UNCONNECTED: - reason = "SPI_ERROR_UNCONNECTED"; - break; + case SPI_ERROR_CURSOR: + reason = "SPI_ERROR_CURSOR"; + break; - default: - sprintf(buf, "unknown RC %d", SPI_result); - reason = buf; - break; - + case SPI_ERROR_TRANSACTION: + reason = "SPI_ERROR_TRANSACTION"; + break; + + case SPI_ERROR_OPUNKNOWN: + reason = "SPI_ERROR_OPUNKNOWN"; + break; + + default: + sprintf(buf, "unknown RC %d", SPI_result); + reason = buf; + break; + + } + + elog(ERROR, "pltcl: SPI_prepare() failed - %s", reason); } - elog(ERROR, "pltcl: SPI_saveplan() failed - %s", reason); - } + /************************************************************ + * Save the plan + ************************************************************/ + qdesc->plan = SPI_saveplan(plan); + if (qdesc->plan == NULL) + { + char buf[128]; + char *reason; + + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + + switch (SPI_result) + { + case SPI_ERROR_ARGUMENT: + reason = "SPI_ERROR_ARGUMENT"; + break; + + case SPI_ERROR_UNCONNECTED: + reason = "SPI_ERROR_UNCONNECTED"; + break; + + default: + sprintf(buf, "unknown RC %d", SPI_result); + reason = buf; + break; + + } - /************************************************************ - * Insert a hashtable entry for the plan and return - * the key to the caller - ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - hashent = Tcl_CreateHashEntry(pltcl_query_hash, qdesc->qname, &hashnew); - Tcl_SetHashValue(hashent, (ClientData)qdesc); + elog(ERROR, "pltcl: SPI_saveplan() failed - %s", reason); + } + + /************************************************************ + * Insert a hashtable entry for the plan and return + * the key to the caller + ************************************************************/ + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + hashent = Tcl_CreateHashEntry(pltcl_query_hash, qdesc->qname, &hashnew); + Tcl_SetHashValue(hashent, (ClientData) qdesc); - Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE); - return TCL_OK; + Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE); + return TCL_OK; } /********************************************************************** * pltcl_SPI_execp() - Execute a prepared plan **********************************************************************/ -static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, - int argc, char *argv[]) +static int +pltcl_SPI_execp(ClientData cdata, Tcl_Interp * interp, + int argc, char *argv[]) { - int spi_rc; - char buf[64]; - int i, j; - int loop_body; - Tcl_HashEntry *hashent; - pltcl_query_desc *qdesc; - char *nulls = NULL; - char *arrayname = NULL; - int count = 0; - int callnargs; - static char **callargs = NULL; - int loop_rc; - int ntuples; - HeapTuple *tuples = NULL; - TupleDesc tupdesc = NULL; - sigjmp_buf save_restart; - - char *usage = "syntax error - 'SPI_execp " - "?-nulls string? ?-count n? " - "?-array name? query ?args? ?loop body?"; - - /************************************************************ - * Tidy up from an earlier abort - ************************************************************/ - if (callargs != NULL) { - ckfree(callargs); - callargs = NULL; - } - - /************************************************************ - * Don't do anything if we are already in restart mode - ************************************************************/ - if (pltcl_restart_in_progress) - return TCL_ERROR; - - /************************************************************ - * 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_VOLATILE); - return TCL_ERROR; - } - arrayname = argv[i++]; - continue; + int spi_rc; + char buf[64]; + int i, + j; + int loop_body; + Tcl_HashEntry *hashent; + pltcl_query_desc *qdesc; + char *nulls = NULL; + char *arrayname = NULL; + int count = 0; + int callnargs; + static char **callargs = NULL; + int loop_rc; + int ntuples; + HeapTuple *tuples = NULL; + TupleDesc tupdesc = NULL; + sigjmp_buf save_restart; + + char *usage = "syntax error - 'SPI_execp " + "?-nulls string? ?-count n? " + "?-array name? query ?args? ?loop body?"; + + /************************************************************ + * Tidy up from an earlier abort + ************************************************************/ + if (callargs != NULL) + { + ckfree(callargs); + callargs = NULL; } - if (strcmp(argv[i], "-nulls") == 0) { - if (++i >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); + + /************************************************************ + * Don't do anything if we are already in restart mode + ************************************************************/ + if (pltcl_restart_in_progress) return TCL_ERROR; - } - nulls = argv[i++]; - continue; + + /************************************************************ + * 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_VOLATILE); + return TCL_ERROR; + } + arrayname = argv[i++]; + continue; + } + if (strcmp(argv[i], "-nulls") == 0) + { + if (++i >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + nulls = argv[i++]; + continue; + } + if (strcmp(argv[i], "-count") == 0) + { + if (++i >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); + return TCL_ERROR; + } + if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK) + { + return TCL_ERROR; + } + continue; + } + + break; } - if (strcmp(argv[i], "-count") == 0) { - if (++i >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); + + /************************************************************ + * Check minimum call arguments + ************************************************************/ + if (i >= argc) + { + Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; - } - if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK) { - return TCL_ERROR; - } - continue; - } - - break; - } - - /************************************************************ - * Check minimum call arguments - ************************************************************/ - if (i >= argc) { - Tcl_SetResult(interp, usage, TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * Get the prepared plan descriptor by it's key - ************************************************************/ - hashent = Tcl_FindHashEntry(pltcl_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); - - /************************************************************ - * 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_VOLATILE); - 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_VOLATILE); - 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_VOLATILE); - if (callargs != NULL) { - ckfree(callargs); - callargs = NULL; - } - return TCL_ERROR; } /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort during the - * parse of the arguments + * Get the prepared plan descriptor by it's key ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - for (j = 0; j < callnargs; j++) { - if (qdesc->arglen[j] < 0 && - qdesc->argvalues[j] != (Datum)NULL) { - pfree((char *)(qdesc->argvalues[j])); - qdesc->argvalues[j] = (Datum)NULL; - } - } - ckfree(callargs); - callargs = NULL; - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); - return TCL_ERROR; + hashent = Tcl_FindHashEntry(pltcl_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); /************************************************************ - * Setup the value array for the SPI_execp() using - * the type specific input functions + * If a nulls string is given, check for correct length ************************************************************/ - for (j = 0; j < callnargs; j++) { - qdesc->argvalues[j] = (Datum)(*fmgr_faddr(&qdesc->arginfuncs[j])) - (callargs[j], - qdesc->argtypelems[j], - qdesc->arglen[j]); + if (nulls != NULL) + { + if (strlen(nulls) != qdesc->nargs) + { + Tcl_SetResult(interp, + "length of nulls string doesn't match # of arguments", + TCL_VOLATILE); + return TCL_ERROR; + } } /************************************************************ - * Free the splitted argument value list + * If there was a argtype list on preparation, we need + * an argument value list now ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - ckfree(callargs); - callargs = NULL; - } else { - callnargs = 0; - } - - /************************************************************ - * Remember the index of the last processed call - * argument - a loop body for SELECT might follow - ************************************************************/ - loop_body = i; - - /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - for (j = 0; j < callnargs; j++) { - if (qdesc->arglen[j] < 0 && qdesc->argvalues[j] != (Datum)NULL) { - pfree((char *)(qdesc->argvalues[j])); - qdesc->argvalues[j] = (Datum)NULL; - } - } - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * Execute the plan - ************************************************************/ - spi_rc = SPI_execp(qdesc->plan, qdesc->argvalues, nulls, count); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - - /************************************************************ - * For varlena data types, free the argument values - ************************************************************/ - for (j = 0; j < callnargs; j++) { - if (qdesc->arglen[j] < 0 && qdesc->argvalues[j] != (Datum)NULL) { - pfree((char *)(qdesc->argvalues[j])); - qdesc->argvalues[j] = (Datum)NULL; - } - } - - /************************************************************ - * Check the return code from SPI_execp() - ************************************************************/ - switch (spi_rc) { - case SPI_OK_UTILITY: - Tcl_SetResult(interp, "0", TCL_VOLATILE); - return TCL_OK; - - case SPI_OK_SELINTO: - case SPI_OK_INSERT: - case SPI_OK_DELETE: - case SPI_OK_UPDATE: - sprintf(buf, "%d", SPI_processed); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; - - case SPI_OK_SELECT: - break; - - case SPI_ERROR_ARGUMENT: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_UNCONNECTED: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_COPY: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_COPY", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_CURSOR: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_TRANSACTION: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION", - TCL_VOLATILE); - return TCL_ERROR; - - case SPI_ERROR_OPUNKNOWN: - Tcl_SetResult(interp, - "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN", - TCL_VOLATILE); - return TCL_ERROR; - - default: - sprintf(buf, "%d", spi_rc); - Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ", - "unknown RC ", buf, NULL); - return TCL_ERROR; - } - - /************************************************************ - * Only SELECT queries fall through to here - remember the - * tuples we got - ************************************************************/ - - ntuples = SPI_processed; - if (ntuples > 0) { - tuples = SPI_tuptable->vals; - tupdesc = SPI_tuptable->tupdesc; - } - - /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort during - * the ouput conversions of the results - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); - return TCL_ERROR; - } - - /************************************************************ - * If there is no loop body given, just set the variables - * from the first tuple (if any) and return the number of - * tuples selected - ************************************************************/ - if (loop_body >= argc) { - if (ntuples > 0) { - pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); - } - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - sprintf(buf, "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; - } - - /************************************************************ - * 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, argv[loop_body]); - - if (loop_rc == TCL_OK) continue; - if (loop_rc == TCL_CONTINUE) continue; - if (loop_rc == TCL_RETURN) { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_RETURN; - } - if (loop_rc == TCL_BREAK) break; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_ERROR; - } - - /************************************************************ - * Finally return the number of tuples - ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - sprintf(buf, "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; -} + if (qdesc->nargs > 0) + { + if (i >= argc) + { + Tcl_SetResult(interp, "missing argument list", TCL_VOLATILE); + return TCL_ERROR; + } + /************************************************************ + * Split the argument values + ************************************************************/ + if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK) + { + return TCL_ERROR; + } -/********************************************************************** - * pltcl_set_tuple_values() - Set variables for all attributes - * of a given tuple - **********************************************************************/ -static void pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname, - int tupno, HeapTuple tuple, TupleDesc tupdesc) -{ - int i; - char *outputstr; - char buf[64]; - Datum attr; - bool isnull; + /************************************************************ + * Check that the # of arguments matches + ************************************************************/ + if (callnargs != qdesc->nargs) + { + Tcl_SetResult(interp, + "argument list length doesn't match # of arguments for query", + TCL_VOLATILE); + if (callargs != NULL) + { + ckfree(callargs); + callargs = NULL; + } + return TCL_ERROR; + } - char *attname; - HeapTuple typeTup; - Oid typoutput; - Oid typelem; + /************************************************************ + * Prepare to start a controlled return through all + * interpreter levels on transaction abort during the + * parse of the arguments + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + for (j = 0; j < callnargs; j++) + { + if (qdesc->arglen[j] < 0 && + qdesc->argvalues[j] != (Datum) NULL) + { + pfree((char *) (qdesc->argvalues[j])); + qdesc->argvalues[j] = (Datum) NULL; + } + } + ckfree(callargs); + callargs = NULL; + pltcl_restart_in_progress = 1; + Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + return TCL_ERROR; + } - char **arrptr; - char **nameptr; - char *nullname = NULL; + /************************************************************ + * Setup the value array for the SPI_execp() using + * the type specific input functions + ************************************************************/ + for (j = 0; j < callnargs; j++) + { + qdesc->argvalues[j] = (Datum) (*fmgr_faddr(&qdesc->arginfuncs[j])) + (callargs[j], + qdesc->argtypelems[j], + qdesc->arglen[j]); + } - /************************************************************ - * 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; - sprintf(buf, "%d", tupno); - Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0); - } + /************************************************************ + * Free the splitted argument value list + ************************************************************/ + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + ckfree(callargs); + callargs = NULL; + } + else + { + callnargs = 0; + } - for (i = 0; i < tupdesc->natts; i++) { /************************************************************ - * Get the attribute name + * Remember the index of the last processed call + * argument - a loop body for SELECT might follow ************************************************************/ - attname = tupdesc->attrs[i]->attname.data; + loop_body = i; /************************************************************ - * Get the attributes value + * Prepare to start a controlled return through all + * interpreter levels on transaction abort ************************************************************/ - attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); - + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + for (j = 0; j < callnargs; j++) + { + if (qdesc->arglen[j] < 0 && qdesc->argvalues[j] != (Datum) NULL) + { + pfree((char *) (qdesc->argvalues[j])); + qdesc->argvalues[j] = (Datum) NULL; + } + } + pltcl_restart_in_progress = 1; + Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + return TCL_ERROR; + } + + /************************************************************ + * Execute the plan + ************************************************************/ + spi_rc = SPI_execp(qdesc->plan, qdesc->argvalues, nulls, count); + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + /************************************************************ - * Lookup the attribute type in the syscache - * for the output function + * For varlena data types, free the argument values ************************************************************/ - typeTup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", - attname, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid)); + for (j = 0; j < callnargs; j++) + { + if (qdesc->arglen[j] < 0 && qdesc->argvalues[j] != (Datum) NULL) + { + pfree((char *) (qdesc->argvalues[j])); + qdesc->argvalues[j] = (Datum) NULL; + } } - typoutput = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typoutput); - typelem = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typelem); + /************************************************************ + * Check the return code from SPI_execp() + ************************************************************/ + switch (spi_rc) + { + case SPI_OK_UTILITY: + Tcl_SetResult(interp, "0", TCL_VOLATILE); + return TCL_OK; + + case SPI_OK_SELINTO: + case SPI_OK_INSERT: + case SPI_OK_DELETE: + case SPI_OK_UPDATE: + sprintf(buf, "%d", SPI_processed); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + return TCL_OK; + + case SPI_OK_SELECT: + break; + + case SPI_ERROR_ARGUMENT: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_UNCONNECTED: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_COPY: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_COPY", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_CURSOR: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_TRANSACTION: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION", + TCL_VOLATILE); + return TCL_ERROR; + + case SPI_ERROR_OPUNKNOWN: + Tcl_SetResult(interp, + "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN", + TCL_VOLATILE); + return TCL_ERROR; + + default: + sprintf(buf, "%d", spi_rc); + Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ", + "unknown RC ", buf, NULL); + return TCL_ERROR; + } /************************************************************ - * 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. + * Only SELECT queries fall through to here - remember the + * tuples we got ************************************************************/ - if (!isnull && OidIsValid(typoutput)) { - FmgrInfo finfo; - fmgr_info(typoutput, &finfo); + ntuples = SPI_processed; + if (ntuples > 0) + { + tuples = SPI_tuptable->vals; + tupdesc = SPI_tuptable->tupdesc; + } + + /************************************************************ + * Prepare to start a controlled return through all + * interpreter levels on transaction abort during + * the ouput conversions of the results + ************************************************************/ + memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); + if (sigsetjmp(Warn_restart, 1) != 0) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + pltcl_restart_in_progress = 1; + Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + return TCL_ERROR; + } - outputstr = (*fmgr_faddr(&finfo)) - (attr, typelem, - tupdesc->attrs[i]->attlen); + /************************************************************ + * If there is no loop body given, just set the variables + * from the first tuple (if any) and return the number of + * tuples selected + ************************************************************/ + if (loop_body >= argc) + { + if (ntuples > 0) + { + pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); + } + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + sprintf(buf, "%d", ntuples); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + return TCL_OK; + } - Tcl_SetVar2(interp, *arrptr, *nameptr, outputstr, 0); - pfree(outputstr); - } else { - Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0); + /************************************************************ + * 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, argv[loop_body]); + + if (loop_rc == TCL_OK) + continue; + if (loop_rc == TCL_CONTINUE) + continue; + if (loop_rc == TCL_RETURN) + { + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_RETURN; + } + if (loop_rc == TCL_BREAK) + break; + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + return TCL_ERROR; } - } + + /************************************************************ + * Finally return the number of tuples + ************************************************************/ + memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + sprintf(buf, "%d", ntuples); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + return TCL_OK; } /********************************************************************** - * pltcl_build_tuple_argument() - Build a string usable for 'array set' - * from all attributes of a given tuple + * pltcl_set_tuple_values() - Set variables for all attributes + * of a given tuple **********************************************************************/ -static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, - Tcl_DString *retval) +static void +pltcl_set_tuple_values(Tcl_Interp * interp, char *arrayname, + int tupno, HeapTuple tuple, TupleDesc tupdesc) { - int i; - char *outputstr; - Datum attr; - bool isnull; + int i; + char *outputstr; + char buf[64]; + Datum attr; + bool isnull; - char *attname; - HeapTuple typeTup; - Oid typoutput; - Oid typelem; + char *attname; + HeapTuple typeTup; + Oid typoutput; + Oid typelem; - for (i = 0; i < tupdesc->natts; i++) { - /************************************************************ - * Get the attribute name - ************************************************************/ - attname = tupdesc->attrs[i]->attname.data; + char **arrptr; + char **nameptr; + char *nullname = NULL; /************************************************************ - * Get the attributes value - ************************************************************/ - attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); - - /************************************************************ - * Lookup the attribute type in the syscache - * for the output function + * Prepare pointers for Tcl_SetVar2() below and in array + * mode set the .tupno element ************************************************************/ - typeTup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) { - elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", - attname, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid)); + if (arrayname == NULL) + { + arrptr = &attname; + nameptr = &nullname; + } + else + { + arrptr = &arrayname; + nameptr = &attname; + sprintf(buf, "%d", tupno); + Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0); } - typoutput = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typoutput); - typelem = (Oid) (((TypeTupleForm)GETSTRUCT(typeTup))->typelem); + for (i = 0; i < tupdesc->natts; i++) + { + /************************************************************ + * Get the attribute name + ************************************************************/ + attname = tupdesc->attrs[i]->attname.data; + + /************************************************************ + * Get the attributes value + ************************************************************/ + attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); + + /************************************************************ + * Lookup the attribute type in the syscache + * for the output function + ************************************************************/ + typeTup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", + attname, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid)); + } - /************************************************************ - * 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)) { - FmgrInfo finfo; + typoutput = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typoutput); + typelem = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typelem); + + /************************************************************ + * 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)) + { + FmgrInfo finfo; + + fmgr_info(typoutput, &finfo); + + outputstr = (*fmgr_faddr(&finfo)) + (attr, typelem, + tupdesc->attrs[i]->attlen); + + Tcl_SetVar2(interp, *arrptr, *nameptr, outputstr, 0); + pfree(outputstr); + } + else + { + Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0); + } + } +} - fmgr_info(typoutput, &finfo); - outputstr = (*fmgr_faddr(&finfo)) - (attr, typelem, - tupdesc->attrs[i]->attlen); +/********************************************************************** + * pltcl_build_tuple_argument() - Build a string usable for 'array set' + * from all attributes of a given tuple + **********************************************************************/ +static void +pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, + Tcl_DString * retval) +{ + int i; + char *outputstr; + Datum attr; + bool isnull; + + char *attname; + HeapTuple typeTup; + Oid typoutput; + Oid typelem; + + for (i = 0; i < tupdesc->natts; i++) + { + /************************************************************ + * Get the attribute name + ************************************************************/ + attname = tupdesc->attrs[i]->attname.data; + + /************************************************************ + * Get the attributes value + ************************************************************/ + attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); + + /************************************************************ + * Lookup the attribute type in the syscache + * for the output function + ************************************************************/ + typeTup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %ld failed", + attname, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid)); + } - Tcl_DStringAppendElement(retval, attname); - Tcl_DStringAppendElement(retval, outputstr); - pfree(outputstr); + typoutput = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typoutput); + typelem = (Oid) (((TypeTupleForm) GETSTRUCT(typeTup))->typelem); + + /************************************************************ + * 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)) + { + FmgrInfo finfo; + + fmgr_info(typoutput, &finfo); + + outputstr = (*fmgr_faddr(&finfo)) + (attr, typelem, + tupdesc->attrs[i]->attlen); + + Tcl_DStringAppendElement(retval, attname); + Tcl_DStringAppendElement(retval, outputstr); + pfree(outputstr); + } } - } } - - |