• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: コミット

firtst release


コミットメタ情報

リビジョンc36270588d9a4d7be0fce752a9eb63fb0f35d5a8 (tree)
日時2019-01-07 19:07:10
作者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 3f6c983 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
@@ -198,6 +198,14 @@ static unsigned int msgqno = 0;
198198 static char qnostr[32];
199199 static const char *current_hint_str = NULL;
200200
201+/*
202+ * However we usually take a hint stirng in post_parse_analyze_hook, we still
203+ * need to do so in planner_hook when client starts query execution from the
204+ * bind message on a prepared query. This variable prevent duplicate and
205+ * sometimes harmful hint string retrieval.
206+ */
207+static bool current_hint_retrieved = false;
208+
201209 /* common data for all hints. */
202210 struct Hint
203211 {
@@ -347,6 +355,10 @@ static void push_hint(HintState *hstate);
347355 static void pop_hint(void);
348356
349357 static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
358+static void pg_hint_plan_ProcessUtility(Node *parsetree,
359+ const char *queryString,
360+ ProcessUtilityContext context, ParamListInfo params,
361+ DestReceiver *dest, char *completionTag);
350362 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
351363 ParamListInfo boundParams);
352364 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
@@ -482,6 +494,7 @@ static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
482494 static planner_hook_type prev_planner = NULL;
483495 static get_relation_info_hook_type prev_get_relation_info = NULL;
484496 static join_search_hook_type prev_join_search = NULL;
497+static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
485498
486499 /* Hold reference to currently active hint */
487500 static HintState *current_hint = NULL;
@@ -606,6 +619,8 @@ _PG_init(void)
606619 get_relation_info_hook = pg_hint_plan_get_relation_info;
607620 prev_join_search = join_search_hook;
608621 join_search_hook = pg_hint_plan_join_search;
622+ prev_ProcessUtility_hook = ProcessUtility_hook;
623+ ProcessUtility_hook = pg_hint_plan_ProcessUtility;
609624
610625 /* setup PL/pgSQL plugin hook */
611626 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -628,6 +643,7 @@ _PG_fini(void)
628643 planner_hook = prev_planner;
629644 get_relation_info_hook = prev_get_relation_info;
630645 join_search_hook = prev_join_search;
646+ ProcessUtility_hook = prev_ProcessUtility_hook;
631647
632648 /* uninstall PL/pgSQL plugin hook */
633649 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -1659,17 +1675,15 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16591675 /*
16601676 * If debug_query_string is set, it is the top level statement. But in some
16611677 * cases we reach here with debug_query_string set NULL for example in the
1662- * case of DESCRIBE message handling. We may still see a candidate
1663- * top-level query in pstate in the case.
1678+ * case of DESCRIBE message handling or EXECUTE command. We may still see a
1679+ * candidate top-level query in pstate in the case.
16641680 */
1665- if (!p)
1666- {
1667- /* We don't see a query string, return NULL */
1668- if (!pstate->p_sourcetext)
1669- return NULL;
1670-
1681+ if (!p && pstate)
16711682 p = pstate->p_sourcetext;
1672- }
1683+
1684+ /* We don't see a query string, return NULL */
1685+ if (!p)
1686+ return NULL;
16731687
16741688 if (jumblequery != NULL)
16751689 *jumblequery = query;
@@ -1753,9 +1767,12 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17531767 if (jumblequery)
17541768 *jumblequery = target_query;
17551769 }
1756-
1757- /* Return NULL if the pstate is not identical to the top-level query */
1758- else if (strcmp(pstate->p_sourcetext, p) != 0)
1770+ /*
1771+ * Return NULL if pstate is not of top-level query. We don't need this
1772+ * when jumble info is not requested or cannot do this when pstate is NULL.
1773+ */
1774+ else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1775+ strcmp(pstate->p_sourcetext, p) != 0)
17591776 p = NULL;
17601777
17611778 return p;
@@ -2437,24 +2454,25 @@ pop_hint(void)
24372454 }
24382455
24392456 /*
2440- * We need only jumbled query for the root query, acquired by
2441- * get_query_string(). We cannot check that in pg_hint_plan_planner since it
2442- * cannot see the truely corresponding query string to the given Query.
2443- * We check that here instead using ParseState.
2457+ * Retrieve and store hint string from given query or from the hint table.
24442458 */
24452459 static void
2446-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2460+get_current_hint_string(ParseState *pstate, Query *query)
24472461 {
24482462 const char *query_str;
24492463 MemoryContext oldcontext;
24502464
2451- if (prev_post_parse_analyze_hook)
2452- prev_post_parse_analyze_hook(pstate, query);
2453-
24542465 /* do nothing under hint table search */
24552466 if (hint_inhibit_level > 0)
24562467 return;
24572468
2469+ /* We alredy have one, don't parse it again. */
2470+ if (current_hint_retrieved)
2471+ return;
2472+
2473+ /* Don't parse the current query hereafter */
2474+ current_hint_retrieved = true;
2475+
24582476 if (!pg_hint_plan_enable_hint)
24592477 {
24602478 if (current_hint_str)
@@ -2495,8 +2513,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
24952513 if (jumblequery)
24962514 {
24972515 /*
2498- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2499- * careful to PostgreSQL's version up.
2516+ * XXX: normalization code is copied from pg_stat_statements.c.
2517+ * Make sure to keep up-to-date with it.
25002518 */
25012519 jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
25022520 jstate.jumble_len = 0;
@@ -2570,8 +2588,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
25702588 {
25712589 /*
25722590 * get hints from the comment. However we may have the same query
2573- * string with the previous call, but just retrieving hints is expected
2574- * to be faster than checking for identicalness before retrieval.
2591+ * string with the previous call, but the extra comparison seems no
2592+ * use..
25752593 */
25762594 if (current_hint_str)
25772595 pfree((void *)current_hint_str);
@@ -2601,6 +2619,40 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
26012619 }
26022620
26032621 /*
2622+ * Retrieve hint string from the current query.
2623+ */
2624+static void
2625+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2626+{
2627+ if (prev_post_parse_analyze_hook)
2628+ prev_post_parse_analyze_hook(pstate, query);
2629+
2630+ /* always retrieve hint from the top-level query string */
2631+ if (plpgsql_recurse_level == 0)
2632+ current_hint_retrieved = false;
2633+
2634+ get_current_hint_string(pstate, query);
2635+}
2636+
2637+/*
2638+ * We need to reset current_hint_retrieved flag always when a command execution
2639+ * is finished. This is true even for a pure utility command that doesn't
2640+ * involve planning phase.
2641+ */
2642+static void
2643+pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2644+ ProcessUtilityContext context, ParamListInfo params,
2645+ DestReceiver *dest, char *completionTag)
2646+{
2647+ if (prev_ProcessUtility_hook)
2648+ prev_ProcessUtility_hook(parsetree, queryString, context, params,
2649+ dest, completionTag);
2650+
2651+ if (plpgsql_recurse_level == 0)
2652+ current_hint_retrieved = false;
2653+}
2654+
2655+/*
26042656 * Read and set up hint information
26052657 */
26062658 static PlannedStmt *
@@ -2646,6 +2698,14 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
26462698 MemoryContextSwitchTo(oldcontext);
26472699 }
26482700
2701+ /*
2702+ * Query execution in extended protocol can be started without the analyze
2703+ * phase. In the case retrieve hint string here.
2704+ */
2705+ if (!current_hint_str)
2706+ get_current_hint_string(NULL, parse);
2707+
2708+ /* No hint, go the normal way */
26492709 if (!current_hint_str)
26502710 goto standard_planner_proc;
26512711
@@ -2714,6 +2774,17 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
27142774 }
27152775 PG_END_TRY();
27162776
2777+
2778+ /*
2779+ * current_hint_str is useless after planning of the top-level query.
2780+ */
2781+ if (plpgsql_recurse_level < 1 && current_hint_str)
2782+ {
2783+ pfree((void *)current_hint_str);
2784+ current_hint_str = NULL;
2785+ current_hint_retrieved = false;
2786+ }
2787+
27172788 /* Print hint in debug mode. */
27182789 if (debug_level == 1)
27192790 HintStateDump(current_hint);
旧リポジトリブラウザで表示