aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop/postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r--src/backend/tcop/postgres.c226
1 files changed, 74 insertions, 152 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 072d50c3951..c7eac71e91e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -161,10 +161,6 @@ static bool ignore_till_sync = false;
*/
static CachedPlanSource *unnamed_stmt_psrc = NULL;
-/* workspace for building a new unnamed statement in */
-static MemoryContext unnamed_stmt_context = NULL;
-
-
/* assorted command-line switches */
static const char *userDoption = NULL; /* -D switch */
@@ -1116,14 +1112,14 @@ exec_parse_message(const char *query_string, /* string to execute */
Oid *paramTypes, /* parameter types */
int numParams) /* number of parameters */
{
+ MemoryContext unnamed_stmt_context = NULL;
MemoryContext oldcontext;
List *parsetree_list;
Node *raw_parse_tree;
const char *commandTag;
- List *querytree_list,
- *stmt_list;
+ List *querytree_list;
+ CachedPlanSource *psrc;
bool is_named;
- bool fully_planned;
bool save_log_statement_stats = log_statement_stats;
char msec_str[32];
@@ -1158,11 +1154,11 @@ exec_parse_message(const char *query_string, /* string to execute */
* named or not. For a named prepared statement, we do parsing in
* MessageContext and copy the finished trees into the prepared
* statement's plancache entry; then the reset of MessageContext releases
- * temporary space used by parsing and planning. For an unnamed prepared
+ * temporary space used by parsing and rewriting. For an unnamed prepared
* statement, we assume the statement isn't going to hang around long, so
* getting rid of temp space quickly is probably not worth the costs of
- * copying parse/plan trees. So in this case, we create the plancache
- * entry's context here, and do all the parsing work therein.
+ * copying parse trees. So in this case, we create the plancache entry's
+ * query_context here, and do all the parsing work therein.
*/
is_named = (stmt_name[0] != '\0');
if (is_named)
@@ -1174,9 +1170,9 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Unnamed prepared statement --- release any prior unnamed stmt */
drop_unnamed_stmt();
- /* Create context for parsing/planning */
+ /* Create context for parsing */
unnamed_stmt_context =
- AllocSetContextCreate(CacheMemoryContext,
+ AllocSetContextCreate(MessageContext,
"unnamed prepared statement",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
@@ -1230,7 +1226,13 @@ exec_parse_message(const char *query_string, /* string to execute */
errdetail_abort()));
/*
- * Set up a snapshot if parse analysis/planning will need one.
+ * Create the CachedPlanSource before we do parse analysis, since
+ * it needs to see the unmodified raw parse tree.
+ */
+ psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
+
+ /*
+ * Set up a snapshot if parse analysis will need one.
*/
if (analyze_requires_snapshot(raw_parse_tree))
{
@@ -1239,18 +1241,14 @@ exec_parse_message(const char *query_string, /* string to execute */
}
/*
- * OK to analyze, rewrite, and plan this query. Note that the
- * originally specified parameter set is not required to be complete,
- * so we have to use parse_analyze_varparams().
- *
- * XXX must use copyObject here since parse analysis scribbles on its
- * input, and we need the unmodified raw parse tree for possible
- * replanning later.
+ * Analyze and rewrite the query. Note that the originally specified
+ * parameter set is not required to be complete, so we have to use
+ * parse_analyze_varparams().
*/
if (log_parser_stats)
ResetUsage();
- query = parse_analyze_varparams(copyObject(raw_parse_tree),
+ query = parse_analyze_varparams(raw_parse_tree,
query_string,
&paramTypes,
&numParams);
@@ -1274,22 +1272,7 @@ exec_parse_message(const char *query_string, /* string to execute */
querytree_list = pg_rewrite_query(query);
- /*
- * If this is the unnamed statement and it has parameters, defer query
- * planning until Bind. Otherwise do it now.
- */
- if (!is_named && numParams > 0)
- {
- stmt_list = querytree_list;
- fully_planned = false;
- }
- else
- {
- stmt_list = pg_plan_queries(querytree_list, 0, NULL);
- fully_planned = true;
- }
-
- /* Done with the snapshot used for parsing/planning */
+ /* Done with the snapshot used for parsing */
if (snapshot_set)
PopActiveSnapshot();
}
@@ -1298,56 +1281,47 @@ exec_parse_message(const char *query_string, /* string to execute */
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
commandTag = NULL;
- stmt_list = NIL;
- fully_planned = true;
+ psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
+ querytree_list = NIL;
}
- /* If we got a cancel signal in analysis or planning, quit */
- CHECK_FOR_INTERRUPTS();
-
/*
- * Store the query as a prepared statement. See above comments.
+ * CachedPlanSource must be a direct child of MessageContext before we
+ * reparent unnamed_stmt_context under it, else we have a disconnected
+ * circular subgraph. Klugy, but less so than flipping contexts even
+ * more above.
*/
+ if (unnamed_stmt_context)
+ MemoryContextSetParent(psrc->context, MessageContext);
+
+ /* Finish filling in the CachedPlanSource */
+ CompleteCachedPlan(psrc,
+ querytree_list,
+ unnamed_stmt_context,
+ paramTypes,
+ numParams,
+ NULL,
+ NULL,
+ 0, /* default cursor options */
+ true); /* fixed result */
+
+ /* If we got a cancel signal during analysis, quit */
+ CHECK_FOR_INTERRUPTS();
+
if (is_named)
{
- StorePreparedStatement(stmt_name,
- raw_parse_tree,
- query_string,
- commandTag,
- paramTypes,
- numParams,
- 0, /* default cursor options */
- stmt_list,
- false);
+ /*
+ * Store the query as a prepared statement.
+ */
+ StorePreparedStatement(stmt_name, psrc, false);
}
else
{
/*
- * paramTypes and query_string need to be copied into
- * unnamed_stmt_context. The rest is there already
+ * We just save the CachedPlanSource into unnamed_stmt_psrc.
*/
- Oid *newParamTypes;
-
- if (numParams > 0)
- {
- newParamTypes = (Oid *) palloc(numParams * sizeof(Oid));
- memcpy(newParamTypes, paramTypes, numParams * sizeof(Oid));
- }
- else
- newParamTypes = NULL;
-
- unnamed_stmt_psrc = FastCreateCachedPlan(raw_parse_tree,
- pstrdup(query_string),
- commandTag,
- newParamTypes,
- numParams,
- 0, /* cursor options */
- stmt_list,
- fully_planned,
- true,
- unnamed_stmt_context);
- /* context now belongs to the plancache entry */
- unnamed_stmt_context = NULL;
+ SaveCachedPlan(psrc);
+ unnamed_stmt_psrc = psrc;
}
MemoryContextSwitchTo(oldcontext);
@@ -1412,7 +1386,6 @@ exec_bind_message(StringInfo input_message)
char *query_string;
char *saved_stmt_name;
ParamListInfo params;
- List *plan_list;
MemoryContext oldContext;
bool save_log_statement_stats = log_statement_stats;
bool snapshot_set = false;
@@ -1437,7 +1410,7 @@ exec_bind_message(StringInfo input_message)
}
else
{
- /* Unnamed statements are re-prepared for every bind */
+ /* special-case the unnamed statement */
psrc = unnamed_stmt_psrc;
if (!psrc)
ereport(ERROR,
@@ -1522,7 +1495,7 @@ exec_bind_message(StringInfo input_message)
/*
* Prepare to copy stuff into the portal's memory context. We do all this
* copying first, because it could possibly fail (out-of-memory) and we
- * don't want a failure to occur between RevalidateCachedPlan and
+ * don't want a failure to occur between GetCachedPlan and
* PortalDefineQuery; that would result in leaking our plancache refcount.
*/
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
@@ -1539,7 +1512,9 @@ exec_bind_message(StringInfo input_message)
/*
* Set a snapshot if we have parameters to fetch (since the input
* functions might need it) or the query isn't a utility command (and
- * hence could require redoing parse analysis and planning).
+ * hence could require redoing parse analysis and planning). We keep
+ * the snapshot active till we're done, so that plancache.c doesn't have
+ * to take new ones.
*/
if (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))
{
@@ -1675,10 +1650,8 @@ exec_bind_message(StringInfo input_message)
params->params[paramno].isnull = isNull;
/*
- * We mark the params as CONST. This has no effect if we already
- * did planning, but if we didn't, it licenses the planner to
- * substitute the parameters directly into the one-shot plan we
- * will generate below.
+ * We mark the params as CONST. This ensures that any custom
+ * plan makes full use of the parameter values.
*/
params->params[paramno].pflags = PARAM_FLAG_CONST;
params->params[paramno].ptype = ptype;
@@ -1703,63 +1676,24 @@ exec_bind_message(StringInfo input_message)
pq_getmsgend(input_message);
- if (psrc->fully_planned)
- {
- /*
- * Revalidate the cached plan; this may result in replanning. Any
- * cruft will be generated in MessageContext. The plan refcount will
- * be assigned to the Portal, so it will be released at portal
- * destruction.
- */
- cplan = RevalidateCachedPlan(psrc, false);
- plan_list = cplan->stmt_list;
- }
- else
- {
- List *query_list;
-
- /*
- * Revalidate the cached plan; this may result in redoing parse
- * analysis and rewriting (but not planning). Any cruft will be
- * generated in MessageContext. The plan refcount is assigned to
- * CurrentResourceOwner.
- */
- cplan = RevalidateCachedPlan(psrc, true);
-
- /*
- * We didn't plan the query before, so do it now. This allows the
- * planner to make use of the concrete parameter values we now have.
- * Because we use PARAM_FLAG_CONST, the plan is good only for this set
- * of param values, and so we generate the plan in the portal's own
- * memory context where it will be thrown away after use. As in
- * exec_parse_message, we make no attempt to recover planner temporary
- * memory until the end of the operation.
- *
- * XXX because the planner has a bad habit of scribbling on its input,
- * we have to make a copy of the parse trees. FIXME someday.
- */
- oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
- query_list = copyObject(cplan->stmt_list);
- plan_list = pg_plan_queries(query_list, 0, params);
- MemoryContextSwitchTo(oldContext);
-
- /* We no longer need the cached plan refcount ... */
- ReleaseCachedPlan(cplan, true);
- /* ... and we don't want the portal to depend on it, either */
- cplan = NULL;
- }
+ /*
+ * Obtain a plan from the CachedPlanSource. Any cruft from (re)planning
+ * will be generated in MessageContext. The plan refcount will be
+ * assigned to the Portal, so it will be released at portal destruction.
+ */
+ cplan = GetCachedPlan(psrc, params, false);
/*
* Now we can define the portal.
*
* DO NOT put any code that could possibly throw an error between the
- * above "RevalidateCachedPlan(psrc, false)" call and here.
+ * above GetCachedPlan call and here.
*/
PortalDefineQuery(portal,
saved_stmt_name,
query_string,
psrc->commandTag,
- plan_list,
+ cplan->stmt_list,
cplan);
/* Done with the snapshot used for parameter I/O and parsing/planning */
@@ -2304,8 +2238,7 @@ exec_describe_statement_message(const char *stmt_name)
/*
* If we are in aborted transaction state, we can't run
- * SendRowDescriptionMessage(), because that needs catalog accesses. (We
- * can't do RevalidateCachedPlan, either, but that's a lesser problem.)
+ * SendRowDescriptionMessage(), because that needs catalog accesses.
* Hence, refuse to Describe statements that return data. (We shouldn't
* just refuse all Describes, since that might break the ability of some
* clients to issue COMMIT or ROLLBACK commands, if they use code that
@@ -2342,18 +2275,12 @@ exec_describe_statement_message(const char *stmt_name)
*/
if (psrc->resultDesc)
{
- CachedPlan *cplan;
List *tlist;
- /* Make sure the plan is up to date */
- cplan = RevalidateCachedPlan(psrc, true);
-
- /* Get the primary statement and find out what it returns */
- tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
+ /* Get the plan's primary targetlist */
+ tlist = CachedPlanGetTargetList(psrc);
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
-
- ReleaseCachedPlan(cplan, true);
}
else
pq_putemptymessage('n'); /* NoData */
@@ -2536,19 +2463,14 @@ IsTransactionStmtList(List *parseTrees)
static void
drop_unnamed_stmt(void)
{
- /* Release any completed unnamed statement */
+ /* paranoia to avoid a dangling pointer in case of error */
if (unnamed_stmt_psrc)
- DropCachedPlan(unnamed_stmt_psrc);
- unnamed_stmt_psrc = NULL;
+ {
+ CachedPlanSource *psrc = unnamed_stmt_psrc;
- /*
- * If we failed while trying to build a prior unnamed statement, we may
- * have a memory context that wasn't assigned to a completed plancache
- * entry. If so, drop it to avoid a permanent memory leak.
- */
- if (unnamed_stmt_context)
- MemoryContextDelete(unnamed_stmt_context);
- unnamed_stmt_context = NULL;
+ unnamed_stmt_psrc = NULL;
+ DropCachedPlan(psrc);
+ }
}