diff options
Diffstat (limited to 'src/pl/tcl/pltcl.c')
-rw-r--r-- | src/pl/tcl/pltcl.c | 715 |
1 files changed, 323 insertions, 392 deletions
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index e51e56e6d74..05c906f30f2 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -31,7 +31,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.86 2004/06/06 00:41:28 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.87 2004/07/31 00:45:57 tgl Exp $ * **********************************************************************/ @@ -41,7 +41,6 @@ #include <unistd.h> #include <fcntl.h> -#include <setjmp.h> /* Hack to deal with Tcl 8.4 const-ification without losing compatibility */ #ifndef CONST84 @@ -132,8 +131,6 @@ typedef struct pltcl_query_desc **********************************************************************/ static bool pltcl_pm_init_done = false; static bool pltcl_be_init_done = false; -static int pltcl_call_level = 0; -static int pltcl_restart_in_progress = 0; static Tcl_Interp *pltcl_hold_interp = NULL; static Tcl_Interp *pltcl_norm_interp = NULL; static Tcl_Interp *pltcl_safe_interp = NULL; @@ -142,6 +139,19 @@ static Tcl_HashTable *pltcl_norm_query_hash = NULL; static Tcl_HashTable *pltcl_safe_query_hash = NULL; static FunctionCallInfo pltcl_current_fcinfo = NULL; +/* + * When a callback from Tcl into PG incurs an error, we temporarily store + * the error information here, and return TCL_ERROR to the Tcl interpreter. + * Any further callback attempts immediately fail, and when the Tcl interpreter + * returns to the calling function, we re-throw the error (even if Tcl + * thinks it trapped the error and doesn't return TCL_ERROR). Eventually + * this ought to be improved to let Tcl code really truly trap the error, + * but that's more of a change from the pre-7.5 semantics than I have time + * for now --- it will only be possible if the callback query is executed + * inside a subtransaction. + */ +static ErrorData *pltcl_error_in_progress = NULL; + /********************************************************************** * Forward declarations **********************************************************************/ @@ -397,21 +407,11 @@ pltcl_call_handler(PG_FUNCTION_ARGS) FunctionCallInfo save_fcinfo; /************************************************************ - * Initialize interpreters + * Initialize interpreters if first time through ************************************************************/ pltcl_init_all(); /************************************************************ - * Connect to SPI manager - ************************************************************/ - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "could not 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 ************************************************************/ @@ -430,8 +430,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS) pltcl_current_fcinfo = save_fcinfo; - pltcl_call_level--; - return retval; } @@ -461,7 +459,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS) int i; int tcl_rc; Datum retval; - sigjmp_buf save_restart; + + /* Connect to SPI manager */ + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "could not connect to SPI manager"); /* Find or compile the function */ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid); @@ -480,23 +481,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS) Tcl_DStringAppendElement(&tcl_cmd, prodesc->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 ************************************************************/ + PG_TRY(); + { for (i = 0; i < prodesc->nargs; i++) { if (prodesc->arg_is_rowtype[i]) @@ -552,60 +540,53 @@ pltcl_func_handler(PG_FUNCTION_ARGS) } } } + } + PG_CATCH(); + { + Tcl_DStringFree(&tcl_cmd); + Tcl_DStringFree(&list_tmp); + PG_RE_THROW(); + } + PG_END_TRY(); Tcl_DStringFree(&list_tmp); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /************************************************************ * Call the Tcl function + * + * We assume no PG error can be thrown directly from this call. ************************************************************/ tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ - * Check the return code from Tcl and handle - * our special restart mechanism to get rid - * of all nested call levels on transaction - * abort. + * If there was an error in a PG callback, propagate that + * no matter what Tcl claims about its success. ************************************************************/ - if (tcl_rc != TCL_OK || pltcl_restart_in_progress) + if (pltcl_error_in_progress) { - if (!pltcl_restart_in_progress) - { - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) - pltcl_restart_in_progress = 0; - UTF_BEGIN; - ereport(ERROR, - (errmsg("pltcl: %s", interp->result), - errdetail("%s", - UTF_U2E(Tcl_GetVar(interp, "errorInfo", - TCL_GLOBAL_ONLY))))); - UTF_END; - } - if (--pltcl_call_level == 0) - pltcl_restart_in_progress = 0; - siglongjmp(Warn_restart, 1); + ErrorData *edata = pltcl_error_in_progress; + + pltcl_error_in_progress = NULL; + ReThrowError(edata); } /************************************************************ - * Convert the result value from the Tcl interpreter - * into its PostgreSQL data format and return it. - * Again, the function call 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. + * Check for errors reported by Tcl itself. ************************************************************/ - if (sigsetjmp(Warn_restart, 1) != 0) + if (tcl_rc != TCL_OK) { - 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); + UTF_BEGIN; + ereport(ERROR, + (errmsg("pltcl: %s", interp->result), + errdetail("%s", + UTF_U2E(Tcl_GetVar(interp, "errorInfo", + TCL_GLOBAL_ONLY))))); + UTF_END; } /************************************************************ * Disconnect from SPI manager and then create the return - * values datum (if the input function does a palloc for it + * value datum (if the input function does a palloc for it * this must not be allocated in the SPI memory context * because SPI_finish would free it). But don't try to call * the result_in_func if we've been told to return a NULL; @@ -627,11 +608,6 @@ pltcl_func_handler(PG_FUNCTION_ARGS) UTF_END; } - /************************************************************ - * Finally we may restore normal error handling. - ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return retval; } @@ -653,15 +629,15 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) Tcl_DString tcl_newtup; int tcl_rc; int i; - int *modattrs; Datum *modvalues; char *modnulls; - int ret_numvals; CONST84 char **ret_values; - sigjmp_buf save_restart; + /* Connect to SPI manager */ + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "could not connect to SPI manager"); /* Find or compile the function */ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, @@ -681,23 +657,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) Tcl_DStringInit(&tcl_cmd); Tcl_DStringInit(&tcl_trigtup); Tcl_DStringInit(&tcl_newtup); - - /************************************************************ - * We call external functions below - care for elog(ERROR) - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) + PG_TRY(); { - 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, prodesc->proname); @@ -799,55 +760,54 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) else elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - Tcl_DStringFree(&tcl_trigtup); - Tcl_DStringFree(&tcl_newtup); - - /************************************************************ - * Finally append the arguments from CREATE TRIGGER - ************************************************************/ + /* Finally append the arguments from CREATE TRIGGER */ for (i = 0; i < trigdata->tg_trigger->tgnargs; i++) Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]); + } + PG_CATCH(); + { + Tcl_DStringFree(&tcl_cmd); + Tcl_DStringFree(&tcl_trigtup); + Tcl_DStringFree(&tcl_newtup); + PG_RE_THROW(); + } + PG_END_TRY(); + Tcl_DStringFree(&tcl_trigtup); + Tcl_DStringFree(&tcl_newtup); + /************************************************************ * Call the Tcl function + * + * We assume no PG error can be thrown directly from this call. ************************************************************/ tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ - * Check the return code from Tcl and handle - * our special restart mechanism to get rid - * of all nested call levels on transaction - * abort. + * If there was an error in a PG callback, propagate that + * no matter what Tcl claims about its success. ************************************************************/ - if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress) + if (pltcl_error_in_progress) { - if (!pltcl_restart_in_progress) - { - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) - pltcl_restart_in_progress = 0; - UTF_BEGIN; - ereport(ERROR, - (errmsg("pltcl: %s", interp->result), - errdetail("%s", - UTF_U2E(Tcl_GetVar(interp, "errorInfo", - TCL_GLOBAL_ONLY))))); - UTF_END; - } - if (--pltcl_call_level == 0) - pltcl_restart_in_progress = 0; - siglongjmp(Warn_restart, 1); + ErrorData *edata = pltcl_error_in_progress; + + pltcl_error_in_progress = NULL; + ReThrowError(edata); } - switch (tcl_rc) + /************************************************************ + * Check for errors reported by Tcl itself. + ************************************************************/ + if (tcl_rc != TCL_OK) { - case TCL_OK: - break; - - default: - elog(ERROR, "unsupported TCL return code: %d", tcl_rc); + UTF_BEGIN; + ereport(ERROR, + (errmsg("pltcl: %s", interp->result), + errdetail("%s", + UTF_U2E(Tcl_GetVar(interp, "errorInfo", + TCL_GLOBAL_ONLY))))); + UTF_END; } /************************************************************ @@ -871,11 +831,12 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) elog(ERROR, "could not split return value from trigger: %s", interp->result); - if (ret_numvals % 2 != 0) + /* Use a TRY to ensure ret_values will get freed */ + PG_TRY(); { - ckfree((char *) ret_values); + + if (ret_numvals % 2 != 0) elog(ERROR, "invalid return list from trigger - must have even # of elements"); - } modattrs = (int *) palloc(tupdesc->natts * sizeof(int)); modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum)); @@ -888,19 +849,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) modnulls = palloc(tupdesc->natts); memset(modnulls, 'n', tupdesc->natts); - /************************************************************ - * Care for possible elog(ERROR)'s below - ************************************************************/ - if (sigsetjmp(Warn_restart, 1) != 0) - { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - ckfree((char *) ret_values); - pltcl_restart_in_progress = 1; - if (--pltcl_call_level == 0) - pltcl_restart_in_progress = 0; - siglongjmp(Warn_restart, 1); - } - for (i = 0; i < ret_numvals; i += 2) { CONST84 char *ret_name = ret_values[i]; @@ -970,8 +918,14 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) if (rettup == NULL) elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result); + } + PG_CATCH(); + { + ckfree((char *) ret_values); + PG_RE_THROW(); + } + PG_END_TRY(); ckfree((char *) ret_values); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return rettup; } @@ -1317,13 +1271,16 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { volatile int level; - sigjmp_buf save_restart; + MemoryContext oldcontext; /************************************************************ - * Suppress messages during the restart process + * Suppress messages if an error is already declared ************************************************************/ - if (pltcl_restart_in_progress) + if (pltcl_error_in_progress) + { + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; + } if (argc != 3) { @@ -1354,26 +1311,25 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp, } /************************************************************ - * Catch the longjmp from elog() and begin a controlled - * return though all interpreter levels if it happens + * If elog() throws an error, catch and save it, then return + * error indication to Tcl interpreter. ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) + oldcontext = CurrentMemoryContext; + PG_TRY(); + { + UTF_BEGIN; + elog(level, "%s", UTF_U2E(argv[2])); + UTF_END; + } + PG_CATCH(); { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); return TCL_ERROR; } + PG_END_TRY(); - /************************************************************ - * Call elog(), restore the original restart address - * and return to the caller (if no longjmp) - ************************************************************/ - UTF_BEGIN; - elog(level, "%s", UTF_U2E(argv[2])); - UTF_END; - - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_OK; } @@ -1535,6 +1491,7 @@ static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { + volatile int my_rc; int spi_rc; char buf[64]; int count = 0; @@ -1546,17 +1503,20 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, HeapTuple *volatile tuples; volatile TupleDesc tupdesc = NULL; SPITupleTable *tuptable; - sigjmp_buf save_restart; + MemoryContext oldcontext; char *usage = "syntax error - 'SPI_exec " "?-count n? " "?-array name? query ?loop body?"; /************************************************************ - * Don't do anything if we are already in restart mode + * Don't do anything if we are already in error mode ************************************************************/ - if (pltcl_restart_in_progress) + if (pltcl_error_in_progress) + { + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; + } /************************************************************ * Check the call syntax and get the count option @@ -1604,25 +1564,24 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, } /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort + * Execute the query and handle return codes ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) + oldcontext = CurrentMemoryContext; + PG_TRY(); { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + UTF_BEGIN; + spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count); + UTF_END; + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; } - - /************************************************************ - * Execute the query and handle return codes - ************************************************************/ - UTF_BEGIN; - spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count); - UTF_END; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + PG_END_TRY(); switch (spi_rc) { @@ -1687,83 +1646,80 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, } /************************************************************ - * Only SELECT queries fall through to here - remember the - * tuples we got + * Only SELECT queries fall through to here - process the tuples we got ************************************************************/ - ntuples = SPI_processed; + tuptable = SPI_tuptable; 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); - snprintf(buf, sizeof(buf), "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - SPI_freetuptable(SPI_tuptable); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_OK; + tuples = tuptable->vals; + tupdesc = tuptable->tupdesc; } - tuptable = SPI_tuptable; - - /************************************************************ - * There is a loop body - process all tuples and evaluate - * the body on each - ************************************************************/ - query_idx++; - for (i = 0; i < ntuples; i++) + my_rc = TCL_OK; + PG_TRY(); { - pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc); + if (argc == query_idx + 1) + { + /************************************************************ + * If there is no loop body given, just set the variables + * from the first tuple (if any) + ************************************************************/ + if (ntuples > 0) + pltcl_set_tuple_values(interp, arrayname, 0, + tuples[0], tupdesc); + } + else + { + /************************************************************ + * There is a loop body - process all tuples and evaluate + * the body on each + ************************************************************/ + 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]); + 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) - { - SPI_freetuptable(tuptable); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_RETURN; + if (loop_rc == TCL_OK) + continue; + if (loop_rc == TCL_CONTINUE) + continue; + if (loop_rc == TCL_RETURN) + { + my_rc = TCL_RETURN; + break; + } + if (loop_rc == TCL_BREAK) + break; + my_rc = TCL_ERROR; + break; + } } - if (loop_rc == TCL_BREAK) - break; + SPI_freetuptable(tuptable); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; } - - SPI_freetuptable(tuptable); + PG_END_TRY(); /************************************************************ * Finally return the number of tuples ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - snprintf(buf, sizeof(buf), "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; + if (my_rc == TCL_OK) + { + snprintf(buf, sizeof(buf), "%d", ntuples); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + return my_rc; } @@ -1787,14 +1743,17 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, HeapTuple typeTup; Tcl_HashEntry *hashent; int hashnew; - sigjmp_buf save_restart; Tcl_HashTable *query_hash; + MemoryContext oldcontext; /************************************************************ - * Don't do anything if we are already in restart mode + * Don't do anything if we are already in error mode ************************************************************/ - if (pltcl_restart_in_progress) + if (pltcl_error_in_progress) + { + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; + } /************************************************************ * Check the call syntax @@ -1822,23 +1781,9 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo)); qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid)); - /************************************************************ - * 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) + oldcontext = CurrentMemoryContext; + PG_TRY(); { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - free(qdesc->argtypes); - free(qdesc->arginfuncs); - free(qdesc->argtypioparams); - free(qdesc); - ckfree((char *) args); - return TCL_ERROR; - } - /************************************************************ * Lookup the argument types by name in the system cache * and remember the required information for input conversion @@ -1882,10 +1827,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, UTF_END; if (plan == NULL) - { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); elog(ERROR, "SPI_prepare() failed"); - } /************************************************************ * Save the plan into permanent memory (right now it's in the @@ -1893,10 +1835,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, ************************************************************/ qdesc->plan = SPI_saveplan(plan); if (qdesc->plan == NULL) - { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); elog(ERROR, "SPI_saveplan() failed"); - } + /* Release the procCxt copy to avoid within-function memory leak */ SPI_freeplan(plan); @@ -1909,7 +1849,21 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, else query_hash = pltcl_safe_query_hash; - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + free(qdesc->argtypes); + free(qdesc->arginfuncs); + free(qdesc->argtypioparams); + free(qdesc); + ckfree((char *) args); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); + return TCL_ERROR; + } + PG_END_TRY(); hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew); Tcl_SetHashValue(hashent, (ClientData) qdesc); @@ -1928,6 +1882,7 @@ static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { + volatile int my_rc; int spi_rc; char buf[64]; volatile int i; @@ -1940,13 +1895,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, CONST84 char *volatile arrayname = NULL; int count = 0; int callnargs; - static CONST84 char **callargs = NULL; + CONST84 char **callargs; int loop_rc; int ntuples; HeapTuple *volatile tuples = NULL; volatile TupleDesc tupdesc = NULL; SPITupleTable *tuptable; - sigjmp_buf save_restart; + volatile MemoryContext oldcontext; Tcl_HashTable *query_hash; char *usage = "syntax error - 'SPI_execp " @@ -1954,19 +1909,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, "?-array name? query ?args? ?loop body?"; /************************************************************ - * Tidy up from an earlier abort + * Don't do anything if we are already in error mode ************************************************************/ - if (callargs != NULL) + if (pltcl_error_in_progress) { - ckfree((char *) callargs); - callargs = NULL; - } - - /************************************************************ - * Don't do anything if we are already in restart mode - ************************************************************/ - if (pltcl_restart_in_progress) + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; + } /************************************************************ * Get the options and check syntax @@ -2074,27 +2023,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, Tcl_SetResult(interp, "argument list length doesn't match # of arguments for query", TCL_VOLATILE); - if (callargs != NULL) - { - ckfree((char *) 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 - ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) - { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); ckfree((char *) callargs); - callargs = NULL; - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); return TCL_ERROR; } @@ -2102,33 +2031,42 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, * Setup the value array for the SPI_execp() using * the type specific input functions ************************************************************/ - argvalues = (Datum *) palloc(callnargs * sizeof(Datum)); - - for (j = 0; j < callnargs; j++) + oldcontext = CurrentMemoryContext; + PG_TRY(); { - if (nulls && nulls[j] == 'n') - { - /* don't try to convert the input for a null */ - argvalues[j] = (Datum) 0; - } - else + argvalues = (Datum *) palloc(callnargs * sizeof(Datum)); + + for (j = 0; j < callnargs; j++) { - UTF_BEGIN; - argvalues[j] = - FunctionCall3(&qdesc->arginfuncs[j], - CStringGetDatum(UTF_U2E(callargs[j])), - ObjectIdGetDatum(qdesc->argtypioparams[j]), - Int32GetDatum(-1)); - UTF_END; + if (nulls && nulls[j] == 'n') + { + /* don't try to convert the input for a null */ + argvalues[j] = (Datum) 0; + } + else + { + UTF_BEGIN; + argvalues[j] = + FunctionCall3(&qdesc->arginfuncs[j], + CStringGetDatum(UTF_U2E(callargs[j])), + ObjectIdGetDatum(qdesc->argtypioparams[j]), + Int32GetDatum(-1)); + UTF_END; + } } } + PG_CATCH(); + { + ckfree((char *) callargs); + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); + return TCL_ERROR; + } + PG_END_TRY(); - /************************************************************ - * Free the splitted argument value list - ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); ckfree((char *) callargs); - callargs = NULL; } else callnargs = 0; @@ -2140,23 +2078,22 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, loop_body = i; /************************************************************ - * Prepare to start a controlled return through all - * interpreter levels on transaction abort + * Execute the plan ************************************************************/ - memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); - if (sigsetjmp(Warn_restart, 1) != 0) + oldcontext = CurrentMemoryContext; + PG_TRY(); { - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - pltcl_restart_in_progress = 1; - Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); + spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count); + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; } - - /************************************************************ - * Execute the plan - ************************************************************/ - spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + PG_END_TRY(); /************************************************************ * Check the return code from SPI_execp() @@ -2224,85 +2161,79 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, } /************************************************************ - * Only SELECT queries fall through to here - remember the - * tuples we got + * Only SELECT queries fall through to here - process the tuples we got ************************************************************/ - ntuples = SPI_processed; + tuptable = SPI_tuptable; if (ntuples > 0) { - tuples = SPI_tuptable->vals; - tupdesc = SPI_tuptable->tupdesc; + tuples = tuptable->vals; + tupdesc = 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) + my_rc = TCL_OK; + PG_TRY(); { - 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)); - snprintf(buf, sizeof(buf), "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - SPI_freetuptable(SPI_tuptable); - return TCL_OK; - } - - tuptable = SPI_tuptable; - - /************************************************************ - * 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); + if (loop_body >= argc) + { + /************************************************************ + * If there is no loop body given, just set the variables + * from the first tuple (if any) + ************************************************************/ + if (ntuples > 0) + pltcl_set_tuple_values(interp, arrayname, 0, + tuples[0], tupdesc); + } + else + { + /************************************************************ + * There is a loop body - process all tuples and evaluate + * the body on each + ************************************************************/ + for (i = 0; i < ntuples; i++) + { + pltcl_set_tuple_values(interp, arrayname, i, + tuples[i], tupdesc); - loop_rc = Tcl_Eval(interp, argv[loop_body]); + 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) - { - SPI_freetuptable(tuptable); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - return TCL_RETURN; + if (loop_rc == TCL_OK) + continue; + if (loop_rc == TCL_CONTINUE) + continue; + if (loop_rc == TCL_RETURN) + { + my_rc = TCL_RETURN; + break; + } + if (loop_rc == TCL_BREAK) + break; + my_rc = TCL_ERROR; + break; + } } - if (loop_rc == TCL_BREAK) - break; + SPI_freetuptable(tuptable); - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + pltcl_error_in_progress = CopyErrorData(); + FlushErrorState(); + Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE); return TCL_ERROR; } - - SPI_freetuptable(tuptable); + PG_END_TRY(); /************************************************************ * Finally return the number of tuples ************************************************************/ - memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); - snprintf(buf, sizeof(buf), "%d", ntuples); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - return TCL_OK; + if (my_rc == TCL_OK) + { + snprintf(buf, sizeof(buf), "%d", ntuples); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + return my_rc; } |