aboutsummaryrefslogtreecommitdiff
path: root/src/pl/tcl/pltcl.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-07-31 00:45:57 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-07-31 00:45:57 +0000
commita393fbf93763709f90ba1f968e50a35bd4cabcfb (patch)
tree955d74e7181214688b575f31c243005fe470dfe1 /src/pl/tcl/pltcl.c
parent94f8f63fdbcf61a56a23b8052d68fd78bec86a3b (diff)
downloadpostgresql-a393fbf93763709f90ba1f968e50a35bd4cabcfb.tar.gz
postgresql-a393fbf93763709f90ba1f968e50a35bd4cabcfb.zip
Restructure error handling as recently discussed. It is now really
possible to trap an error inside a function rather than letting it propagate out to PostgresMain. You still have to use AbortCurrentTransaction to clean up, but at least the error handling itself will cooperate.
Diffstat (limited to 'src/pl/tcl/pltcl.c')
-rw-r--r--src/pl/tcl/pltcl.c715
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;
}