• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision69736277118038029051a2173e1effc400c8dca5 (tree)
Time2019-01-07 19:07:39
AuthorKyotaro Horiguchi <horiguchi.kyotaro@lab....>
CommiterKyotaro Horiguchi

Log Message

Support prepared statements on extended protocol

However pg_hint_plan doesn't fully consider the extended protocol,
commit 64ab47b 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.

Change Summary

Incremental Difference

--- 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");
@@ -1660,17 +1676,15 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16601676 /*
16611677 * If debug_query_string is set, it is the top level statement. But in some
16621678 * cases we reach here with debug_query_string set NULL for example in the
1663- * case of DESCRIBE message handling. We may still see a candidate
1664- * top-level query in pstate in the case.
1679+ * case of DESCRIBE message handling or EXECUTE command. We may still see a
1680+ * candidate top-level query in pstate in the case.
16651681 */
1666- if (!p)
1667- {
1668- /* We don't see a query string, return NULL */
1669- if (!pstate->p_sourcetext)
1670- return NULL;
1671-
1682+ if (!p && pstate)
16721683 p = pstate->p_sourcetext;
1673- }
1684+
1685+ /* We don't see a query string, return NULL */
1686+ if (!p)
1687+ return NULL;
16741688
16751689 if (jumblequery != NULL)
16761690 *jumblequery = query;
@@ -1754,9 +1768,12 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17541768 if (jumblequery)
17551769 *jumblequery = target_query;
17561770 }
1757-
1758- /* Return NULL if the pstate is not identical to the top-level query */
1759- else if (strcmp(pstate->p_sourcetext, p) != 0)
1771+ /*
1772+ * Return NULL if pstate is not of top-level query. We don't need this
1773+ * when jumble info is not requested or cannot do this when pstate is NULL.
1774+ */
1775+ else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1776+ strcmp(pstate->p_sourcetext, p) != 0)
17601777 p = NULL;
17611778
17621779 return p;
@@ -2438,24 +2455,25 @@ pop_hint(void)
24382455 }
24392456
24402457 /*
2441- * Retrieve and store a hint string from given query or from the hint table.
2442- * If we are using the hint table, the query string is needed to be normalized.
2443- * However, ParseState, which is not available in planner_hook, is required to
2444- * check if the query tree (Query) is surely corresponding to the target query.
2458+ * Retrieve and store hint string from given query or from the hint table.
24452459 */
24462460 static void
2447-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2461+get_current_hint_string(ParseState *pstate, Query *query)
24482462 {
24492463 const char *query_str;
24502464 MemoryContext oldcontext;
24512465
2452- if (prev_post_parse_analyze_hook)
2453- prev_post_parse_analyze_hook(pstate, query);
2454-
24552466 /* do nothing under hint table search */
24562467 if (hint_inhibit_level > 0)
24572468 return;
24582469
2470+ /* We alredy have one, don't parse it again. */
2471+ if (current_hint_retrieved)
2472+ return;
2473+
2474+ /* Don't parse the current query hereafter */
2475+ current_hint_retrieved = true;
2476+
24592477 if (!pg_hint_plan_enable_hint)
24602478 {
24612479 if (current_hint_str)
@@ -2496,8 +2514,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
24962514 if (jumblequery)
24972515 {
24982516 /*
2499- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2500- * careful to PostgreSQL's version up.
2517+ * XXX: normalization code is copied from pg_stat_statements.c.
2518+ * Make sure to keep up-to-date with it.
25012519 */
25022520 jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
25032521 jstate.jumble_len = 0;
@@ -2573,8 +2591,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
25732591 {
25742592 /*
25752593 * get hints from the comment. However we may have the same query
2576- * string with the previous call, but just retrieving hints is expected
2577- * to be faster than checking for identicalness before retrieval.
2594+ * string with the previous call, but the extra comparison seems no
2595+ * use..
25782596 */
25792597 if (current_hint_str)
25802598 pfree((void *)current_hint_str);
@@ -2606,6 +2624,40 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
26062624 }
26072625
26082626 /*
2627+ * Retrieve hint string from the current query.
2628+ */
2629+static void
2630+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2631+{
2632+ if (prev_post_parse_analyze_hook)
2633+ prev_post_parse_analyze_hook(pstate, query);
2634+
2635+ /* always retrieve hint from the top-level query string */
2636+ if (plpgsql_recurse_level == 0)
2637+ current_hint_retrieved = false;
2638+
2639+ get_current_hint_string(pstate, query);
2640+}
2641+
2642+/*
2643+ * We need to reset current_hint_retrieved flag always when a command execution
2644+ * is finished. This is true even for a pure utility command that doesn't
2645+ * involve planning phase.
2646+ */
2647+static void
2648+pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2649+ ProcessUtilityContext context, ParamListInfo params,
2650+ DestReceiver *dest, char *completionTag)
2651+{
2652+ if (prev_ProcessUtility_hook)
2653+ prev_ProcessUtility_hook(parsetree, queryString, context, params,
2654+ dest, completionTag);
2655+
2656+ if (plpgsql_recurse_level == 0)
2657+ current_hint_retrieved = false;
2658+}
2659+
2660+/*
26092661 * Read and set up hint information
26102662 */
26112663 static PlannedStmt *
@@ -2651,6 +2703,14 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
26512703 MemoryContextSwitchTo(oldcontext);
26522704 }
26532705
2706+ /*
2707+ * Query execution in extended protocol can be started without the analyze
2708+ * phase. In the case retrieve hint string here.
2709+ */
2710+ if (!current_hint_str)
2711+ get_current_hint_string(NULL, parse);
2712+
2713+ /* No hint, go the normal way */
26542714 if (!current_hint_str)
26552715 goto standard_planner_proc;
26562716
@@ -2719,6 +2779,17 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
27192779 }
27202780 PG_END_TRY();
27212781
2782+
2783+ /*
2784+ * current_hint_str is useless after planning of the top-level query.
2785+ */
2786+ if (plpgsql_recurse_level < 1 && current_hint_str)
2787+ {
2788+ pfree((void *)current_hint_str);
2789+ current_hint_str = NULL;
2790+ current_hint_retrieved = false;
2791+ }
2792+
27222793 /* Print hint in debug mode. */
27232794 if (debug_level == 1)
27242795 HintStateDump(current_hint);
Show on old repository browser