Beispiel #1
0
/*
 * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
 *
 * Exit an explicit subtransaction. exc_type is an exception type, exc
 * is the exception object, tb is the traceback.  If exc_type is None,
 * commit the subtransactiony, if not abort it.
 *
 * The method signature is chosen to allow subtransaction objects to
 * be used as context managers as described in
 * <http://www.python.org/dev/peps/pep-0343/>.
 */
static PyObject *
PLy_subtransaction_exit(PyObject *self, PyObject *args)
{
	PyObject   *type;
	PyObject   *value;
	PyObject   *traceback;
	PLySubtransactionData *subxactdata;
	PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;

	if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
		return NULL;

	if (!subxact->started)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
		return NULL;
	}

	if (subxact->exited)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
		return NULL;
	}

	if (explicit_subtransactions == NIL)
	{
		PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
		return NULL;
	}

	subxact->exited = true;

	if (type != Py_None)
	{
		/* Abort the inner transaction */
		RollbackAndReleaseCurrentSubTransaction();
	}
	else
	{
		ReleaseCurrentSubTransaction();
	}

	subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
	explicit_subtransactions = list_delete_first(explicit_subtransactions);

	MemoryContextSwitchTo(subxactdata->oldcontext);
	CurrentResourceOwner = subxactdata->oldowner;
	pfree(subxactdata);

	/*
	 * AtEOSubXact_SPI() should not have popped any SPI context, but just in
	 * case it did, make sure we remain connected.
	 */
	SPI_restore_connection();

	Py_INCREF(Py_None);
	return Py_None;
}
Beispiel #2
0
void
PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
{
	/* 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();
}
Beispiel #3
0
SV *
plperl_spi_fetchrow(char *cursor)
{
	SV		   *row;

	/*
	 * Execute the FETCH 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();
	{
		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;

		/* 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;
}
Beispiel #4
0
SV *
plperl_spi_query(char *query)
{
	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);
		if (portal)
			cursor = newSVpv(portal->name, 0);
		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();

	return cursor;
}
Beispiel #5
0
HV *
plperl_spi_exec(char *query, int limit)
{
	HV		   *ret_hv;

	/*
	 * 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();
	{
		int			spi_rv;

		spi_rv = SPI_execute(query, plperl_current_prodesc->fn_readonly,
							 limit);
		ret_hv = plperl_spi_execute_fetch_result(SPI_tuptable, SPI_processed,
												 spi_rv);

		/* 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();

	return ret_hv;
}
Beispiel #6
0
bool
pl_ist_commit(unsigned long xid, char state)
{
	bool r = true;

	/*
	 * Attempted commit while in an error state?
	 * Resolve to abort, and raise an exception.
	 */
	if (pl_state)
	{
		r = false; /* Always raises an exception in here */

		if (pl_ist_abort(xid, state))
		{
			/*
			 * The abort was successful, but that fact needs to be
			 * communicated as the original action was a commit.
			 * Raise an exception.
			 */
			PyErr_SetInFailedTransaction();
		}

		Assert(PyErr_Occurred());
	}
	else
	{
		PG_TRY();
		{
			if (pl_ist_count != xid)
			{
				ereport(ERROR,(
					errcode(ERRCODE_SAVEPOINT_EXCEPTION),
					errmsg("out-of-order commit attempted on subtransaction %lu", xid),
					errdetail("Subtransaction %lu was expected to exit next.", pl_ist_count)
				));
			}
			/* Prevent wrap around */
			if (pl_ist_count == 0)
			{
				ereport(ERROR,(
					errcode(ERRCODE_SAVEPOINT_EXCEPTION),
					errmsg("no current internal subtransaction"),
					errhint("Attempt to commit an IST, when none running.")
				));
			}
			if (state == pl_ist_committed)
			{
				ereport(ERROR,(
					errcode(ERRCODE_SAVEPOINT_EXCEPTION),
					errmsg("subtransaction already committed")
				));
			}
			if (state == pl_ist_aborted)
			{
				ereport(ERROR,(
					errcode(ERRCODE_SAVEPOINT_EXCEPTION),
					errmsg("cannot commit aborted subtransaction")
				));
			}
			if (state == pl_ist_new)
			{
				ereport(ERROR,(
					errcode(ERRCODE_SAVEPOINT_EXCEPTION),
					errmsg("cannot commit a subtransaction that has not been started")
				));
			}

			pl_ist_count = pl_ist_count - 1;
			ReleaseCurrentSubTransaction();
		}
		PG_CATCH();
		{
			PyErr_SetPgError(true);
			pl_state = pl_in_failed_transaction;
			r = false;
		}
		PG_END_TRY();
		/* end of commit */
	}

	return(r);
}
Beispiel #7
0
static void
test_spi_exec_utility(int *passed, int *total)
{
	int				rc;
	volatile int	caseno = 0;

	/* Initialize */
	rc = SPI_connect();
	if (rc != SPI_OK_CONNECT)
		elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));

	/*
	 * *-*-1
	 *   - query error
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		spi_exec_utility("RESET dummy_parameter");
		elog(WARNING, "*-*-%d failed", caseno);
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
		(*passed)++;
		RollbackAndReleaseCurrentSubTransaction();
		FlushErrorState();
		SPI_restore_connection();
	}
	PG_END_TRY();

	/*
	 * *-*-2
	 *   - query success
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		spi_exec_utility("RESET client_min_messages");
		elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
		(*passed)++;
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "*-*-%d failed", caseno);
		RollbackAndReleaseCurrentSubTransaction();
		SPI_restore_connection();
	}
	PG_END_TRY();

	/* report # of tests */
	(*total) += caseno;

	/* Cleanup */
	rc = SPI_finish();
	if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
		elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
}
Beispiel #8
0
static void
test_spi_exec_query(int *passed, int *total)
{
	int				rc;
	volatile int	caseno = 0;
	SPIPlanPtr		ptr = NULL;
	SPIPlanPtr		org_ptr;

	/* Initialize */
	rc = SPI_connect();
	if (rc != SPI_OK_CONNECT)
		elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));

	/*
	 * *-*-1
	 *   - plan is not cached
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
		if (ptr != NULL && SPI_processed == 1)
		{
			elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
			(*passed)++;
		}
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "*-*-%d failed", caseno);
		RollbackAndReleaseCurrentSubTransaction();
		SPI_restore_connection();
	}
	PG_END_TRY();

	/*
	 * *-*-2
	 *   - plan is cached
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		org_ptr = ptr;
		spi_exec_query(NULL, 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
		if (ptr == org_ptr && SPI_processed == 1)
		{
			elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
			(*passed)++;
		}
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "*-*-%d failed", caseno);
		RollbackAndReleaseCurrentSubTransaction();
		FlushErrorState();
		SPI_restore_connection();
	}
	PG_END_TRY();
	SPI_freeplan(ptr);
	ptr = NULL;

	/*
	 * *-*-3
	 *   - query error
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		spi_exec_query("SELECT 1 / 0",
					   0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
		elog(WARNING, "*-*-%d failed", caseno);
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
		(*passed)++;
		RollbackAndReleaseCurrentSubTransaction();
		FlushErrorState();
		SPI_restore_connection();
	}
	PG_END_TRY();
	SPI_freeplan(ptr);
	ptr = NULL;

	/*
	 * *-*-4
	 *   - query success
	 */
	caseno++;
	BeginInternalSubTransaction("test");
	PG_TRY();
	{
		spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
		if (ptr != NULL && SPI_processed == 1)
		{
			elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
			(*passed)++;
		}
		ReleaseCurrentSubTransaction();
	}
	PG_CATCH();
	{
		elog(WARNING, "*-*-%d failed", caseno);
		PG_RE_THROW();
		RollbackAndReleaseCurrentSubTransaction();
		SPI_restore_connection();
	}
	PG_END_TRY();
	SPI_freeplan(ptr);
	ptr = NULL;

	/* report # of tests */
	(*total) += caseno;

	/* Cleanup */
	rc = SPI_finish();
	if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
		elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
}