• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revisiondff1e4a328ea8954c619663c720a5014507d4d34 (tree)
Time2019-01-07 19:13:37
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 89149ce 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
@@ -222,6 +222,14 @@ static unsigned int msgqno = 0;
222222 static char qnostr[32];
223223 static const char *current_hint_str = NULL;
224224
225+/*
226+ * However we usually take a hint stirng in post_parse_analyze_hook, we still
227+ * need to do so in planner_hook when client starts query execution from the
228+ * bind message on a prepared query. This variable prevent duplicate and
229+ * sometimes harmful hint string retrieval.
230+ */
231+static bool current_hint_retrieved = false;
232+
225233 /* common data for all hints. */
226234 struct Hint
227235 {
@@ -382,6 +390,10 @@ static void push_hint(HintState *hstate);
382390 static void pop_hint(void);
383391
384392 static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
393+static void pg_hint_plan_ProcessUtility(Node *parsetree,
394+ const char *queryString,
395+ ProcessUtilityContext context, ParamListInfo params,
396+ DestReceiver *dest, char *completionTag);
385397 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
386398 ParamListInfo boundParams);
387399 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
@@ -545,6 +557,7 @@ static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
545557 static planner_hook_type prev_planner = NULL;
546558 static join_search_hook_type prev_join_search = NULL;
547559 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
560+static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
548561
549562 /* Hold reference to currently active hint */
550563 static HintState *current_hint_state = NULL;
@@ -674,6 +687,8 @@ _PG_init(void)
674687 join_search_hook = pg_hint_plan_join_search;
675688 prev_set_rel_pathlist = set_rel_pathlist_hook;
676689 set_rel_pathlist_hook = pg_hint_plan_set_rel_pathlist;
690+ prev_ProcessUtility_hook = ProcessUtility_hook;
691+ ProcessUtility_hook = pg_hint_plan_ProcessUtility;
677692
678693 /* setup PL/pgSQL plugin hook */
679694 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -696,6 +711,7 @@ _PG_fini(void)
696711 planner_hook = prev_planner;
697712 join_search_hook = prev_join_search;
698713 set_rel_pathlist_hook = prev_set_rel_pathlist;
714+ ProcessUtility_hook = prev_ProcessUtility_hook;
699715
700716 /* uninstall PL/pgSQL plugin hook */
701717 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -1796,17 +1812,15 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17961812 /*
17971813 * If debug_query_string is set, it is the top level statement. But in some
17981814 * cases we reach here with debug_query_string set NULL for example in the
1799- * case of DESCRIBE message handling. We may still see a candidate
1800- * 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.
18011817 */
1802- if (!p)
1803- {
1804- /* We don't see a query string, return NULL */
1805- if (!pstate->p_sourcetext)
1806- return NULL;
1807-
1818+ if (!p && pstate)
18081819 p = pstate->p_sourcetext;
1809- }
1820+
1821+ /* We don't see a query string, return NULL */
1822+ if (!p)
1823+ return NULL;
18101824
18111825 if (jumblequery != NULL)
18121826 *jumblequery = query;
@@ -1890,9 +1904,12 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
18901904 if (jumblequery)
18911905 *jumblequery = target_query;
18921906 }
1893-
1894- /* Return NULL if the pstate is not identical to the top-level query */
1895- else if (strcmp(pstate->p_sourcetext, p) != 0)
1907+ /*
1908+ * Return NULL if pstate is not of top-level query. We don't need this
1909+ * when jumble info is not requested or cannot do this when pstate is NULL.
1910+ */
1911+ else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1912+ strcmp(pstate->p_sourcetext, p) != 0)
18961913 p = NULL;
18971914
18981915 return p;
@@ -2755,24 +2772,25 @@ pop_hint(void)
27552772 }
27562773
27572774 /*
2758- * Retrieve and store a hint string from given query or from the hint table.
2759- * If we are using the hint table, the query string is needed to be normalized.
2760- * However, ParseState, which is not available in planner_hook, is required to
2761- * check if the query tree (Query) is surely corresponding to the target query.
2775+ * Retrieve and store hint string from given query or from the hint table.
27622776 */
27632777 static void
2764-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2778+get_current_hint_string(ParseState *pstate, Query *query)
27652779 {
27662780 const char *query_str;
27672781 MemoryContext oldcontext;
27682782
2769- if (prev_post_parse_analyze_hook)
2770- prev_post_parse_analyze_hook(pstate, query);
2771-
27722783 /* do nothing under hint table search */
27732784 if (hint_inhibit_level > 0)
27742785 return;
27752786
2787+ /* We alredy have one, don't parse it again. */
2788+ if (current_hint_retrieved)
2789+ return;
2790+
2791+ /* Don't parse the current query hereafter */
2792+ current_hint_retrieved = true;
2793+
27762794 if (!pg_hint_plan_enable_hint)
27772795 {
27782796 if (current_hint_str)
@@ -2813,8 +2831,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28132831 if (jumblequery)
28142832 {
28152833 /*
2816- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2817- * careful to PostgreSQL's version up.
2834+ * XXX: normalization code is copied from pg_stat_statements.c.
2835+ * Make sure to keep up-to-date with it.
28182836 */
28192837 jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
28202838 jstate.jumble_len = 0;
@@ -2890,8 +2908,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28902908 {
28912909 /*
28922910 * get hints from the comment. However we may have the same query
2893- * string with the previous call, but just retrieving hints is expected
2894- * to be faster than checking for identicalness before retrieval.
2911+ * string with the previous call, but the extra comparison seems no
2912+ * use..
28952913 */
28962914 if (current_hint_str)
28972915 pfree((void *)current_hint_str);
@@ -2914,7 +2932,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
29142932 ereport(pg_hint_plan_debug_message_level,
29152933 (errmsg("hints in comment=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
29162934 current_hint_str ? current_hint_str : "(none)",
2917- query_str, debug_query_string),
2935+ query_str ? query_str : "(none)",
2936+ debug_query_string ? debug_query_string : "(none)"),
29182937 errhidestmt(msgqno != qno),
29192938 errhidecontext(msgqno != qno)));
29202939 msgqno = qno;
@@ -2922,6 +2941,40 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
29222941 }
29232942
29242943 /*
2944+ * Retrieve hint string from the current query.
2945+ */
2946+static void
2947+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2948+{
2949+ if (prev_post_parse_analyze_hook)
2950+ prev_post_parse_analyze_hook(pstate, query);
2951+
2952+ /* always retrieve hint from the top-level query string */
2953+ if (plpgsql_recurse_level == 0)
2954+ current_hint_retrieved = false;
2955+
2956+ get_current_hint_string(pstate, query);
2957+}
2958+
2959+/*
2960+ * We need to reset current_hint_retrieved flag always when a command execution
2961+ * is finished. This is true even for a pure utility command that doesn't
2962+ * involve planning phase.
2963+ */
2964+static void
2965+pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2966+ ProcessUtilityContext context, ParamListInfo params,
2967+ DestReceiver *dest, char *completionTag)
2968+{
2969+ if (prev_ProcessUtility_hook)
2970+ prev_ProcessUtility_hook(parsetree, queryString, context, params,
2971+ dest, completionTag);
2972+
2973+ if (plpgsql_recurse_level == 0)
2974+ current_hint_retrieved = false;
2975+}
2976+
2977+/*
29252978 * Read and set up hint information
29262979 */
29272980 static PlannedStmt *
@@ -2967,6 +3020,14 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
29673020 MemoryContextSwitchTo(oldcontext);
29683021 }
29693022
3023+ /*
3024+ * Query execution in extended protocol can be started without the analyze
3025+ * phase. In the case retrieve hint string here.
3026+ */
3027+ if (!current_hint_str)
3028+ get_current_hint_string(NULL, parse);
3029+
3030+ /* No hint, go the normal way */
29703031 if (!current_hint_str)
29713032 goto standard_planner_proc;
29723033
@@ -3036,6 +3097,17 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
30363097 }
30373098 PG_END_TRY();
30383099
3100+
3101+ /*
3102+ * current_hint_str is useless after planning of the top-level query.
3103+ */
3104+ if (plpgsql_recurse_level < 1 && current_hint_str)
3105+ {
3106+ pfree((void *)current_hint_str);
3107+ current_hint_str = NULL;
3108+ current_hint_retrieved = false;
3109+ }
3110+
30393111 /* Print hint in debug mode. */
30403112 if (debug_level == 1)
30413113 HintStateDump(current_hint_state);
Show on old repository browser