* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.189 2004/07/21 20:34:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.190 2004/07/31 00:45:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <unistd.h>
#include <signal.h>
-#include <setjmp.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
for (i = 0; i < HASHTABLESIZE; ++i)
hashtable[i] = NULL;
- /*
- * abort processing resumes here (this is probably dead code?)
- */
- if (sigsetjmp(Warn_restart, 1) != 0)
- {
- Warnings++;
- AbortCurrentTransaction();
- }
-
/*
* Process bootstrap input.
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.227 2004/06/16 01:26:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.228 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h>
static bool line_buf_converted;
/* non-export function prototypes */
+static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
+ char *delim, char *null_print, bool csv_mode, char *quote,
+ char *escape, List *force_quote_atts, bool fe_copy);
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
List *force_quote_atts);
ListCell *option;
List *attnamelist = stmt->attlist;
List *attnumlist;
+ bool fe_copy = false;
bool binary = false;
bool oids = false;
bool csv_mode = false;
if (pipe)
{
if (whereToSendOutput == Remote)
- SendCopyBegin(binary, list_length(attnumlist));
+ fe_copy = true;
else
copy_file = stdout;
}
errmsg("\"%s\" is a directory", filename)));
}
}
- CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
- quote, escape, force_quote_atts);
+
+ DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
+ quote, escape, force_quote_atts, fe_copy);
}
if (!pipe)
errmsg("could not write to file \"%s\": %m",
filename)));
}
- else if (whereToSendOutput == Remote && !is_from)
- SendCopyEnd(binary);
pfree(attribute_buf.data);
pfree(line_buf.data);
}
+/*
+ * This intermediate routine just exists to localize the effects of setjmp
+ * so we don't need to plaster a lot of variables with "volatile".
+ */
+static void
+DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
+ char *delim, char *null_print, bool csv_mode, char *quote,
+ char *escape, List *force_quote_atts, bool fe_copy)
+{
+ PG_TRY();
+ {
+ if (fe_copy)
+ SendCopyBegin(binary, list_length(attnumlist));
+
+ CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
+ quote, escape, force_quote_atts);
+
+ if (fe_copy)
+ SendCopyEnd(binary);
+ }
+ PG_CATCH();
+ {
+ /*
+ * Make sure we turn off old-style COPY OUT mode upon error.
+ * It is okay to do this in all cases, since it does nothing
+ * if the mode is not on.
+ */
+ pq_endcopyout(true);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+}
+
/*
* Copy from relation TO file.
*/
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.29 2004/07/17 03:28:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.30 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* We must make the portal's resource owner current */
saveResourceOwner = CurrentResourceOwner;
- CurrentResourceOwner = portal->resowner;
- ExecutorEnd(queryDesc);
+ PG_TRY();
+ {
+ CurrentResourceOwner = portal->resowner;
+ ExecutorEnd(queryDesc);
+ }
+ PG_CATCH();
+ {
+ /* Ensure CurrentResourceOwner is restored on error */
+ CurrentResourceOwner = saveResourceOwner;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
CurrentResourceOwner = saveResourceOwner;
}
}
portal->status = PORTAL_ACTIVE;
/*
- * Set global portal context pointers.
+ * Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
- ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
- CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
- PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
- QueryContext = portal->queryContext;
+ PG_TRY();
+ {
+ ActivePortal = portal;
+ CurrentResourceOwner = portal->resowner;
+ PortalContext = PortalGetHeapMemory(portal);
+ QueryContext = portal->queryContext;
+
+ MemoryContextSwitchTo(PortalContext);
+
+ /*
+ * Rewind the executor: we need to store the entire result set in the
+ * tuplestore, so that subsequent backward FETCHs can be processed.
+ */
+ ExecutorRewind(queryDesc);
+
+ /* Change the destination to output to the tuplestore */
+ queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
+
+ /* Fetch the result set into the tuplestore */
+ ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+ (*queryDesc->dest->rDestroy) (queryDesc->dest);
+ queryDesc->dest = NULL;
+
+ /*
+ * Now shut down the inner executor.
+ */
+ portal->queryDesc = NULL; /* prevent double shutdown */
+ ExecutorEnd(queryDesc);
+
+ /*
+ * Reset the position in the result set: ideally, this could be
+ * implemented by just skipping straight to the tuple # that we need
+ * to be at, but the tuplestore API doesn't support that. So we start
+ * at the beginning of the tuplestore and iterate through it until we
+ * reach where we need to be. FIXME someday?
+ */
+ MemoryContextSwitchTo(portal->holdContext);
+
+ if (!portal->atEnd)
+ {
+ long store_pos;
- MemoryContextSwitchTo(PortalContext);
+ if (portal->posOverflow) /* oops, cannot trust portalPos */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not reposition held cursor")));
- /*
- * Rewind the executor: we need to store the entire result set in the
- * tuplestore, so that subsequent backward FETCHs can be processed.
- */
- ExecutorRewind(queryDesc);
+ tuplestore_rescan(portal->holdStore);
- /* Change the destination to output to the tuplestore */
- queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
+ for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
+ {
+ HeapTuple tup;
+ bool should_free;
- /* Fetch the result set into the tuplestore */
- ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+ tup = tuplestore_gettuple(portal->holdStore, true,
+ &should_free);
- (*queryDesc->dest->rDestroy) (queryDesc->dest);
- queryDesc->dest = NULL;
+ if (tup == NULL)
+ elog(ERROR, "unexpected end of tuple stream");
- /*
- * Now shut down the inner executor.
- */
- portal->queryDesc = NULL; /* prevent double shutdown */
- ExecutorEnd(queryDesc);
-
- /*
- * Reset the position in the result set: ideally, this could be
- * implemented by just skipping straight to the tuple # that we need
- * to be at, but the tuplestore API doesn't support that. So we start
- * at the beginning of the tuplestore and iterate through it until we
- * reach where we need to be. FIXME someday?
- */
- MemoryContextSwitchTo(portal->holdContext);
-
- if (!portal->atEnd)
+ if (should_free)
+ pfree(tup);
+ }
+ }
+ }
+ PG_CATCH();
{
- long store_pos;
-
- if (portal->posOverflow) /* oops, cannot trust portalPos */
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("could not reposition held cursor")));
-
- tuplestore_rescan(portal->holdStore);
-
- for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
- {
- HeapTuple tup;
- bool should_free;
-
- tup = tuplestore_gettuple(portal->holdStore, true,
- &should_free);
+ /* Uncaught error while executing portal: mark it dead */
+ portal->status = PORTAL_FAILED;
- if (tup == NULL)
- elog(ERROR, "unexpected end of tuple stream");
+ /* Restore global vars and propagate error */
+ ActivePortal = saveActivePortal;
+ CurrentResourceOwner = saveResourceOwner;
+ PortalContext = savePortalContext;
+ QueryContext = saveQueryContext;
- if (should_free)
- pfree(tup);
- }
+ PG_RE_THROW();
}
+ PG_END_TRY();
MemoryContextSwitchTo(oldcxt);
- /*
- * We can now release any subsidiary memory of the portal's heap
- * context; we'll never use it again. The executor already dropped
- * its context, but this will clean up anything that glommed onto the
- * portal's heap via PortalContext.
- */
- MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
-
/* Mark portal not active */
portal->status = PORTAL_READY;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
+
+ /*
+ * We can now release any subsidiary memory of the portal's heap
+ * context; we'll never use it again. The executor already dropped
+ * its context, but this will clean up anything that glommed onto the
+ * portal's heap via PortalContext.
+ */
+ MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.284 2004/07/21 22:31:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.285 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
vacuum(VacuumStmt *vacstmt)
{
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
- MemoryContext anl_context = NULL;
TransactionId initialOldestXmin = InvalidTransactionId;
TransactionId initialFreezeLimit = InvalidTransactionId;
- bool all_rels,
+ volatile MemoryContext anl_context = NULL;
+ volatile bool all_rels,
in_outer_xact,
use_own_xacts;
List *relations;
- ListCell *cur;
if (vacstmt->verbose)
elevel = INFO;
in_outer_xact = IsInTransactionChain((void *) vacstmt);
}
- /* Turn vacuum cost accounting on or off */
- VacuumCostActive = (VacuumCostNaptime > 0);
- VacuumCostBalance = 0;
-
/*
* Send info about dead objects to the statistics collector
*/
CommitTransactionCommand();
}
- /*
- * Loop to process each selected relation.
- */
- foreach(cur, relations)
+ /* Turn vacuum cost accounting on or off */
+ PG_TRY();
{
- Oid relid = lfirst_oid(cur);
+ ListCell *cur;
- if (vacstmt->vacuum)
- {
- if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))
- all_rels = false; /* forget about updating dbstats */
- }
- if (vacstmt->analyze)
+ VacuumCostActive = (VacuumCostNaptime > 0);
+ VacuumCostBalance = 0;
+
+ /*
+ * Loop to process each selected relation.
+ */
+ foreach(cur, relations)
{
- MemoryContext old_context = NULL;
+ Oid relid = lfirst_oid(cur);
- /*
- * If using separate xacts, start one for analyze. Otherwise,
- * we can use the outer transaction, but we still need to call
- * analyze_rel in a memory context that will be cleaned up on
- * return (else we leak memory while processing multiple
- * tables).
- */
- if (use_own_xacts)
+ if (vacstmt->vacuum)
{
- StartTransactionCommand();
- SetQuerySnapshot(); /* might be needed for functions
- * in indexes */
+ if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))
+ all_rels = false; /* forget about updating dbstats */
}
- else
- old_context = MemoryContextSwitchTo(anl_context);
+ if (vacstmt->analyze)
+ {
+ MemoryContext old_context = NULL;
- /*
- * Tell the buffer replacement strategy that vacuum is
- * causing the IO
- */
- StrategyHintVacuum(true);
+ /*
+ * If using separate xacts, start one for analyze. Otherwise,
+ * we can use the outer transaction, but we still need to call
+ * analyze_rel in a memory context that will be cleaned up on
+ * return (else we leak memory while processing multiple
+ * tables).
+ */
+ if (use_own_xacts)
+ {
+ StartTransactionCommand();
+ SetQuerySnapshot(); /* might be needed for functions
+ * in indexes */
+ }
+ else
+ old_context = MemoryContextSwitchTo(anl_context);
+
+ /*
+ * Tell the buffer replacement strategy that vacuum is
+ * causing the IO
+ */
+ StrategyHintVacuum(true);
- analyze_rel(relid, vacstmt);
+ analyze_rel(relid, vacstmt);
- StrategyHintVacuum(false);
+ StrategyHintVacuum(false);
- if (use_own_xacts)
- CommitTransactionCommand();
- else
- {
- MemoryContextSwitchTo(old_context);
- MemoryContextResetAndDeleteChildren(anl_context);
+ if (use_own_xacts)
+ CommitTransactionCommand();
+ else
+ {
+ MemoryContextSwitchTo(old_context);
+ MemoryContextResetAndDeleteChildren(anl_context);
+ }
}
}
}
+ PG_CATCH();
+ {
+ /* Make sure cost accounting is turned off after error */
+ VacuumCostActive = false;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* Turn off vacuum cost accounting */
+ VacuumCostActive = false;
/*
* Finish up processing.
if (anl_context)
MemoryContextDelete(anl_context);
-
- /* Turn off vacuum cost accounting */
- VacuumCostActive = false;
}
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.3 2004/06/03 02:08:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.4 2004/07/31 00:45:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
+#include "utils/memutils.h"
/*----------
void
BackgroundWriterMain(void)
{
+ sigjmp_buf local_sigjmp_buf;
+
Assert(BgWriterShmem != NULL);
BgWriterShmem->bgwriter_pid = MyProcPid;
am_bg_writer = true;
/*
* If an exception is encountered, processing resumes here.
+ *
+ * See notes in postgres.c about the design of this coding.
*/
- if (sigsetjmp(Warn_restart, 1) != 0)
+ if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
- /*
- * Make sure we're not interrupted while cleaning up. Also forget
- * any pending QueryCancel request, since we're aborting anyway.
- * Force InterruptHoldoffCount to a known state in case we
- * ereport'd from inside a holdoff section.
- */
- ImmediateInterruptOK = false;
- QueryCancelPending = false;
- InterruptHoldoffCount = 1;
- CritSectionCount = 0; /* should be unnecessary, but... */
+ /* Since not using PG_TRY, must reset error stack by hand */
+ error_context_stack = NULL;
+
+ /* Prevent interrupts while cleaning up */
+ HOLD_INTERRUPTS();
+
+ /* Report the error to the server log */
+ EmitErrorReport();
/*
* These operations are really just a minimal subset of
AbortBufferIO();
UnlockBuffers();
- /*
- * Clear flag to indicate that we got out of error recovery mode
- * successfully. (Flag was set in elog.c before longjmp().)
- */
- InError = false;
-
/* Warn any waiting backends that the checkpoint failed. */
if (ckpt_active)
{
}
/*
- * Exit interrupt holdoff section we implicitly established above.
+ * Now return to normal top-level context and clear ErrorContext
+ * for next time.
*/
+ MemoryContextSwitchTo(TopMemoryContext);
+ FlushErrorState();
+
+ /* Now we can allow interrupts again */
RESUME_INTERRUPTS();
/*
pg_usleep(1000000L);
}
- Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
+ /* We can now handle ereport(ERROR) */
+ PG_exception_stack = &local_sigjmp_buf;
/*
* Unblock signals (they were blocked when the postmaster forked us)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.416 2004/07/27 01:46:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.417 2004/07/31 00:45:33 tgl Exp $
*
* NOTES
*
#include <ctype.h>
#include <sys/stat.h>
#include <sys/socket.h>
-#include <errno.h>
#include <fcntl.h>
#include <sys/param.h>
#include <netinet/in.h>
/* Lose the postmaster's on-exit routines and port connections */
on_exit_reset();
+ /* Release postmaster's working memory context */
+ MemoryContextSwitchTo(TopMemoryContext);
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
+
BootstrapMain(ac, av);
ExitPostmaster(0);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.103 2004/07/28 14:23:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.104 2004/07/31 00:45:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Arrange for the top xact to own these relation references */
currentOwner = CurrentResourceOwner;
- CurrentResourceOwner = TopTransactionResourceOwner;
-
- /* Use RowExclusiveLock since we might either read or write */
- if (lo_heap_r == NULL)
- lo_heap_r = heap_openr(LargeObjectRelationName, RowExclusiveLock);
- if (lo_index_r == NULL)
- lo_index_r = index_openr(LargeObjectLOidPNIndex);
+ PG_TRY();
+ {
+ CurrentResourceOwner = TopTransactionResourceOwner;
+ /* Use RowExclusiveLock since we might either read or write */
+ if (lo_heap_r == NULL)
+ lo_heap_r = heap_openr(LargeObjectRelationName, RowExclusiveLock);
+ if (lo_index_r == NULL)
+ lo_index_r = index_openr(LargeObjectLOidPNIndex);
+ }
+ PG_CATCH();
+ {
+ /* Ensure CurrentResourceOwner is restored on error */
+ CurrentResourceOwner = currentOwner;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
CurrentResourceOwner = currentOwner;
}
ResourceOwner currentOwner;
currentOwner = CurrentResourceOwner;
- CurrentResourceOwner = TopTransactionResourceOwner;
-
- if (lo_index_r)
- index_close(lo_index_r);
- if (lo_heap_r)
- heap_close(lo_heap_r, NoLock);
+ PG_TRY();
+ {
+ CurrentResourceOwner = TopTransactionResourceOwner;
+ if (lo_index_r)
+ index_close(lo_index_r);
+ if (lo_heap_r)
+ heap_close(lo_heap_r, NoLock);
+ }
+ PG_CATCH();
+ {
+ /* Ensure CurrentResourceOwner is restored on error */
+ CurrentResourceOwner = currentOwner;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
CurrentResourceOwner = currentOwner;
}
lo_heap_r = NULL;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.426 2004/07/28 22:05:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.427 2004/07/31 00:45:36 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
-#include <errno.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
CommandDest whereToSendOutput = Debug;
-/* note: these declarations had better match tcopprot.h */
-sigjmp_buf Warn_restart;
-
-bool Warn_restart_ready = false;
-bool InError = false;
-
/* flag for logging end of session */
bool Log_disconnections = false;
/*
* Ideally this should be ereport(FATAL), but then we'd not get
- * control back (perhaps could fix by doing local sigsetjmp?)
+ * control back...
*/
ereport(WARNING,
(errcode(ERRCODE_CRASH_SHUTDOWN),
int save_errno = errno;
/*
- * Don't joggle the elbow of proc_exit, nor an already-in-progress
- * abort
+ * Don't joggle the elbow of proc_exit
*/
- if (!proc_exit_inprogress && !InError)
+ if (!proc_exit_inprogress)
{
InterruptPending = true;
QueryCancelPending = true;
}
-
/* ----------------------------------------------------------------
* PostgresMain
* postgres main loop -- all backends, interactive or otherwise start here
int firstchar;
char stack_base;
StringInfoData input_message;
+ sigjmp_buf local_sigjmp_buf;
volatile bool send_rfq = true;
/*
*
* If an exception is encountered, processing resumes here so we abort
* the current transaction and start a new one.
+ *
+ * You might wonder why this isn't coded as an infinite loop around
+ * a PG_TRY construct. The reason is that this is the bottom of the
+ * exception stack, and so with PG_TRY there would be no exception
+ * handler in force at all during the CATCH part. By leaving the
+ * outermost setjmp always active, we have at least some chance of
+ * recovering from an error during error recovery. (If we get into
+ * an infinite loop thereby, it will soon be stopped by overflow of
+ * elog.c's internal state stack.)
*/
- if (sigsetjmp(Warn_restart, 1) != 0)
+ if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/*
* NOTE: if you are tempted to add more code in this if-block,
- * consider the probability that it should be in
- * AbortTransaction() instead.
- *
- * Make sure we're not interrupted while cleaning up. Also forget
- * any pending QueryCancel request, since we're aborting anyway.
- * Force InterruptHoldoffCount to a known state in case we
- * ereport'd from inside a holdoff section.
+ * consider the high probability that it should be in
+ * AbortTransaction() instead. The only stuff done directly here
+ * should be stuff that is guaranteed to apply *only* for outer-level
+ * error recovery, such as adjusting the FE/BE protocol status.
+ */
+
+ /* Since not using PG_TRY, must reset error stack by hand */
+ error_context_stack = NULL;
+
+ /* Prevent interrupts while cleaning up */
+ HOLD_INTERRUPTS();
+
+ /*
+ * Forget any pending QueryCancel request, since we're returning
+ * to the idle loop anyway, and cancel the statement timer if running.
*/
- ImmediateInterruptOK = false;
QueryCancelPending = false;
- InterruptHoldoffCount = 1;
- CritSectionCount = 0; /* should be unnecessary, but... */
disable_sig_alarm(true);
QueryCancelPending = false; /* again in case timeout occurred */
+
+ /*
+ * Turn off these interrupts too. This is only needed here and not
+ * in other exception-catching places since these interrupts are
+ * only enabled while we wait for client input.
+ */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
- debug_query_string = NULL;
+
+ /* Report the error to the client and/or server log */
+ EmitErrorReport();
/*
- * If there's an active portal, mark it as failed
+ * Make sure debug_query_string gets reset before we possibly clobber
+ * the storage it points at.
*/
- if (ActivePortal)
- ActivePortal->status = PORTAL_FAILED;
+ debug_query_string = NULL;
/*
- * Make sure we are in a valid memory context during recovery.
- *
- * We use ErrorContext in hopes that it will have some free space
- * even if we're otherwise up against it...
+ * Abort the current transaction in order to recover.
*/
- MemoryContextSwitchTo(ErrorContext);
-
- /* Make sure we are using a sane ResourceOwner, too */
- CurrentResourceOwner = CurTransactionResourceOwner;
-
- /* Do the recovery */
- ereport(DEBUG2,
- (errmsg_internal("AbortCurrentTransaction")));
AbortCurrentTransaction();
/*
* for next time.
*/
MemoryContextSwitchTo(TopMemoryContext);
- MemoryContextResetAndDeleteChildren(ErrorContext);
- ActivePortal = NULL;
- PortalContext = NULL;
+ FlushErrorState();
QueryContext = NULL;
- /*
- * Clear flag to indicate that we got out of error recovery mode
- * successfully. (Flag was set in elog.c before longjmp().)
- */
- InError = false;
- xact_started = false;
-
- /*
- * Clear flag that causes accounting for cost based vacuum.
- */
- VacuumCostActive = false;
-
/*
* If we were handling an extended-query-protocol message,
* initiate skip till next Sync. This also causes us not to issue
if (doing_extended_query_message)
ignore_till_sync = true;
- /*
- * Exit interrupt holdoff section we implicitly established above.
- */
+ /* We don't have a transaction command open anymore */
+ xact_started = false;
+
+ /* Now we can allow interrupts again */
RESUME_INTERRUPTS();
}
- Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
+ /* We can now handle ereport(ERROR) */
+ PG_exception_stack = &local_sigjmp_buf;
PG_SETMASK(&UnBlockSig);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.81 2004/07/17 03:29:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.82 2004/07/31 00:45:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AssertState(portal->status == PORTAL_NEW); /* else extra PortalStart */
/*
- * Set global portal context pointers. (Should we set QueryContext?)
+ * Set up global portal context pointers. (Should we set QueryContext?)
*/
saveActivePortal = ActivePortal;
- ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
- CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
- PortalContext = PortalGetHeapMemory(portal);
+ PG_TRY();
+ {
+ ActivePortal = portal;
+ CurrentResourceOwner = portal->resowner;
+ PortalContext = PortalGetHeapMemory(portal);
- oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+ oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
- /* Must remember portal param list, if any */
- portal->portalParams = params;
+ /* Must remember portal param list, if any */
+ portal->portalParams = params;
- /*
- * Determine the portal execution strategy
- */
- portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+ /*
+ * Determine the portal execution strategy
+ */
+ portal->strategy = ChoosePortalStrategy(portal->parseTrees);
- /*
- * Fire her up according to the strategy
- */
- switch (portal->strategy)
- {
- case PORTAL_ONE_SELECT:
+ /*
+ * Fire her up according to the strategy
+ */
+ switch (portal->strategy)
+ {
+ case PORTAL_ONE_SELECT:
- /*
- * Must set query snapshot before starting executor.
- */
- SetQuerySnapshot();
+ /*
+ * Must set query snapshot before starting executor.
+ */
+ SetQuerySnapshot();
- /*
- * Create QueryDesc in portal's context; for the moment, set
- * the destination to None.
- */
- queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
- (Plan *) linitial(portal->planTrees),
- None_Receiver,
- params,
- false);
+ /*
+ * Create QueryDesc in portal's context; for the moment, set
+ * the destination to None.
+ */
+ queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
+ (Plan *) linitial(portal->planTrees),
+ None_Receiver,
+ params,
+ false);
- /*
- * Call ExecStart to prepare the plan for execution
- */
- ExecutorStart(queryDesc, false, false);
+ /*
+ * Call ExecStart to prepare the plan for execution
+ */
+ ExecutorStart(queryDesc, false, false);
- /*
- * This tells PortalCleanup to shut down the executor
- */
- portal->queryDesc = queryDesc;
+ /*
+ * This tells PortalCleanup to shut down the executor
+ */
+ portal->queryDesc = queryDesc;
- /*
- * Remember tuple descriptor (computed by ExecutorStart)
- */
- portal->tupDesc = queryDesc->tupDesc;
+ /*
+ * Remember tuple descriptor (computed by ExecutorStart)
+ */
+ portal->tupDesc = queryDesc->tupDesc;
- /*
- * Reset cursor position data to "start of query"
- */
- portal->atStart = true;
- portal->atEnd = false; /* allow fetches */
- portal->portalPos = 0;
- portal->posOverflow = false;
- break;
+ /*
+ * Reset cursor position data to "start of query"
+ */
+ portal->atStart = true;
+ portal->atEnd = false; /* allow fetches */
+ portal->portalPos = 0;
+ portal->posOverflow = false;
+ break;
- case PORTAL_UTIL_SELECT:
+ case PORTAL_UTIL_SELECT:
- /*
- * We don't set query snapshot here, because PortalRunUtility
- * will take care of it.
- */
- portal->tupDesc =
- UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+ /*
+ * We don't set query snapshot here, because PortalRunUtility
+ * will take care of it.
+ */
+ portal->tupDesc =
+ UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
- /*
- * Reset cursor position data to "start of query"
- */
- portal->atStart = true;
- portal->atEnd = false; /* allow fetches */
- portal->portalPos = 0;
- portal->posOverflow = false;
- break;
+ /*
+ * Reset cursor position data to "start of query"
+ */
+ portal->atStart = true;
+ portal->atEnd = false; /* allow fetches */
+ portal->portalPos = 0;
+ portal->posOverflow = false;
+ break;
- case PORTAL_MULTI_QUERY:
- /* Need do nothing now */
- portal->tupDesc = NULL;
- break;
+ case PORTAL_MULTI_QUERY:
+ /* Need do nothing now */
+ portal->tupDesc = NULL;
+ break;
+ }
}
+ PG_CATCH();
+ {
+ /* Uncaught error while executing portal: mark it dead */
+ portal->status = PORTAL_FAILED;
+
+ /* Restore global vars and propagate error */
+ ActivePortal = saveActivePortal;
+ CurrentResourceOwner = saveResourceOwner;
+ PortalContext = savePortalContext;
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
MemoryContextSwitchTo(oldContext);
portal->status = PORTAL_ACTIVE;
/*
- * Set global portal context pointers.
+ * Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
- ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
- CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
- PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
- QueryContext = portal->queryContext;
+ PG_TRY();
+ {
+ ActivePortal = portal;
+ CurrentResourceOwner = portal->resowner;
+ PortalContext = PortalGetHeapMemory(portal);
+ QueryContext = portal->queryContext;
- oldContext = MemoryContextSwitchTo(PortalContext);
+ oldContext = MemoryContextSwitchTo(PortalContext);
- switch (portal->strategy)
- {
- case PORTAL_ONE_SELECT:
- (void) PortalRunSelect(portal, true, count, dest);
- /* we know the query is supposed to set the tag */
- if (completionTag && portal->commandTag)
- strcpy(completionTag, portal->commandTag);
+ switch (portal->strategy)
+ {
+ case PORTAL_ONE_SELECT:
+ (void) PortalRunSelect(portal, true, count, dest);
+ /* we know the query is supposed to set the tag */
+ if (completionTag && portal->commandTag)
+ strcpy(completionTag, portal->commandTag);
- /* Mark portal not active */
- portal->status = PORTAL_READY;
+ /* Mark portal not active */
+ portal->status = PORTAL_READY;
- /*
- * Since it's a forward fetch, say DONE iff atEnd is now true.
- */
- result = portal->atEnd;
- break;
+ /*
+ * Since it's a forward fetch, say DONE iff atEnd is now true.
+ */
+ result = portal->atEnd;
+ break;
- case PORTAL_UTIL_SELECT:
+ case PORTAL_UTIL_SELECT:
- /*
- * If we have not yet run the utility statement, do so,
- * storing its results in the portal's tuplestore.
- */
- if (!portal->portalUtilReady)
- {
- DestReceiver *treceiver;
-
- PortalCreateHoldStore(portal);
- treceiver = CreateDestReceiver(Tuplestore, portal);
- PortalRunUtility(portal, linitial(portal->parseTrees),
- treceiver, NULL);
- (*treceiver->rDestroy) (treceiver);
- portal->portalUtilReady = true;
- }
+ /*
+ * If we have not yet run the utility statement, do so,
+ * storing its results in the portal's tuplestore.
+ */
+ if (!portal->portalUtilReady)
+ {
+ DestReceiver *treceiver;
+
+ PortalCreateHoldStore(portal);
+ treceiver = CreateDestReceiver(Tuplestore, portal);
+ PortalRunUtility(portal, linitial(portal->parseTrees),
+ treceiver, NULL);
+ (*treceiver->rDestroy) (treceiver);
+ portal->portalUtilReady = true;
+ }
- /*
- * Now fetch desired portion of results.
- */
- (void) PortalRunSelect(portal, true, count, dest);
+ /*
+ * Now fetch desired portion of results.
+ */
+ (void) PortalRunSelect(portal, true, count, dest);
- /*
- * We know the query is supposed to set the tag; we assume
- * only the default tag is needed.
- */
- if (completionTag && portal->commandTag)
- strcpy(completionTag, portal->commandTag);
+ /*
+ * We know the query is supposed to set the tag; we assume
+ * only the default tag is needed.
+ */
+ if (completionTag && portal->commandTag)
+ strcpy(completionTag, portal->commandTag);
- /* Mark portal not active */
- portal->status = PORTAL_READY;
+ /* Mark portal not active */
+ portal->status = PORTAL_READY;
- /*
- * Since it's a forward fetch, say DONE iff atEnd is now true.
- */
- result = portal->atEnd;
- break;
+ /*
+ * Since it's a forward fetch, say DONE iff atEnd is now true.
+ */
+ result = portal->atEnd;
+ break;
- case PORTAL_MULTI_QUERY:
- PortalRunMulti(portal, dest, altdest, completionTag);
+ case PORTAL_MULTI_QUERY:
+ PortalRunMulti(portal, dest, altdest, completionTag);
- /* Prevent portal's commands from being re-executed */
- portal->status = PORTAL_DONE;
+ /* Prevent portal's commands from being re-executed */
+ portal->status = PORTAL_DONE;
- /* Always complete at end of RunMulti */
- result = true;
- break;
+ /* Always complete at end of RunMulti */
+ result = true;
+ break;
- default:
- elog(ERROR, "unrecognized portal strategy: %d",
- (int) portal->strategy);
- result = false; /* keep compiler quiet */
- break;
+ default:
+ elog(ERROR, "unrecognized portal strategy: %d",
+ (int) portal->strategy);
+ result = false; /* keep compiler quiet */
+ break;
+ }
+ }
+ PG_CATCH();
+ {
+ /* Uncaught error while executing portal: mark it dead */
+ portal->status = PORTAL_FAILED;
+
+ /* Restore global vars and propagate error */
+ ActivePortal = saveActivePortal;
+ CurrentResourceOwner = saveResourceOwner;
+ PortalContext = savePortalContext;
+ QueryContext = saveQueryContext;
+
+ PG_RE_THROW();
}
+ PG_END_TRY();
MemoryContextSwitchTo(oldContext);
portal->status = PORTAL_ACTIVE;
/*
- * Set global portal context pointers.
+ * Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
- ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
- CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
- PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
- QueryContext = portal->queryContext;
+ PG_TRY();
+ {
+ ActivePortal = portal;
+ CurrentResourceOwner = portal->resowner;
+ PortalContext = PortalGetHeapMemory(portal);
+ QueryContext = portal->queryContext;
- oldContext = MemoryContextSwitchTo(PortalContext);
+ oldContext = MemoryContextSwitchTo(PortalContext);
- switch (portal->strategy)
+ switch (portal->strategy)
+ {
+ case PORTAL_ONE_SELECT:
+ result = DoPortalRunFetch(portal, fdirection, count, dest);
+ break;
+
+ default:
+ elog(ERROR, "unsupported portal strategy");
+ result = 0; /* keep compiler quiet */
+ break;
+ }
+ }
+ PG_CATCH();
{
- case PORTAL_ONE_SELECT:
- result = DoPortalRunFetch(portal, fdirection, count, dest);
- break;
+ /* Uncaught error while executing portal: mark it dead */
+ portal->status = PORTAL_FAILED;
- default:
- elog(ERROR, "unsupported portal strategy");
- result = 0; /* keep compiler quiet */
- break;
+ /* Restore global vars and propagate error */
+ ActivePortal = saveActivePortal;
+ CurrentResourceOwner = saveResourceOwner;
+ PortalContext = savePortalContext;
+ QueryContext = saveQueryContext;
+
+ PG_RE_THROW();
}
+ PG_END_TRY();
MemoryContextSwitchTo(oldContext);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.143 2004/07/28 22:05:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.144 2004/07/31 00:45:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <fcntl.h>
-#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
/* Global variables */
ErrorContextCallback *error_context_stack = NULL;
+sigjmp_buf *PG_exception_stack = NULL;
+
/* GUC parameters */
PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
char *Log_line_prefix = NULL; /* format for extra log line info */
static void write_eventlog(int level, const char *line);
#endif
-/*
- * ErrorData holds the data accumulated during any one ereport() cycle.
- * Any non-NULL pointers must point to palloc'd data in ErrorContext.
- * (The const pointers are an exception; we assume they point at non-freeable
- * constant strings.)
- */
-
-typedef struct ErrorData
-{
- int elevel; /* error level */
- bool output_to_server; /* will report to server log? */
- bool output_to_client; /* will report to client? */
- bool show_funcname; /* true to force funcname inclusion */
- const char *filename; /* __FILE__ of ereport() call */
- int lineno; /* __LINE__ of ereport() call */
- const char *funcname; /* __func__ of ereport() call */
- int sqlerrcode; /* encoded ERRSTATE */
- char *message; /* primary error message */
- char *detail; /* detail error message */
- char *hint; /* hint message */
- char *context; /* context message */
- int cursorpos; /* cursor index into query string */
- int internalpos; /* cursor index into internalquery */
- char *internalquery; /* text of internally-generated query */
- int saved_errno; /* errno at entry */
-} ErrorData;
-
/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE 5
/*
* Convert initialization errors into fatal errors. This is probably
- * redundant, because Warn_restart_ready won't be set anyway.
+ * redundant, because PG_exception_stack will still be null anyway.
*/
if (elevel == ERROR && IsInitProcessingMode())
elevel = FATAL;
}
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
{
- /* Wups, stack not big enough */
- int i;
-
- elevel = Max(elevel, ERROR);
-
/*
- * Don't forget any FATAL/PANIC status on the stack (see comments
- * in errfinish)
+ * Wups, stack not big enough. We treat this as a PANIC condition
+ * because it suggests an infinite loop of errors during error
+ * recovery.
*/
- for (i = 0; i < errordata_stack_depth; i++)
- elevel = Max(elevel, errordata[i].elevel);
- /* Clear the stack and try again */
- errordata_stack_depth = -1;
- ereport(elevel, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
+ errordata_stack_depth = -1; /* make room on stack */
+ ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
/* Initialize data for this error frame */
econtext = econtext->previous)
(*econtext->callback) (econtext->arg);
- /* Send to server log, if enabled */
- if (edata->output_to_server)
- send_message_to_server_log(edata);
+ /*
+ * If the error level is ERROR or more, we are not going to return to
+ * caller; therefore, if there is any stacked error already in
+ * progress it will be lost. This is more or less okay, except we do
+ * not want to have a FATAL or PANIC error downgraded because the
+ * reporting process was interrupted by a lower-grade error. So check
+ * the stack and make sure we panic if panic is warranted.
+ */
+ if (elevel >= ERROR)
+ {
+ int i;
+
+ for (i = 0; i <= errordata_stack_depth; i++)
+ elevel = Max(elevel, errordata[i].elevel);
+ }
+
+ /*
+ * Check some other reasons for treating ERROR as FATAL:
+ *
+ * 1. we have no handler to pass the error to (implies we are in
+ * the postmaster or in backend startup).
+ *
+ * 2. ExitOnAnyError mode switch is set (initdb uses this).
+ *
+ * 3. the error occurred after proc_exit has begun to run. (It's
+ * proc_exit's responsibility to see that this doesn't turn into
+ * infinite recursion!)
+ */
+ if (elevel == ERROR)
+ {
+ if (PG_exception_stack == NULL ||
+ ExitOnAnyError ||
+ proc_exit_inprogress)
+ elevel = FATAL;
+ else
+ {
+ /*
+ * Otherwise we can pass the error off to the current handler.
+ * Printing it and popping the stack is the responsibility of
+ * the handler.
+ *
+ * We do some minimal cleanup before longjmp'ing so that handlers
+ * can execute in a reasonably sane state.
+ */
+
+ /* This is just in case the error came while waiting for input */
+ ImmediateInterruptOK = false;
+
+ /*
+ * Reset InterruptHoldoffCount in case we ereport'd from inside an
+ * interrupt holdoff section. (We assume here that no handler
+ * will itself be inside a holdoff section. If necessary, such
+ * a handler could save and restore InterruptHoldoffCount for
+ * itself, but this should make life easier for most.)
+ */
+ InterruptHoldoffCount = 0;
+
+ CritSectionCount = 0; /* should be unnecessary, but... */
+
+ /*
+ * Note that we leave CurrentMemoryContext set to ErrorContext.
+ * The handler should reset it to something else soon.
+ */
+
+ recursion_depth--;
+ PG_RE_THROW();
+ }
+ }
/*
- * Abort any old-style COPY OUT in progress when an error is detected.
+ * If we are doing FATAL or PANIC, abort any old-style COPY OUT in
+ * progress, so that we can report the message before dying. (Without
+ * this, pq_putmessage will refuse to send the message at all, which
+ * is what we want for NOTICE messages, but not for fatal exits.)
* This hack is necessary because of poor design of old-style copy
* protocol. Note we must do this even if client is fool enough to
- * have set client_min_messages above ERROR, so don't look at
+ * have set client_min_messages above FATAL, so don't look at
* output_to_client.
*/
- if (elevel >= ERROR && whereToSendOutput == Remote)
+ if (elevel >= FATAL && whereToSendOutput == Remote)
pq_endcopyout(true);
- /* Send to client, if enabled */
- if (edata->output_to_client)
- send_message_to_frontend(edata);
+ /* Emit the message to the right places */
+ EmitErrorReport();
/* Now free up subsidiary data attached to stack entry, and release it */
if (edata->message)
if (edata->internalquery)
pfree(edata->internalquery);
- MemoryContextSwitchTo(oldcontext);
-
errordata_stack_depth--;
+
+ /* Exit error-handling context */
+ MemoryContextSwitchTo(oldcontext);
recursion_depth--;
/*
- * If the error level is ERROR or more, we are not going to return to
- * caller; therefore, if there is any stacked error already in
- * progress it will be lost. This is more or less okay, except we do
- * not want to have a FATAL or PANIC error downgraded because the
- * reporting process was interrupted by a lower-grade error. So check
- * the stack and make sure we panic if panic is warranted.
+ * Perform error recovery action as specified by elevel.
*/
- if (elevel >= ERROR)
+ if (elevel == FATAL)
{
- int i;
-
- for (i = 0; i <= errordata_stack_depth; i++)
- elevel = Max(elevel, errordata[i].elevel);
-
/*
- * Also, be sure to reset the stack to empty. We do not clear
- * ErrorContext here, though; PostgresMain does that later on.
+ * For a FATAL error, we let proc_exit clean up and exit.
*/
- errordata_stack_depth = -1;
- recursion_depth = 0;
- error_context_stack = NULL;
- }
-
- /*
- * Perform error recovery action as specified by elevel.
- */
- if (elevel == ERROR || elevel == FATAL)
- {
- /* Prevent immediate interrupt while entering error recovery */
ImmediateInterruptOK = false;
/*
* disconnect on receiving it, so don't send any more to the
* client.
*/
- if (!Warn_restart_ready && whereToSendOutput == Remote)
+ if (PG_exception_stack == NULL && whereToSendOutput == Remote)
whereToSendOutput = None;
/*
- * For a FATAL error, we let proc_exit clean up and exit.
- *
- * There are several other cases in which we treat ERROR as FATAL and
- * go directly to proc_exit:
- *
- * 1. ExitOnAnyError mode switch is set (initdb uses this).
- *
- * 2. we have not yet entered the main backend loop (ie, we are in
- * the postmaster or in backend startup); we have noplace to
- * recover.
- *
- * 3. the error occurred after proc_exit has begun to run. (It's
- * proc_exit's responsibility to see that this doesn't turn into
- * infinite recursion!)
- *
- * In the last case, we exit with nonzero exit code to indicate that
- * something's pretty wrong. We also want to exit with nonzero
- * exit code if not running under the postmaster (for example, if
- * we are being run from the initdb script, we'd better return an
- * error status).
- */
- if (elevel == FATAL ||
- ExitOnAnyError ||
- !Warn_restart_ready ||
- proc_exit_inprogress)
- {
- /*
- * fflush here is just to improve the odds that we get to see
- * the error message, in case things are so hosed that
- * proc_exit crashes. Any other code you might be tempted to
- * add here should probably be in an on_proc_exit callback
- * instead.
- */
- fflush(stdout);
- fflush(stderr);
- proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
- }
-
- /*
- * Guard against infinite loop from errors during error recovery.
+ * fflush here is just to improve the odds that we get to see
+ * the error message, in case things are so hosed that
+ * proc_exit crashes. Any other code you might be tempted to
+ * add here should probably be in an on_proc_exit callback
+ * instead.
*/
- if (InError)
- ereport(PANIC, (errmsg("error during error recovery, giving up")));
- InError = true;
+ fflush(stdout);
+ fflush(stderr);
/*
- * Otherwise we can return to the main loop in postgres.c.
+ * If proc_exit is already running, we exit with nonzero exit code to
+ * indicate that something's pretty wrong. We also want to exit with
+ * nonzero exit code if not running under the postmaster (for example,
+ * if we are being run from the initdb script, we'd better return an
+ * error status).
*/
- siglongjmp(Warn_restart, 1);
+ proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
}
if (elevel >= PANIC)
errfinish(0);
}
+/*
+ * Actual output of the top-of-stack error message
+ *
+ * In the ereport(ERROR) case this is called from PostgresMain (or not at all,
+ * if the error is caught by somebody). For all other severity levels this
+ * is called by errfinish.
+ */
+void
+EmitErrorReport(void)
+{
+ ErrorData *edata = &errordata[errordata_stack_depth];
+ MemoryContext oldcontext;
+
+ recursion_depth++;
+ CHECK_STACK_DEPTH();
+ oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+ /* Send to server log, if enabled */
+ if (edata->output_to_server)
+ send_message_to_server_log(edata);
+
+ /* Send to client, if enabled */
+ if (edata->output_to_client)
+ send_message_to_frontend(edata);
+
+ MemoryContextSwitchTo(oldcontext);
+ recursion_depth--;
+}
+
+/*
+ * CopyErrorData --- obtain a copy of the topmost error stack entry
+ *
+ * This is only for use in error handler code. The data is copied into the
+ * current memory context, so callers should always switch away from
+ * ErrorContext first; otherwise it will be lost when FlushErrorState is done.
+ */
+ErrorData *
+CopyErrorData(void)
+{
+ ErrorData *edata = &errordata[errordata_stack_depth];
+ ErrorData *newedata;
+
+ /*
+ * we don't increment recursion_depth because out-of-memory here does
+ * not indicate a problem within the error subsystem.
+ */
+ CHECK_STACK_DEPTH();
+
+ Assert(CurrentMemoryContext != ErrorContext);
+
+ /* Copy the struct itself */
+ newedata = (ErrorData *) palloc(sizeof(ErrorData));
+ memcpy(newedata, edata, sizeof(ErrorData));
+
+ /* Make copies of separately-allocated fields */
+ if (newedata->message)
+ newedata->message = pstrdup(newedata->message);
+ if (newedata->detail)
+ newedata->detail = pstrdup(newedata->detail);
+ if (newedata->hint)
+ newedata->hint = pstrdup(newedata->hint);
+ if (newedata->context)
+ newedata->context = pstrdup(newedata->context);
+ if (newedata->internalquery)
+ newedata->internalquery = pstrdup(newedata->internalquery);
+
+ return newedata;
+}
+
+/*
+ * FreeErrorData --- free the structure returned by CopyErrorData.
+ *
+ * Error handlers should use this in preference to assuming they know all
+ * the separately-allocated fields.
+ */
+void
+FreeErrorData(ErrorData *edata)
+{
+ if (edata->message)
+ pfree(edata->message);
+ if (edata->detail)
+ pfree(edata->detail);
+ if (edata->hint)
+ pfree(edata->hint);
+ if (edata->context)
+ pfree(edata->context);
+ if (edata->internalquery)
+ pfree(edata->internalquery);
+ pfree(edata);
+}
+
+/*
+ * FlushErrorState --- flush the error state after error recovery
+ *
+ * This should be called by an error handler after it's done processing
+ * the error; or as soon as it's done CopyErrorData, if it intends to
+ * do stuff that is likely to provoke another error. You are not "out" of
+ * the error subsystem until you have done this.
+ */
+void
+FlushErrorState(void)
+{
+ /*
+ * Reset stack to empty. The only case where it would be more than
+ * one deep is if we serviced an error that interrupted construction
+ * of another message. We assume control escaped out of that
+ * message construction and won't ever go back.
+ */
+ errordata_stack_depth = -1;
+ recursion_depth = 0;
+ /* Delete all data in ErrorContext */
+ MemoryContextResetAndDeleteChildren(ErrorContext);
+}
+
+/*
+ * ReThrowError --- re-throw a previously copied error
+ *
+ * A handler can do CopyErrorData/FlushErrorState to get out of the error
+ * subsystem, then do some processing, and finally ReThrowError to re-throw
+ * the original error. This is slower than just PG_RE_THROW() but should
+ * be used if the "some processing" is likely to incur another error.
+ */
+void
+ReThrowError(ErrorData *edata)
+{
+ ErrorData *newedata;
+
+ Assert(edata->elevel == ERROR);
+
+ /* Push the data back into the error context */
+ recursion_depth++;
+ MemoryContextSwitchTo(ErrorContext);
+
+ if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
+ {
+ /*
+ * Wups, stack not big enough. We treat this as a PANIC condition
+ * because it suggests an infinite loop of errors during error
+ * recovery.
+ */
+ errordata_stack_depth = -1; /* make room on stack */
+ ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
+ }
+
+ newedata = &errordata[errordata_stack_depth];
+ memcpy(newedata, edata, sizeof(ErrorData));
+
+ /* Make copies of separately-allocated fields */
+ if (newedata->message)
+ newedata->message = pstrdup(newedata->message);
+ if (newedata->detail)
+ newedata->detail = pstrdup(newedata->detail);
+ if (newedata->hint)
+ newedata->hint = pstrdup(newedata->hint);
+ if (newedata->context)
+ newedata->context = pstrdup(newedata->context);
+ if (newedata->internalquery)
+ newedata->internalquery = pstrdup(newedata->internalquery);
+
+ recursion_depth--;
+ PG_RE_THROW();
+}
/*
* Initialization of error output file
-$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.1 2004/07/17 03:30:10 tgl Exp $
+$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.2 2004/07/31 00:45:40 tgl Exp $
Notes about resource owners
---------------------------
when the buffer, lock, or cache reference was acquired. It would be possible
to relax this restriction given additional bookkeeping effort, but at present
there seems no need.
+
+Code that transiently changes CurrentResourceOwner should generally use a
+PG_TRY construct to ensure that the previous value of CurrentResourceOwner
+is restored if control is lost during an error exit.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.1 2004/07/17 03:30:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.2 2004/07/31 00:45:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
+/* Internal routines */
+static void ResourceOwnerReleaseInternal(ResourceOwner owner,
+ ResourceReleasePhase phase,
+ bool isCommit,
+ bool isTopLevel);
+
+
/*****************************************************************************
* EXPORTED ROUTINES *
*****************************************************************************/
ResourceReleasePhase phase,
bool isCommit,
bool isTopLevel)
+{
+ /* Rather than PG_TRY at every level of recursion, set it up once */
+ ResourceOwner save;
+
+ save = CurrentResourceOwner;
+ PG_TRY();
+ {
+ ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
+ }
+ PG_CATCH();
+ {
+ CurrentResourceOwner = save;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ CurrentResourceOwner = save;
+}
+
+static void
+ResourceOwnerReleaseInternal(ResourceOwner owner,
+ ResourceReleasePhase phase,
+ bool isCommit,
+ bool isTopLevel)
{
ResourceOwner child;
ResourceOwner save;
/* Recurse to handle descendants */
for (child = owner->firstchild; child != NULL; child = child->nextchild)
- ResourceOwnerRelease(child, phase, isCommit, isTopLevel);
+ ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
/*
* Make CurrentResourceOwner point to me, so that ReleaseBuffer etc
- * don't get confused.
+ * don't get confused. We needn't PG_TRY here because the outermost
+ * level will fix it on error abort.
*/
save = CurrentResourceOwner;
CurrentResourceOwner = owner;
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.68 2004/07/28 22:05:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.69 2004/07/31 00:45:41 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
#ifndef TCOPPROT_H
#define TCOPPROT_H
-#include <setjmp.h>
-
#include "executor/execdesc.h"
#include "nodes/params.h"
#include "tcop/dest.h"
#include "utils/guc.h"
-extern DLLIMPORT sigjmp_buf Warn_restart;
-extern bool Warn_restart_ready;
-extern bool InError;
extern CommandDest whereToSendOutput;
extern DLLIMPORT const char *debug_query_string;
extern int max_stack_depth;
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.70 2004/07/06 19:51:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.71 2004/07/31 00:45:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef ELOG_H
#define ELOG_H
+#include <setjmp.h>
+
/* Error level codes */
#define DEBUG5 10 /* Debugging messages, in categories of
* decreasing detail. */
extern DLLIMPORT ErrorContextCallback *error_context_stack;
+/*----------
+ * API for catching ereport(ERROR) exits. Use these macros like so:
+ *
+ * PG_TRY();
+ * {
+ * ... code that might throw ereport(ERROR) ...
+ * }
+ * PG_CATCH();
+ * {
+ * ... error recovery code ...
+ * }
+ * PG_END_TRY();
+ *
+ * (The braces are not actually necessary, but are recommended so that
+ * pg_indent will indent the construct nicely.) The error recovery code
+ * can optionally do PG_RE_THROW() to propagate the same error outwards.
+ *
+ * Note: while the system will correctly propagate any new ereport(ERROR)
+ * occurring in the recovery section, there is a small limit on the number
+ * of levels this will work for. It's best to keep the error recovery
+ * section simple enough that it can't generate any new errors, at least
+ * not before popping the error stack.
+ *----------
+ */
+#define PG_TRY() \
+ do { \
+ sigjmp_buf *save_exception_stack = PG_exception_stack; \
+ ErrorContextCallback *save_context_stack = error_context_stack; \
+ sigjmp_buf local_sigjmp_buf; \
+ if (sigsetjmp(local_sigjmp_buf, 1) == 0) \
+ { \
+ PG_exception_stack = &local_sigjmp_buf
+
+#define PG_CATCH() \
+ } \
+ else \
+ { \
+ PG_exception_stack = save_exception_stack; \
+ error_context_stack = save_context_stack
+
+#define PG_END_TRY() \
+ } \
+ PG_exception_stack = save_exception_stack; \
+ error_context_stack = save_context_stack; \
+ } while (0)
+
+#define PG_RE_THROW() \
+ siglongjmp(*PG_exception_stack, 1)
+
+extern DLLIMPORT sigjmp_buf *PG_exception_stack;
+
+
+/* Stuff that error handlers might want to use */
+
+/*
+ * ErrorData holds the data accumulated during any one ereport() cycle.
+ * Any non-NULL pointers must point to palloc'd data.
+ * (The const pointers are an exception; we assume they point at non-freeable
+ * constant strings.)
+ */
+typedef struct ErrorData
+{
+ int elevel; /* error level */
+ bool output_to_server; /* will report to server log? */
+ bool output_to_client; /* will report to client? */
+ bool show_funcname; /* true to force funcname inclusion */
+ const char *filename; /* __FILE__ of ereport() call */
+ int lineno; /* __LINE__ of ereport() call */
+ const char *funcname; /* __func__ of ereport() call */
+ int sqlerrcode; /* encoded ERRSTATE */
+ char *message; /* primary error message */
+ char *detail; /* detail error message */
+ char *hint; /* hint message */
+ char *context; /* context message */
+ int cursorpos; /* cursor index into query string */
+ int internalpos; /* cursor index into internalquery */
+ char *internalquery; /* text of internally-generated query */
+ int saved_errno; /* errno at entry */
+} ErrorData;
+
+extern void EmitErrorReport(void);
+extern ErrorData *CopyErrorData(void);
+extern void FreeErrorData(ErrorData *edata);
+extern void FlushErrorState(void);
+extern void ReThrowError(ErrorData *edata);
+
+
/* GUC-configurable parameters */
typedef enum
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.47 2004/07/21 20:45:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.48 2004/07/31 00:45:44 tgl Exp $
*
**********************************************************************/
#include "postgres.h"
/* system stuff */
-#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
+#include <unistd.h>
/* postgreSQL stuff */
#include "access/heapam.h"
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.77 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.78 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include "plpgsql.h"
#include <ctype.h>
-#include <setjmp.h>
#include "pl.tab.h"
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.107 2004/06/09 19:08:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.108 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include "pl.tab.h"
#include <ctype.h>
-#include <setjmp.h>
#include "access/heapam.h"
#include "catalog/pg_proc.h"
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.49 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.50 2004/07/31 00:45:52 tgl Exp $
*
*********************************************************************
*/
/* system stuff */
#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
/* postgreSQL stuff */
#include "access/heapam.h"
#include <Python.h>
#include <compile.h>
#include <eval.h>
-#include "plpython.h"
/* convert Postgresql Datum or tuple into a PyObject.
* input to Python. Tuples are converted to dictionary
static void PLy_init_interp(void);
static void PLy_init_plpy(void);
-/* error handler. collects the current Python exception, if any,
- * and appends it to the error and sends it to elog
- */
-static void PLy_elog(int, const char *,...);
-
/* call PyErr_SetString with a vprint interface
*/
static void
/* some utility functions
*/
+static void PLy_elog(int, const char *,...);
+static char *PLy_traceback(int *);
+static char *PLy_vprintf(const char *fmt, va_list ap);
+static char *PLy_printf(const char *fmt,...);
+
static void *PLy_malloc(size_t);
static void *PLy_realloc(void *, size_t);
static void PLy_free(void *);
/* global data
*/
static int PLy_first_call = 1;
-static volatile int PLy_call_level = 0;
/*
* Last function called by postgres backend
+ *
+ * XXX replace this with errcontext mechanism
*/
static PLyProcedure *PLy_last_procedure = NULL;
-/* this gets modified in plpython_call_handler and PLy_elog.
- * test it any old where, but do NOT modify it anywhere except
- * those two functions
+/*
+ * When a callback from Python into PG incurs an error, we temporarily store
+ * the error information here, and return NULL to the Python interpreter.
+ * Any further callback attempts immediately fail, and when the Python
+ * interpreter returns to the calling function, we re-throw the error (even if
+ * Python thinks it trapped the error and doesn't return NULL). Eventually
+ * this ought to be improved to let Python 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 volatile int PLy_restart_in_progress = 0;
+static ErrorData *PLy_error_in_progress = NULL;
static PyObject *PLy_interp_globals = NULL;
static PyObject *PLy_interp_safe_globals = NULL;
};
-#if DEBUG_EXC
-volatile int exc_save_calls = 0;
-volatile int exc_restore_calls = 0;
-volatile int func_enter_calls = 0;
-volatile int func_leave_calls = 0;
-#endif
-
/*
* the function definitions
*/
Datum
plpython_call_handler(PG_FUNCTION_ARGS)
{
- DECLARE_EXC();
Datum retval;
PLyProcedure *volatile proc = NULL;
- enter();
-
PLy_init_all();
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
- CALL_LEVEL_INC();
-
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
- CALL_LEVEL_DEC();
- if (PLy_call_level == 0)
+ if (CALLED_AS_TRIGGER(fcinfo))
{
- PLy_restart_in_progress = 0;
- PyErr_Clear();
+ TriggerData *tdata = (TriggerData *) fcinfo->context;
+ HeapTuple trv;
+
+ proc = PLy_procedure_get(fcinfo,
+ RelationGetRelid(tdata->tg_relation));
+ trv = PLy_trigger_handler(fcinfo, proc);
+ retval = PointerGetDatum(trv);
}
else
- PLy_restart_in_progress += 1;
+ {
+ proc = PLy_procedure_get(fcinfo, InvalidOid);
+ retval = PLy_function_handler(fcinfo, proc);
+ }
+ }
+ PG_CATCH();
+ {
if (proc)
{
/* note: Py_DECREF needs braces around it, as of 2003/08 */
Py_DECREF(proc->me);
}
- RERAISE_EXC();
+ PyErr_Clear();
+ PG_RE_THROW();
}
-
- /*
- * elog(DEBUG3, "PLy_restart_in_progress is %d",
- * PLy_restart_in_progress);
- */
-
- if (CALLED_AS_TRIGGER(fcinfo))
- {
- TriggerData *tdata = (TriggerData *) fcinfo->context;
- HeapTuple trv;
-
- proc = PLy_procedure_get(fcinfo,
- RelationGetRelid(tdata->tg_relation));
- trv = PLy_trigger_handler(fcinfo, proc);
- retval = PointerGetDatum(trv);
- }
- else
- {
- proc = PLy_procedure_get(fcinfo, InvalidOid);
- retval = PLy_function_handler(fcinfo, proc);
- }
-
- CALL_LEVEL_DEC();
- RESTORE_EXC();
+ PG_END_TRY();
Py_DECREF(proc->me);
- refc(proc->me);
return retval;
}
* BEFORE the event and is ROW level. postgres expects the function
* to take no arguments and return an argument of type trigger.
*/
-HeapTuple
+static HeapTuple
PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
{
- DECLARE_EXC();
HeapTuple rv = NULL;
PyObject *volatile plargs = NULL;
PyObject *volatile plrv = NULL;
- enter();
-
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
-
- Py_XDECREF(plargs);
- Py_XDECREF(plrv);
-
- RERAISE_EXC();
- }
-
plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
plrv = PLy_procedure_call(proc, "TD", plargs);
+ Assert(plrv != NULL);
+ Assert(!PLy_error_in_progress);
+
/*
* Disconnect from SPI manager
*/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
- if (plrv == NULL)
- elog(FATAL, "PLy_procedure_call returned NULL");
-
- if (PLy_restart_in_progress)
- elog(FATAL, "restart in progress not expected");
-
/*
* return of None means we're happy with the tuple
*/
elog(ERROR, "expected return to be \"SKIP\" or \"MODIFY\"");
}
}
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(plargs);
+ Py_XDECREF(plrv);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
Py_DECREF(plargs);
Py_DECREF(plrv);
- RESTORE_EXC();
-
return rv;
}
-HeapTuple
+static HeapTuple
PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
HeapTuple otup)
{
- DECLARE_EXC();
PyObject *volatile plntup;
PyObject *volatile plkeys;
PyObject *volatile platt;
modvalues = NULL;
modnulls = NULL;
- enter();
-
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
-
- Py_XDECREF(plntup);
- Py_XDECREF(plkeys);
- Py_XDECREF(platt);
- Py_XDECREF(plval);
- Py_XDECREF(plstr);
-
- if (modnulls)
- pfree(modnulls);
- if (modvalues)
- pfree(modvalues);
- if (modattrs)
- pfree(modattrs);
-
- RERAISE_EXC();
- }
-
if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
elog(ERROR, "TD[\"new\"] deleted, unable to modify tuple");
if (!PyDict_Check(plntup))
rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
modattrs, modvalues, modnulls);
-
- /*
- * FIXME -- these leak if not explicitly pfree'd by other elog calls,
- * no? (No, I think, but might as well leave the pfrees here...)
- */
- pfree(modattrs);
- pfree(modvalues);
- pfree(modnulls);
-
if (rtup == NULL)
elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result);
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(plntup);
+ Py_XDECREF(plkeys);
+ Py_XDECREF(platt);
+ Py_XDECREF(plval);
+ Py_XDECREF(plstr);
+
+ if (modnulls)
+ pfree(modnulls);
+ if (modvalues)
+ pfree(modvalues);
+ if (modattrs)
+ pfree(modattrs);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
Py_DECREF(plntup);
Py_DECREF(plkeys);
- RESTORE_EXC();
+ pfree(modattrs);
+ pfree(modvalues);
+ pfree(modnulls);
return rtup;
}
-PyObject *
+static PyObject *
PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *rv)
{
- DECLARE_EXC();
- TriggerData *tdata;
+ TriggerData *tdata = (TriggerData *) fcinfo->context;
PyObject *pltname,
*pltevent,
*pltwhen,
PyObject *volatile pltdata = NULL;
char *stroid;
- enter();
-
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
-
- Py_XDECREF(pltdata);
-
- RERAISE_EXC();
- }
-
- tdata = (TriggerData *) fcinfo->context;
-
pltdata = PyDict_New();
if (!pltdata)
PLy_elog(ERROR, "could not build arguments for trigger procedure");
}
PyDict_SetItemString(pltdata, "args", pltargs);
Py_DECREF(pltargs);
-
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(pltdata);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return pltdata;
}
/* function handler and friends
*/
-Datum
+static Datum
PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
{
- DECLARE_EXC();
Datum rv;
PyObject *volatile plargs = NULL;
PyObject *volatile plrv = NULL;
PyObject *volatile plrv_so = NULL;
char *plrv_sc;
- enter();
-
- /*
- * setup to catch elog in while building function arguments, and
- * DECREF the plargs if the function call fails
- */
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
-
- Py_XDECREF(plargs);
- Py_XDECREF(plrv);
- Py_XDECREF(plrv_so);
-
- RERAISE_EXC();
- }
-
plargs = PLy_function_build_args(fcinfo, proc);
plrv = PLy_procedure_call(proc, "args", plargs);
+ Assert(plrv != NULL);
+ Assert(!PLy_error_in_progress);
+
/*
* Disconnect from SPI manager and then create the return values datum
* (if the input function does a palloc for it this must not be
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
- if (plrv == NULL)
- {
- elog(FATAL, "PLy_procedure_call returned NULL");
-#ifdef NOT_USED
- if (!PLy_restart_in_progress)
- PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
-
- /*
- * FIXME is this dead code? i'm pretty sure it is for unnested
- * calls, but not for nested calls
- */
- RAISE_EXC(1);
-#endif
- }
-
/*
- * convert the python PyObject to a postgresql Datum FIXME returning a
- * NULL, ie PG_RETURN_NULL() blows the backend to small messy bits...
- * it this a bug or expected? so just call with the string value of
- * None for now
+ * convert the python PyObject to a postgresql Datum
*/
-
if (plrv == Py_None)
{
fcinfo->isnull = true;
Int32GetDatum(-1));
}
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(plargs);
+ Py_XDECREF(plrv);
+ Py_XDECREF(plrv_so);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
Py_XDECREF(plargs);
Py_DECREF(plrv);
return rv;
}
-PyObject *
+static PyObject *
PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs)
{
PyObject *rv;
PLyProcedure *current;
- enter();
-
current = PLy_last_procedure;
PLy_last_procedure = proc;
PyDict_SetItemString(proc->globals, kargs, vargs);
- rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals);
+ rv = PyEval_EvalCode((PyCodeObject *) proc->code,
+ proc->globals, proc->globals);
PLy_last_procedure = current;
+ /*
+ * If there was an error in a PG callback, propagate that
+ * no matter what Python claims about its success.
+ */
+ if (PLy_error_in_progress)
+ {
+ ErrorData *edata = PLy_error_in_progress;
+
+ PLy_error_in_progress = NULL;
+ ReThrowError(edata);
+ }
+
if ((rv == NULL) || (PyErr_Occurred()))
{
Py_XDECREF(rv);
- if (!PLy_restart_in_progress)
- PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
- RAISE_EXC(1);
+ PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
}
return rv;
}
-PyObject *
+static PyObject *
PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
{
- DECLARE_EXC();
PyObject *volatile arg = NULL;
PyObject *volatile args = NULL;
int i;
- enter();
-
- /*
- * FIXME -- if the setjmp setup is expensive, add the arg and args
- * field to the procedure struct and cleanup at the start of the next
- * call
- */
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
- Py_XDECREF(arg);
- Py_XDECREF(args);
-
- RERAISE_EXC();
- }
-
args = PyList_New(proc->nargs);
for (i = 0; i < proc->nargs; i++)
{
*/
PyList_SetItem(args, i, arg);
}
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(arg);
+ Py_XDECREF(args);
- RESTORE_EXC();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return args;
}
PLyProcedure *proc = NULL;
int rv;
- enter();
-
fn_oid = fcinfo->flinfo->fn_oid;
procTup = SearchSysCache(PROCOID,
ObjectIdGetDatum(fn_oid),
if (!PyCObject_Check(plproc))
elog(FATAL, "expected a PyCObject, didn't get one");
- mark();
-
proc = PyCObject_AsVoidPtr(plproc);
if (proc->me != plproc)
elog(FATAL, "proc->me != plproc");
{
char procName[NAMEDATALEN + 256];
- DECLARE_EXC();
Form_pg_proc procStruct;
PLyProcedure *volatile proc;
char *volatile procSource = NULL;
int i,
rv;
- enter();
-
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
if (OidIsValid(tgreloid))
proc->code = proc->statics = NULL;
proc->globals = proc->me = NULL;
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
- PLy_procedure_delete(proc);
- if (procSource)
- pfree(procSource);
- RERAISE_EXC();
- }
-
/*
* get information required for output conversion of the return value,
* but only if this isn't a trigger.
proc->me = PyCObject_FromVoidPtr(proc, NULL);
PyDict_SetItemString(PLy_procedure_cache, key, proc->me);
+ }
+ PG_CATCH();
+ {
+ PLy_procedure_delete(proc);
+ if (procSource)
+ pfree(procSource);
- RESTORE_EXC();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return proc;
}
-void
+static void
PLy_procedure_compile(PLyProcedure * proc, const char *src)
{
PyObject *crv = NULL;
char *msrc;
- enter();
-
proc->globals = PyDict_Copy(PLy_interp_globals);
/*
PLy_elog(ERROR, "could not compile function \"%s\"", proc->proname);
}
-char *
+static char *
PLy_procedure_munge_source(const char *name, const char *src)
{
char *mrc,
size_t mlen,
plen;
- enter();
-
/*
* room for function source and the def statement
*/
return mrc;
}
-void
+static void
PLy_procedure_delete(PLyProcedure * proc)
{
int i;
- enter();
-
Py_XDECREF(proc->code);
Py_XDECREF(proc->statics);
Py_XDECREF(proc->globals);
if (proc->args[i].out.r.atts)
PLy_free(proc->args[i].out.r.atts);
}
-
- leave();
}
/* conversion functions. remember output from python is
* input to postgresql, and vis versa.
*/
-void
+static void
PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
{
int i;
- enter();
-
if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
}
}
-void
+static void
PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
{
int i;
- enter();
-
if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
}
}
-void
+static void
PLy_output_datum_func(PLyTypeInfo * arg, HeapTuple typeTup)
{
- enter();
-
if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
arg->is_rowtype = 0;
PLy_output_datum_func2(&(arg->out.d), typeTup);
}
-void
+static void
PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
- enter();
-
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
arg->typioparam = getTypeIOParam(typeTup);
arg->typbyval = typeStruct->typbyval;
}
-void
+static void
PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, HeapTuple typeTup)
{
- enter();
-
if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
arg->is_rowtype = 0;
PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup);
}
-void
+static void
PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
}
}
-void
+static void
PLy_typeinfo_init(PLyTypeInfo * arg)
{
arg->is_rowtype = -1;
arg->out.r.atts = NULL;
}
-void
+static void
PLy_typeinfo_dealloc(PLyTypeInfo * arg)
{
if (arg->is_rowtype == 1)
/* assumes that a bool is always returned as a 't' or 'f'
*/
-PyObject *
+static PyObject *
PLyBool_FromString(const char *src)
{
- enter();
-
if (src[0] == 't')
return PyInt_FromLong(1);
return PyInt_FromLong(0);
}
-PyObject *
+static PyObject *
PLyFloat_FromString(const char *src)
{
double v;
char *eptr;
- enter();
-
errno = 0;
v = strtod(src, &eptr);
if ((*eptr != '\0') || (errno))
return PyFloat_FromDouble(v);
}
-PyObject *
+static PyObject *
PLyInt_FromString(const char *src)
{
long v;
char *eptr;
- enter();
-
errno = 0;
v = strtol(src, &eptr, 0);
if ((*eptr != '\0') || (errno))
return PyInt_FromLong(v);
}
-PyObject *
+static PyObject *
PLyLong_FromString(const char *src)
{
return PyLong_FromString((char *) src, NULL, 0);
}
-PyObject *
+static PyObject *
PLyString_FromString(const char *src)
{
return PyString_FromString(src);
}
-PyObject *
+static PyObject *
PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
{
- DECLARE_EXC();
PyObject *volatile dict;
int i;
- enter();
-
if (info->is_rowtype != 1)
elog(ERROR, "PLyTypeInfo structure describes a datum");
if (dict == NULL)
PLy_elog(ERROR, "could not create tuple dictionary");
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
- Py_DECREF(dict);
-
- RERAISE_EXC();
- }
-
for (i = 0; i < info->in.r.natts; i++)
{
char *key,
Py_DECREF(value);
}
}
-
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_DECREF(dict);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return dict;
}
/* plan object methods
*/
-PyObject *
+static PyObject *
PLy_plan_new(void)
{
PLyPlanObject *ob;
- enter();
-
if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL)
return NULL;
}
-void
+static void
PLy_plan_dealloc(PyObject * arg)
{
PLyPlanObject *ob = (PLyPlanObject *) arg;
- enter();
-
if (ob->plan)
SPI_freeplan(ob->plan);
if (ob->types)
}
PyMem_DEL(arg);
-
- leave();
}
-PyObject *
+static PyObject *
PLy_plan_getattr(PyObject * self, char *name)
{
return Py_FindMethod(PLy_plan_methods, self, name);
}
-PyObject *
+static PyObject *
PLy_plan_status(PyObject * self, PyObject * args)
{
if (PyArg_ParseTuple(args, ""))
/* result object methods
*/
-PyObject *
+static PyObject *
PLy_result_new(void)
{
PLyResultObject *ob;
- enter();
-
if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL)
return NULL;
return (PyObject *) ob;
}
-void
+static void
PLy_result_dealloc(PyObject * arg)
{
PLyResultObject *ob = (PLyResultObject *) arg;
- enter();
-
Py_XDECREF(ob->nrows);
Py_XDECREF(ob->rows);
Py_XDECREF(ob->status);
PyMem_DEL(ob);
}
-PyObject *
+static PyObject *
PLy_result_getattr(PyObject * self, char *attr)
{
return NULL;
#ifdef NOT_USED
/* Appear to be unused */
-PyObject *
+static PyObject *
PLy_result_fetch(PyObject * self, PyObject * args)
{
return NULL;
}
-PyObject *
+static PyObject *
PLy_result_nrows(PyObject * self, PyObject * args)
{
PLyResultObject *ob = (PLyResultObject *) self;
return ob->nrows;
}
-PyObject *
+static PyObject *
PLy_result_status(PyObject * self, PyObject * args)
{
PLyResultObject *ob = (PLyResultObject *) self;
return ob->status;
}
#endif
-int
+
+static int
PLy_result_length(PyObject * arg)
{
PLyResultObject *ob = (PLyResultObject *) arg;
return PyList_Size(ob->rows);
}
-PyObject *
+static PyObject *
PLy_result_item(PyObject * arg, int idx)
{
PyObject *rv;
return rv;
}
-int
+static int
PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
{
int rv;
return rv;
}
-PyObject *
+static PyObject *
PLy_result_slice(PyObject * arg, int lidx, int hidx)
{
PyObject *rv;
return rv;
}
-int
+static int
PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
{
int rv;
/* SPI interface
*/
-PyObject *
+static PyObject *
PLy_spi_prepare(PyObject * self, PyObject * args)
{
- DECLARE_EXC();
PLyPlanObject *plan;
PyObject *list = NULL;
PyObject *volatile optr = NULL;
char *query;
void *tmpplan;
+ MemoryContext oldcontext;
- enter();
+ /* Can't execute more if we have an unhandled error */
+ if (PLy_error_in_progress)
+ {
+ PyErr_SetString(PLy_exc_error, "Transaction aborted.");
+ return NULL;
+ }
if (!PyArg_ParseTuple(args, "s|O", &query, &list))
{
return NULL;
}
-
if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
return NULL;
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- RESTORE_EXC();
- Py_DECREF(plan);
- Py_XDECREF(optr);
- if (!PyErr_Occurred())
- PyErr_SetString(PLy_exc_spi_error,
- "Unknown error in PLy_spi_prepare");
- /* XXX this oughta be replaced with errcontext mechanism */
- PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
- RERAISE_EXC();
- }
-
if (list != NULL)
{
int nargs,
optr = PySequence_GetItem(list, i);
if (!PyString_Check(optr))
- {
- PyErr_SetString(PLy_exc_spi_error,
- "Type names must be strings.");
- RAISE_EXC(1);
- }
+ elog(ERROR, "Type names must be strings.");
sptr = PyString_AsString(optr);
/* XXX should extend this to allow qualified type names */
typeTup = typenameType(makeTypeName(sptr));
if (typeStruct->typtype != 'c')
PLy_output_datum_func(&plan->args[i], typeTup);
else
- {
- PyErr_SetString(PLy_exc_spi_error,
- "tuples not handled in plpy.prepare, yet.");
- RAISE_EXC(1);
- }
+ elog(ERROR, "tuples not handled in plpy.prepare, yet.");
ReleaseSysCache(typeTup);
}
}
plan->plan = SPI_prepare(query, plan->nargs, plan->types);
if (plan->plan == NULL)
- {
- PLy_exception_set(PLy_exc_spi_error,
- "Unable to prepare plan. SPI_prepare failed -- %s.",
- PLy_spi_error_string(SPI_result));
- RAISE_EXC(1);
- }
+ elog(ERROR, "Unable to prepare plan. SPI_prepare failed -- %s.",
+ PLy_spi_error_string(SPI_result));
/* transfer plan from procCxt to topCxt */
tmpplan = plan->plan;
plan->plan = SPI_saveplan(tmpplan);
SPI_freeplan(tmpplan);
if (plan->plan == NULL)
+ elog(ERROR, "Unable to save plan. SPI_saveplan failed -- %s.",
+ PLy_spi_error_string(SPI_result));
+ }
+ PG_CATCH();
{
- PLy_exception_set(PLy_exc_spi_error,
- "Unable to save plan. SPI_saveplan failed -- %s.",
- PLy_spi_error_string(SPI_result));
- RAISE_EXC(1);
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Py_DECREF(plan);
+ Py_XDECREF(optr);
+ if (!PyErr_Occurred())
+ PyErr_SetString(PLy_exc_spi_error,
+ "Unknown error in PLy_spi_prepare");
+ /* XXX this oughta be replaced with errcontext mechanism */
+ PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
+ return NULL;
}
-
- RESTORE_EXC();
+ PG_END_TRY();
return (PyObject *) plan;
}
/* execute(query="select * from foo", limit=5)
* execute(plan=plan, values=(foo, bar), limit=5)
*/
-PyObject *
+static PyObject *
PLy_spi_execute(PyObject * self, PyObject * args)
{
char *query;
PyObject *list = NULL;
int limit = 0;
- enter();
-
-#ifdef NOT_USED
-
- /*
- * there should - hahaha - be an python exception set so just return
- * NULL. FIXME -- is this needed?
- */
- if (PLy_restart_in_progress)
+ /* Can't execute more if we have an unhandled error */
+ if (PLy_error_in_progress)
+ {
+ PyErr_SetString(PLy_exc_error, "Transaction aborted.");
return NULL;
-#endif
+ }
if (PyArg_ParseTuple(args, "s|i", &query, &limit))
return PLy_spi_execute_query(query, limit);
if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) &&
(is_PLyPlanObject(plan)))
- {
- PyObject *rv = PLy_spi_execute_plan(plan, list, limit);
-
- return rv;
- }
+ return PLy_spi_execute_plan(plan, list, limit);
PyErr_SetString(PLy_exc_error, "Expected a query or plan.");
return NULL;
}
-PyObject *
+static PyObject *
PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
{
- DECLARE_EXC();
volatile int nargs;
int i,
rv;
PLyPlanObject *plan;
char *nulls;
-
- enter();
+ MemoryContext oldcontext;
if (list != NULL)
{
return NULL;
}
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- RESTORE_EXC();
-
- /*
- * cleanup plan->values array
- */
- for (i = 0; i < nargs; i++)
- {
- if (!plan->args[i].out.d.typbyval &&
- (plan->values[i] != (Datum) NULL))
- {
- pfree(DatumGetPointer(plan->values[i]));
- plan->values[i] = (Datum) NULL;
- }
- }
-
- if (!PyErr_Occurred())
- PyErr_SetString(PLy_exc_error,
- "Unknown error in PLy_spi_execute_plan");
- PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
- RERAISE_EXC();
- }
-
nulls = palloc(nargs * sizeof(char));
for (i = 0; i < nargs; i++)
sv = PyString_AsString(so);
/*
- * FIXME -- if this can elog, we have leak
+ * FIXME -- if this elogs, we have Python reference leak
*/
plan->values[i] =
FunctionCall3(&(plan->args[i].out.d.typfunc),
rv = SPI_execp(plan->plan, plan->values, nulls, limit);
pfree(nulls);
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ /*
+ * cleanup plan->values array
+ */
+ for (i = 0; i < nargs; i++)
+ {
+ if (!plan->args[i].out.d.typbyval &&
+ (plan->values[i] != (Datum) NULL))
+ {
+ pfree(DatumGetPointer(plan->values[i]));
+ plan->values[i] = (Datum) NULL;
+ }
+ }
- RESTORE_EXC();
+ if (!PyErr_Occurred())
+ PyErr_SetString(PLy_exc_error,
+ "Unknown error in PLy_spi_execute_plan");
+ PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
+ return NULL;
+ }
+ PG_END_TRY();
for (i = 0; i < nargs; i++)
{
return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
}
-PyObject *
+static PyObject *
PLy_spi_execute_query(char *query, int limit)
{
- DECLARE_EXC();
int rv;
+ MemoryContext oldcontext;
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
+ {
+ rv = SPI_exec(query, limit);
+ }
+ PG_CATCH();
{
- RESTORE_EXC();
- if ((!PLy_restart_in_progress) && (!PyErr_Occurred()))
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ if (!PyErr_Occurred())
PyErr_SetString(PLy_exc_spi_error,
"Unknown error in PLy_spi_execute_query");
PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
- RERAISE_EXC();
+ return NULL;
}
+ PG_END_TRY();
- rv = SPI_exec(query, limit);
- RESTORE_EXC();
if (rv < 0)
{
PLy_exception_set(PLy_exc_spi_error,
return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
}
-PyObject *
+static PyObject *
PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
{
PLyResultObject *result;
-
- enter();
+ MemoryContext oldcontext;
result = (PLyResultObject *) PLy_result_new();
Py_DECREF(result->status);
}
else
{
- DECLARE_EXC();
PLyTypeInfo args;
int i;
Py_DECREF(result->nrows);
result->nrows = PyInt_FromLong(rows);
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- RESTORE_EXC();
-
- if (!PyErr_Occurred())
- PyErr_SetString(PLy_exc_error,
- "Unknown error in PLy_spi_execute_fetch_result");
- Py_DECREF(result);
- PLy_typeinfo_dealloc(&args);
- RERAISE_EXC();
- }
+ if (rows)
+ {
+ Py_DECREF(result->rows);
+ result->rows = PyList_New(rows);
- if (rows)
- {
- Py_DECREF(result->rows);
- result->rows = PyList_New(rows);
+ PLy_input_tuple_funcs(&args, tuptable->tupdesc);
+ for (i = 0; i < rows; i++)
+ {
+ PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i],
+ tuptable->tupdesc);
- PLy_input_tuple_funcs(&args, tuptable->tupdesc);
- for (i = 0; i < rows; i++)
- {
- PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i],
- tuptable->tupdesc);
+ PyList_SetItem(result->rows, i, row);
+ }
+ PLy_typeinfo_dealloc(&args);
- PyList_SetItem(result->rows, i, row);
+ SPI_freetuptable(tuptable);
}
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ if (!PyErr_Occurred())
+ PyErr_SetString(PLy_exc_error,
+ "Unknown error in PLy_spi_execute_fetch_result");
+ Py_DECREF(result);
PLy_typeinfo_dealloc(&args);
-
- SPI_freetuptable(tuptable);
+ return NULL;
}
- RESTORE_EXC();
+ PG_END_TRY();
}
return (PyObject *) result;
}
-const char *
+static const char *
PLy_spi_error_string(int code)
{
switch (code)
if (!PLy_first_call)
return;
- enter();
-
if (init_active)
elog(FATAL, "initialization of language module failed");
init_active = 1;
PLy_elog(ERROR, "could not create procedure cache");
PLy_first_call = 0;
-
- leave();
}
static void
}
-void
+static void
PLy_init_interp(void)
{
PyObject *mainmod;
- enter();
-
mainmod = PyImport_AddModule("__main__");
if ((mainmod == NULL) || (PyErr_Occurred()))
PLy_elog(ERROR, "could not import \"__main__\" module.");
PLy_elog(ERROR, "could not initialize globals");
}
-void
+static void
PLy_init_plpy(void)
{
PyObject *main_mod,
PyObject *plpy,
*plpy_dict;
- enter();
-
/*
* initialize plpy module
*/
*/
static PyObject *PLy_output(int, PyObject *, PyObject *);
-PyObject *
+static PyObject *
PLy_debug(PyObject * self, PyObject * args)
{
return PLy_output(DEBUG2, self, args);
}
-PyObject *
+static PyObject *
PLy_log(PyObject * self, PyObject * args)
{
return PLy_output(LOG, self, args);
}
-PyObject *
+static PyObject *
PLy_info(PyObject * self, PyObject * args)
{
return PLy_output(INFO, self, args);
}
-PyObject *
+static PyObject *
PLy_notice(PyObject * self, PyObject * args)
{
return PLy_output(NOTICE, self, args);
}
-PyObject *
+static PyObject *
PLy_warning(PyObject * self, PyObject * args)
{
return PLy_output(WARNING, self, args);
}
-PyObject *
+static PyObject *
PLy_error(PyObject * self, PyObject * args)
{
return PLy_output(ERROR, self, args);
}
-PyObject *
+static PyObject *
PLy_fatal(PyObject * self, PyObject * args)
{
return PLy_output(FATAL, self, args);
}
-PyObject *
+static PyObject *
PLy_output(volatile int level, PyObject * self, PyObject * args)
{
- DECLARE_EXC();
PyObject *so;
char *volatile sv;
-
- enter();
-
- if (args == NULL)
- elog(WARNING, "args is NULL");
+ MemoryContext oldcontext;
so = PyObject_Str(args);
if ((so == NULL) || ((sv = PyString_AsString(so)) == NULL))
sv = "Unable to parse error message in `plpy.elog'";
}
- /*
- * returning NULL here causes the python interpreter to bail. when
- * control passes back into plpython_*_handler, we check for python
- * exceptions and do the actual elog call. actually PLy_elog.
- */
- if (level == ERROR)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- PyErr_SetString(PLy_exc_error, sv);
- return NULL;
+ elog(level, "%s", sv);
}
- else if (level >= FATAL)
+ PG_CATCH();
{
- PyErr_SetString(PLy_exc_fatal, sv);
- return NULL;
- }
-
- /*
- * ok, this is a WARNING, or LOG message
- *
- * but just in case DON'T long jump out of the interpreter!
- */
- SAVE_EXC();
- if (TRAP_EXC())
- {
- RESTORE_EXC();
-
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
Py_XDECREF(so);
-
/*
- * the real error message should already be written into the
- * postgresql log, no? whatever, this shouldn't happen so die
- * hideously.
+ * returning NULL here causes the python interpreter to bail. when
+ * control passes back to PLy_procedure_call, we check for PG
+ * exceptions and re-throw the error.
*/
- elog(FATAL, "elog threw an unknown exception");
- RERAISE_EXC();
+ PyErr_SetString(PLy_exc_error, sv);
+ return NULL;
}
-
- elog(level, "%s", sv);
-
- RESTORE_EXC();
+ PG_END_TRY();
Py_XDECREF(so);
- Py_INCREF(Py_None);
/*
* return a legal object so the interpreter will continue on its merry
* way
*/
+ Py_INCREF(Py_None);
return Py_None;
}
* NB: this returns SQL name, not the internal Python procedure name
*/
-char *
+static char *
PLy_procedure_name(PLyProcedure * proc)
{
if (proc == NULL)
/* output a python traceback/exception via the postgresql elog
* function. not pretty.
*/
-
-static char *PLy_traceback(int *);
-static char *PLy_vprintf(const char *fmt, va_list ap);
-static char *PLy_printf(const char *fmt,...);
-
-void
+static void
PLy_exception_set(PyObject * exc, const char *fmt,...)
{
char buf[1024];
PyErr_SetString(exc, buf);
}
-void
+/* Emit a PG error or notice, together with any available info about the
+ * current Python error. This should be used to propagate Python errors
+ * into PG.
+ */
+static void
PLy_elog(int elevel, const char *fmt,...)
{
- DECLARE_EXC();
va_list ap;
char *xmsg,
*emsg;
int xlevel;
- enter();
-
xmsg = PLy_traceback(&xlevel);
va_start(ap, fmt);
emsg = PLy_vprintf(fmt, ap);
va_end(ap);
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
+ {
+ ereport(elevel,
+ (errmsg("plpython: %s", emsg),
+ (xmsg) ? errdetail("%s", xmsg) : 0));
+ }
+ PG_CATCH();
{
- RESTORE_EXC();
- mark();
-
- /*
- * elog called siglongjmp. cleanup, restore and reraise
- */
- PLy_restart_in_progress += 1;
PLy_free(emsg);
if (xmsg)
PLy_free(xmsg);
- RERAISE_EXC();
+ PG_RE_THROW();
}
-
- ereport(elevel,
- (errmsg("plpython: %s", emsg),
- (xmsg) ? errdetail("%s", xmsg) : 0));
+ PG_END_TRY();
PLy_free(emsg);
if (xmsg)
PLy_free(xmsg);
-
- leave();
-
- RESTORE_EXC();
}
-char *
+static char *
PLy_traceback(int *xlevel)
{
PyObject *e,
*estr,
*xstr = NULL;
- enter();
-
/*
* get the current exception
*/
else
*xlevel = ERROR;
- leave();
-
return xstr;
}
-char *
+static char *
PLy_printf(const char *fmt,...)
{
va_list ap;
return emsg;
}
-char *
+static char *
PLy_vprintf(const char *fmt, va_list ap)
{
size_t blen;
/* some dumb utility functions
*/
-void *
+static void *
PLy_malloc(size_t bytes)
{
void *ptr = malloc(bytes);
return ptr;
}
-void *
+static void *
PLy_realloc(void *optr, size_t bytes)
{
void *nptr = realloc(optr, bytes);
/* define this away
*/
-void
+static void
PLy_free(void *ptr)
{
free(ptr);
+++ /dev/null
-/* $PostgreSQL: pgsql/src/pl/plpython/plpython.h,v 1.9 2003/11/29 19:52:13 pgsql Exp $ */
-
-#ifndef PLPYTHON_H
-#define PLPYTHON_H
-
-#define DEBUG_EXC 0
-#define DEBUG_LEVEL 0
-
-#define DECLARE_N_EXC(N) int rv_##N; sigjmp_buf buf_##N
-#define TRAP_N_EXC(N) ((rv_##N = sigsetjmp(Warn_restart, 1)) != 0)
-
-#if !DEBUG_EXC
-#define RESTORE_N_EXC(N) memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf))
-#define SAVE_N_EXC(N) memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf))
-#define RERAISE_N_EXC(N) siglongjmp(Warn_restart, rv_##N)
-#define RAISE_EXC(V) siglongjmp(Warn_restart, (V))
-#else
-#define RESTORE_N_EXC(N) do { \
- elog(WARNING, "exception (%d,%d) restore at %s:%d",\
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__));\
- exc_save_calls -= 1; \
- memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf)); } while (0)
-#define SAVE_N_EXC(N) do { \
- exc_save_calls += 1; \
- elog(WARNING, "exception (%d,%d) save at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf)); } while (0)
-#define RERAISE_N_EXC(N) do { \
- elog(WARNING, "exception (%d,%d) reraise at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- siglongjmp(Warn_restart, rv_##N); } while (0)
-#define RAISE_EXC(V) do { \
- elog(WARNING, "exception (%d,%d) raise at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- siglongjmp(Warn_restart, (V)); } while (0)
-#endif
-
-#define DECLARE_EXC() DECLARE_N_EXC(save_restart)
-#define SAVE_EXC() SAVE_N_EXC(save_restart)
-#define RERAISE_EXC() RERAISE_N_EXC(save_restart)
-#define RESTORE_EXC() RESTORE_N_EXC(save_restart)
-#define TRAP_EXC() TRAP_N_EXC(save_restart)
-
-#if DEBUG_LEVEL
-#define CALL_LEVEL_INC() do { PLy_call_level += 1; \
- elog(DEBUG4, "level: %d", PLy_call_level); } while (0)
-#define CALL_LEVEL_DEC() do { elog(DEBUG4, "level: %d", PLy_call_level); \
- PLy_call_level -= 1; } while (0)
-#else
-#define CALL_LEVEL_INC() do { PLy_call_level += 1; } while (0)
-#define CALL_LEVEL_DEC() do { PLy_call_level -= 1; } while (0)
-#endif
-
-/* temporary debugging macros
- */
-#if DEBUG_LEVEL
-#define enter() elog(DEBUG4, "Enter(%d): %s", func_enter_calls++,__FUNCTION__)
-#define leave() elog(DEBUG4, "Leave(%d): %s", func_leave_calls++,__FUNCTION__)
-#define mark() elog(DEBUG4, "Mark: %s:%d", __FUNCTION__, __LINE__);
-#define refc(O) elog(DEBUG4, "Ref<%p>:<%d>:%s:%d", (O), (((O) == NULL) ? -1 : (O)->ob_refcnt), __FUNCTION__, __LINE__)
-#else
-#define enter()
-#define leave()
-#define mark()
-#define refc(O)
-#endif
-
-#endif /* PLPYTHON_H */
* 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 $
*
**********************************************************************/
#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
/* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
#ifndef CONST84
**********************************************************************/
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;
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
**********************************************************************/
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
pltcl_current_fcinfo = save_fcinfo;
- pltcl_call_level--;
-
return retval;
}
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);
Tcl_DStringInit(&list_tmp);
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])
}
}
}
+ }
+ 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;
UTF_END;
}
- /************************************************************
- * Finally we may restore normal error handling.
- ************************************************************/
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
return retval;
}
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,
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);
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;
}
/************************************************************
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));
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];
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;
}
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)
{
}
/************************************************************
- * 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;
}
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;
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
}
/************************************************************
- * 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)
{
}
/************************************************************
- * 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;
}
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
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
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
************************************************************/
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);
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);
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;
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 "
"?-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
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;
}
* 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;
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()
}
/************************************************************
- * 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;
}