diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2013-01-30 20:02:23 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2013-01-30 20:02:23 -0500 |
commit | 0900ac2d0dba3168ba574e5b0b61170237b4fdea (patch) | |
tree | 5004798e96f95f465cbfb45936be00332e466d9a /src/backend/executor | |
parent | 670a6c7a228aea1f31fb96f6bfa69969962e133e (diff) | |
download | postgresql-0900ac2d0dba3168ba574e5b0b61170237b4fdea.tar.gz postgresql-0900ac2d0dba3168ba574e5b0b61170237b4fdea.zip |
Fix plpgsql's reporting of plan-time errors in possibly-simple expressions.
exec_simple_check_plan and exec_eval_simple_expr attempted to call
GetCachedPlan directly. This meant that if an error was thrown during
planning, the resulting context traceback would not include the line
normally contributed by _SPI_error_callback. This is already inconsistent,
but just to be really odd, a re-execution of the very same expression
*would* show the additional context line, because we'd already have cached
the plan and marked the expression as non-simple.
The problem is easy to demonstrate in 9.2 and HEAD because planning of a
cached plan doesn't occur at all until GetCachedPlan is done. In earlier
versions, it could only be an issue if initial planning had succeeded, then
a replan was forced (already somewhat improbable for a simple expression),
and the replan attempt failed. Since the issue is mainly cosmetic in older
branches anyway, it doesn't seem worth the risk of trying to fix it there.
It is worth fixing in 9.2 since the instability of the context printout can
affect the results of GET STACKED DIAGNOSTICS, as per a recent discussion
on pgsql-novice.
To fix, introduce a SPI function that wraps GetCachedPlan while installing
the correct callback function. Use this instead of calling GetCachedPlan
directly from plpgsql.
Also introduce a wrapper function for extracting a SPI plan's
CachedPlanSource list. This lets us stop including spi_priv.h in
pl_exec.c, which was never a very good idea from a modularity standpoint.
In passing, fix a similar inconsistency that could occur in SPI_cursor_open,
which was also calling GetCachedPlan without setting up a context callback.
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/spi.c | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 3da2d263023..de8d59a8cdc 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1132,6 +1132,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, Snapshot snapshot; MemoryContext oldcontext; Portal portal; + ErrorContextCallback spierrcontext; /* * Check that the plan is something the Portal code will special-case as @@ -1182,6 +1183,15 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, 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 * plancache refcount. @@ -1191,6 +1201,9 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, cplan = GetCachedPlan(plansource, paramLI, false); stmt_list = cplan->stmt_list; + /* Pop the error context stack */ + error_context_stack = spierrcontext.previous; + if (!plan->saved) { /* @@ -1552,6 +1565,65 @@ SPI_result_code_string(int code) 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 =================== */ /* |