Snapshot snapshot;
MemoryContext oldcontext;
Portal portal;
+ ErrorContextCallback spierrcontext;
/*
* Check that the plan is something the Portal code will special-case as
query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
plansource->query_string);
+ /*
+ * Setup error traceback support for ereport(), in case GetCachedPlan
+ * throws an error.
+ */
+ spierrcontext.callback = _SPI_error_callback;
+ spierrcontext.arg = (void *) plansource->query_string;
+ spierrcontext.previous = error_context_stack;
+ error_context_stack = &spierrcontext;
+
/*
* Note: for a saved plan, we mustn't have any failure occur between
* GetCachedPlan and PortalDefineQuery; that would result in leaking our
cplan = GetCachedPlan(plansource, paramLI, false);
stmt_list = cplan->stmt_list;
+ /* Pop the error context stack */
+ error_context_stack = spierrcontext.previous;
+
if (!plan->saved)
{
/*
return buf;
}
+/*
+ * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
+ * CachedPlanSources.
+ *
+ * This is exported so that pl/pgsql can use it (this beats letting pl/pgsql
+ * look directly into the SPIPlan for itself). It's not documented in
+ * spi.sgml because we'd just as soon not have too many places using this.
+ */
+List *
+SPI_plan_get_plan_sources(SPIPlanPtr plan)
+{
+ Assert(plan->magic == _SPI_PLAN_MAGIC);
+ return plan->plancache_list;
+}
+
+/*
+ * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
+ * if the SPI plan contains exactly one CachedPlanSource. If not,
+ * return NULL. Caller is responsible for doing ReleaseCachedPlan().
+ *
+ * This is exported so that pl/pgsql can use it (this beats letting pl/pgsql
+ * look directly into the SPIPlan for itself). It's not documented in
+ * spi.sgml because we'd just as soon not have too many places using this.
+ */
+CachedPlan *
+SPI_plan_get_cached_plan(SPIPlanPtr plan)
+{
+ CachedPlanSource *plansource;
+ CachedPlan *cplan;
+ ErrorContextCallback spierrcontext;
+
+ Assert(plan->magic == _SPI_PLAN_MAGIC);
+
+ /* Can't support one-shot plans here */
+ if (plan->oneshot)
+ return NULL;
+
+ /* Must have exactly one CachedPlanSource */
+ if (list_length(plan->plancache_list) != 1)
+ return NULL;
+ plansource = (CachedPlanSource *) linitial(plan->plancache_list);
+
+ /* Setup error traceback support for ereport() */
+ spierrcontext.callback = _SPI_error_callback;
+ spierrcontext.arg = (void *) plansource->query_string;
+ spierrcontext.previous = error_context_stack;
+ error_context_stack = &spierrcontext;
+
+ /* Get the generic plan for the query */
+ cplan = GetCachedPlan(plansource, NULL, plan->saved);
+ Assert(cplan == plansource->gplan);
+
+ /* Pop the error context stack */
+ error_context_stack = spierrcontext.previous;
+
+ return cplan;
+}
+
+
/* =================== private functions =================== */
/*
extern bool SPI_plan_is_valid(SPIPlanPtr plan);
extern const char *SPI_result_code_string(int code);
+extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
+extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
+
extern HeapTuple SPI_copytuple(HeapTuple tuple);
extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
#include "access/tupconvert.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "executor/spi_priv.h"
+#include "executor/spi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
exec_prepare_plan(estate, expr, 0);
stmt->mod_stmt = false;
- foreach(l, expr->plan->plancache_list)
+ foreach(l, SPI_plan_get_plan_sources(expr->plan))
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l);
ListCell *l2;
*
* It is possible though unlikely for a simple expression to become non-simple
* (consider for example redefining a trivial view). We must handle that for
- * correctness; fortunately it's normally inexpensive to do GetCachedPlan on a
- * simple expression. We do not consider the other direction (non-simple
- * expression becoming simple) because we'll still give correct results if
- * that happens, and it's unlikely to be worth the cycles to check.
+ * correctness; fortunately it's normally inexpensive to call
+ * SPI_plan_get_cached_plan for a simple expression. We do not consider the
+ * other direction (non-simple expression becoming simple) because we'll still
+ * give correct results if that happens, and it's unlikely to be worth the
+ * cycles to check.
*
* Note: if pass-by-reference, the result is in the eval_econtext's
* temporary memory context. It will be freed when exec_eval_cleanup
{
ExprContext *econtext = estate->eval_econtext;
LocalTransactionId curlxid = MyProc->lxid;
- CachedPlanSource *plansource;
CachedPlan *cplan;
ParamListInfo paramLI;
PLpgSQL_expr *save_cur_expr;
/*
* Revalidate cached plan, so that we will notice if it became stale. (We
- * need to hold a refcount while using the plan, anyway.) Note that even
- * if replanning occurs, the length of plancache_list can't change, since
- * it is a property of the raw parsetree generated from the query text.
+ * need to hold a refcount while using the plan, anyway.)
*/
- Assert(list_length(expr->plan->plancache_list) == 1);
- plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
+ cplan = SPI_plan_get_cached_plan(expr->plan);
- /* Get the generic plan for the query */
- cplan = GetCachedPlan(plansource, NULL, true);
- Assert(cplan == plansource->gplan);
+ /*
+ * We can't get a failure here, because the number of CachedPlanSources in
+ * the SPI plan can't change from what exec_simple_check_plan saw; it's a
+ * property of the raw parsetree generated from the query text.
+ */
+ Assert(cplan != NULL);
if (cplan->generation != expr->expr_simple_generation)
{
static void
exec_simple_check_plan(PLpgSQL_expr *expr)
{
+ List *plansources;
CachedPlanSource *plansource;
Query *query;
CachedPlan *cplan;
/*
* We can only test queries that resulted in exactly one CachedPlanSource
*/
- if (list_length(expr->plan->plancache_list) != 1)
+ plansources = SPI_plan_get_plan_sources(expr->plan);
+ if (list_length(plansources) != 1)
return;
- plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
+ plansource = (CachedPlanSource *) linitial(plansources);
/*
* Do some checking on the analyzed-and-rewritten form of the query. These
*/
/* Get the generic plan for the query */
- cplan = GetCachedPlan(plansource, NULL, true);
- Assert(cplan == plansource->gplan);
+ cplan = SPI_plan_get_cached_plan(expr->plan);
+
+ /* Can't fail, because we checked for a single CachedPlanSource above */
+ Assert(cplan != NULL);
/* Share the remaining work with recheck code path */
exec_simple_recheck_plan(expr, cplan);
rollback;
drop function error2(p_name_table text);
drop function error1(text);
+-- Test for consistent reporting of error context
+create function fail() returns int language plpgsql as $$
+begin
+ return 1/0;
+end
+$$;
+select fail();
+ERROR: division by zero
+CONTEXT: SQL statement "SELECT 1/0"
+PL/pgSQL function fail() line 3 at RETURN
+select fail();
+ERROR: division by zero
+CONTEXT: SQL statement "SELECT 1/0"
+PL/pgSQL function fail() line 3 at RETURN
+drop function fail();
-- Test handling of string literals.
set standard_conforming_strings = off;
create or replace function strtest() returns text as $$
drop function error2(p_name_table text);
drop function error1(text);
+-- Test for consistent reporting of error context
+
+create function fail() returns int language plpgsql as $$
+begin
+ return 1/0;
+end
+$$;
+
+select fail();
+select fail();
+
+drop function fail();
+
-- Test handling of string literals.
set standard_conforming_strings = off;