diff options
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r-- | src/backend/tcop/postgres.c | 226 |
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, ¶mTypes, &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); + } } |