• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: コミット

firtst release


コミットメタ情報

リビジョンd5ec24302cfa4139f6af702e555118a8f8f5a1c2 (tree)
日時2019-01-07 19:08:30
作者Kyotaro Horiguchi <horiguchi.kyotaro@lab....>
コミッターKyotaro Horiguchi

ログメッセージ

Support prepared statements on extended protocol

However pg_hint_plan doesn't fully consider the extended protocol,
commit c05bb31 accidentially broke the case where an analyzed prepared
statement is executed on extended protocol. This patch fixes that only
for the hints-in-comment case. Hint-table still doesn't work in the
case since query-normalization needs Query, which is not available in
planner_hook.

変更サマリ

差分

--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -225,6 +225,14 @@ static unsigned int msgqno = 0;
225225 static char qnostr[32];
226226 static const char *current_hint_str = NULL;
227227
228+/*
229+ * However we usually take a hint stirng in post_parse_analyze_hook, we still
230+ * need to do so in planner_hook when client starts query execution from the
231+ * bind message on a prepared query. This variable prevent duplicate and
232+ * sometimes harmful hint string retrieval.
233+ */
234+static bool current_hint_retrieved = false;
235+
228236 /* common data for all hints. */
229237 struct Hint
230238 {
@@ -388,6 +396,11 @@ static void push_hint(HintState *hstate);
388396 static void pop_hint(void);
389397
390398 static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
399+static void pg_hint_plan_ProcessUtility(PlannedStmt *pstmt,
400+ const char *queryString,
401+ ProcessUtilityContext context,
402+ ParamListInfo params, QueryEnvironment *queryEnv,
403+ DestReceiver *dest, char *completionTag);
391404 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
392405 ParamListInfo boundParams);
393406 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
@@ -543,6 +556,7 @@ static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
543556 static planner_hook_type prev_planner = NULL;
544557 static join_search_hook_type prev_join_search = NULL;
545558 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
559+static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
546560
547561 /* Hold reference to currently active hint */
548562 static HintState *current_hint_state = NULL;
@@ -672,6 +686,8 @@ _PG_init(void)
672686 join_search_hook = pg_hint_plan_join_search;
673687 prev_set_rel_pathlist = set_rel_pathlist_hook;
674688 set_rel_pathlist_hook = pg_hint_plan_set_rel_pathlist;
689+ prev_ProcessUtility_hook = ProcessUtility_hook;
690+ ProcessUtility_hook = pg_hint_plan_ProcessUtility;
675691
676692 /* setup PL/pgSQL plugin hook */
677693 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -694,6 +710,7 @@ _PG_fini(void)
694710 planner_hook = prev_planner;
695711 join_search_hook = prev_join_search;
696712 set_rel_pathlist_hook = prev_set_rel_pathlist;
713+ ProcessUtility_hook = prev_ProcessUtility_hook;
697714
698715 /* uninstall PL/pgSQL plugin hook */
699716 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -1795,17 +1812,15 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17951812 /*
17961813 * If debug_query_string is set, it is the top level statement. But in some
17971814 * cases we reach here with debug_query_string set NULL for example in the
1798- * case of DESCRIBE message handling. We may still see a candidate
1799- * top-level query in pstate in the case.
1815+ * case of DESCRIBE message handling or EXECUTE command. We may still see a
1816+ * candidate top-level query in pstate in the case.
18001817 */
1801- if (!p)
1802- {
1803- /* We don't see a query string, return NULL */
1804- if (!pstate->p_sourcetext)
1805- return NULL;
1806-
1818+ if (!p && pstate)
18071819 p = pstate->p_sourcetext;
1808- }
1820+
1821+ /* We don't see a query string, return NULL */
1822+ if (!p)
1823+ return NULL;
18091824
18101825 if (jumblequery != NULL)
18111826 *jumblequery = query;
@@ -1879,8 +1894,12 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
18791894 if (jumblequery)
18801895 *jumblequery = target_query;
18811896 }
1882- /* Return NULL if the pstate is not identical to the top-level query */
1883- else if (strcmp(pstate->p_sourcetext, p) != 0)
1897+ /*
1898+ * Return NULL if pstate is not of top-level query. We don't need this
1899+ * when jumble info is not requested or cannot do this when pstate is NULL.
1900+ */
1901+ else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1902+ strcmp(pstate->p_sourcetext, p) != 0)
18841903 p = NULL;
18851904
18861905 return p;
@@ -2749,24 +2768,25 @@ pop_hint(void)
27492768 }
27502769
27512770 /*
2752- * Retrieve and store a hint string from given query or from the hint table.
2753- * If we are using the hint table, the query string is needed to be normalized.
2754- * However, ParseState, which is not available in planner_hook, is required to
2755- * check if the query tree (Query) is surely corresponding to the target query.
2771+ * Retrieve and store hint string from given query or from the hint table.
27562772 */
27572773 static void
2758-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2774+get_current_hint_string(ParseState *pstate, Query *query)
27592775 {
27602776 const char *query_str;
27612777 MemoryContext oldcontext;
27622778
2763- if (prev_post_parse_analyze_hook)
2764- prev_post_parse_analyze_hook(pstate, query);
2765-
27662779 /* do nothing under hint table search */
27672780 if (hint_inhibit_level > 0)
27682781 return;
27692782
2783+ /* We alredy have one, don't parse it again. */
2784+ if (current_hint_retrieved)
2785+ return;
2786+
2787+ /* Don't parse the current query hereafter */
2788+ current_hint_retrieved = true;
2789+
27702790 if (!pg_hint_plan_enable_hint)
27712791 {
27722792 if (current_hint_str)
@@ -2807,8 +2827,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28072827 if (jumblequery)
28082828 {
28092829 /*
2810- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2811- * careful to PostgreSQL's version up.
2830+ * XXX: normalization code is copied from pg_stat_statements.c.
2831+ * Make sure to keep up-to-date with it.
28122832 */
28132833 jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
28142834 jstate.jumble_len = 0;
@@ -2885,8 +2905,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28852905 {
28862906 /*
28872907 * get hints from the comment. However we may have the same query
2888- * string with the previous call, but just retrieving hints is expected
2889- * to be faster than checking for identicalness before retrieval.
2908+ * string with the previous call, but the extra comparison seems no
2909+ * use..
28902910 */
28912911 if (current_hint_str)
28922912 pfree((void *)current_hint_str);
@@ -2918,6 +2938,41 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
29182938 }
29192939
29202940 /*
2941+ * Retrieve hint string from the current query.
2942+ */
2943+static void
2944+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2945+{
2946+ if (prev_post_parse_analyze_hook)
2947+ prev_post_parse_analyze_hook(pstate, query);
2948+
2949+ /* always retrieve hint from the top-level query string */
2950+ if (plpgsql_recurse_level == 0)
2951+ current_hint_retrieved = false;
2952+
2953+ get_current_hint_string(pstate, query);
2954+}
2955+
2956+/*
2957+ * We need to reset current_hint_retrieved flag always when a command execution
2958+ * is finished. This is true even for a pure utility command that doesn't
2959+ * involve planning phase.
2960+ */
2961+static void
2962+pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
2963+ ProcessUtilityContext context,
2964+ ParamListInfo params, QueryEnvironment *queryEnv,
2965+ DestReceiver *dest, char *completionTag)
2966+{
2967+ if (prev_ProcessUtility_hook)
2968+ prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
2969+ dest, completionTag);
2970+
2971+ if (plpgsql_recurse_level == 0)
2972+ current_hint_retrieved = false;
2973+}
2974+
2975+/*
29212976 * Read and set up hint information
29222977 */
29232978 static PlannedStmt *
@@ -2963,6 +3018,14 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
29633018 MemoryContextSwitchTo(oldcontext);
29643019 }
29653020
3021+ /*
3022+ * Query execution in extended protocol can be started without the analyze
3023+ * phase. In the case retrieve hint string here.
3024+ */
3025+ if (!current_hint_str)
3026+ get_current_hint_string(NULL, parse);
3027+
3028+ /* No hint, go the normal way */
29663029 if (!current_hint_str)
29673030 goto standard_planner_proc;
29683031
@@ -3035,6 +3098,17 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
30353098 }
30363099 PG_END_TRY();
30373100
3101+
3102+ /*
3103+ * current_hint_str is useless after planning of the top-level query.
3104+ */
3105+ if (plpgsql_recurse_level < 1 && current_hint_str)
3106+ {
3107+ pfree((void *)current_hint_str);
3108+ current_hint_str = NULL;
3109+ current_hint_retrieved = false;
3110+ }
3111+
30383112 /* Print hint in debug mode. */
30393113 if (debug_level == 1)
30403114 HintStateDump(current_hint_state);
旧リポジトリブラウザで表示