* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.93 2005/10/15 02:49:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.94 2005/10/18 17:13:14 tgl Exp $
*
**********************************************************************/
Datum plperl_validator(PG_FUNCTION_ARGS);
void plperl_init(void);
-HV *plperl_spi_exec(char *query, int limit);
-SV *plperl_spi_query(char *);
-
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
static void plperl_init_shared_libs(pTHX);
static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int);
-void plperl_return_next(SV *);
-
/*
* This routine is a crock, and so is everyplace that calls it. The problem
* is that the cached form of plperl functions/queries is allocated permanently
}
+/*
+ * Note: plperl_return_next is called both in Postgres and Perl contexts.
+ * We report any errors in Postgres fashion (via ereport). If called in
+ * Perl context, it is SPI.xs's responsibility to catch the error and
+ * convert to a Perl error. We assume (perhaps without adequate justification)
+ * that we need not abort the current transaction if the Perl code traps the
+ * error.
+ */
void
-plperl_return_next(SV * sv)
+plperl_return_next(SV *sv)
{
plperl_proc_desc *prodesc = plperl_current_prodesc;
FunctionCallInfo fcinfo = plperl_current_caller_info;
return;
if (!prodesc->fn_retisset)
- {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use return_next in a non-SETOF function")));
- }
if (prodesc->fn_retistuple &&
!(SvOK(sv) && SvTYPE(sv) == SVt_RV && SvTYPE(SvRV(sv)) == SVt_PVHV))
- {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("setof-composite-returning Perl function "
"must call return_next with reference to hash")));
- }
cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
{
SV *cursor;
+ /*
+ * Execute the query inside a sub-transaction, so we can cope with errors
+ * sanely
+ */
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
BeginInternalSubTransaction(NULL);
+ /* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
PG_TRY();
void *plan;
Portal portal = NULL;
+ /* Create a cursor for the query */
plan = SPI_prepare(query, 0, NULL);
if (plan)
portal = SPI_cursor_open(NULL, plan, NULL, NULL, false);
else
cursor = newSV(0);
+ /* Commit the inner transaction, return to outer xact context */
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
+
+ /*
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
+ * in case it did, make sure we remain connected.
+ */
SPI_restore_connection();
}
PG_CATCH();
{
ErrorData *edata;
+ /* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
+ /* Abort the inner transaction */
RollbackAndReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
+ /*
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
+ * have left us in a disconnected state. We need this hack to return
+ * to connected state.
+ */
SPI_restore_connection();
+
+ /* Punt the error to Perl */
croak("%s", edata->message);
+
+ /* Can't get here, but keep compiler quiet */
return NULL;
}
PG_END_TRY();
SV *
plperl_spi_fetchrow(char *cursor)
{
- SV *row = newSV(0);
- Portal p = SPI_cursor_find(cursor);
+ SV *row;
+
+ /*
+ * Execute the FETCH inside a sub-transaction, so we can cope with errors
+ * sanely
+ */
+ MemoryContext oldcontext = CurrentMemoryContext;
+ ResourceOwner oldowner = CurrentResourceOwner;
- if (!p)
- return row;
+ BeginInternalSubTransaction(NULL);
+ /* Want to run inside function's memory context */
+ MemoryContextSwitchTo(oldcontext);
- SPI_cursor_fetch(p, true, 1);
- if (SPI_processed == 0)
+ PG_TRY();
{
- SPI_cursor_close(p);
- return row;
+ Portal p = SPI_cursor_find(cursor);
+
+ if (!p)
+ row = newSV(0);
+ else
+ {
+ SPI_cursor_fetch(p, true, 1);
+ if (SPI_processed == 0)
+ {
+ SPI_cursor_close(p);
+ row = newSV(0);
+ }
+ else
+ {
+ row = plperl_hash_from_tuple(SPI_tuptable->vals[0],
+ SPI_tuptable->tupdesc);
+ }
+ SPI_freetuptable(SPI_tuptable);
+ }
+
+ /* Commit the inner transaction, return to outer xact context */
+ ReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
+ * in case it did, make sure we remain connected.
+ */
+ SPI_restore_connection();
}
+ PG_CATCH();
+ {
+ ErrorData *edata;
- row = plperl_hash_from_tuple(SPI_tuptable->vals[0],
- SPI_tuptable->tupdesc);
- SPI_freetuptable(SPI_tuptable);
+ /* Save error info */
+ MemoryContextSwitchTo(oldcontext);
+ edata = CopyErrorData();
+ FlushErrorState();
+
+ /* Abort the inner transaction */
+ RollbackAndReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
+ * have left us in a disconnected state. We need this hack to return
+ * to connected state.
+ */
+ SPI_restore_connection();
+
+ /* Punt the error to Perl */
+ croak("%s", edata->message);
+
+ /* Can't get here, but keep compiler quiet */
+ return NULL;
+ }
+ PG_END_TRY();
return row;
}