• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision3cb8d0bf50e4ee23eb7c9c485d936cd3c4dce0a1 (tree)
Time2019-01-07 19:08:05
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 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.

Change Summary

Incremental Difference

--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -224,6 +224,14 @@ static unsigned int msgqno = 0;
224224 static char qnostr[32];
225225 static const char *current_hint_str = NULL;
226226
227+/*
228+ * However we usually take a hint stirng in post_parse_analyze_hook, we still
229+ * need to do so in planner_hook when client starts query execution from the
230+ * bind message on a prepared query. This variable prevent duplicate and
231+ * sometimes harmful hint string retrieval.
232+ */
233+static bool current_hint_retrieved = false;
234+
227235 /* common data for all hints. */
228236 struct Hint
229237 {
@@ -387,6 +395,11 @@ static void push_hint(HintState *hstate);
387395 static void pop_hint(void);
388396
389397 static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
398+static void pg_hint_plan_ProcessUtility(PlannedStmt *pstmt,
399+ const char *queryString,
400+ ProcessUtilityContext context,
401+ ParamListInfo params, QueryEnvironment *queryEnv,
402+ DestReceiver *dest, char *completionTag);
390403 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
391404 ParamListInfo boundParams);
392405 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
@@ -552,6 +565,7 @@ static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
552565 static planner_hook_type prev_planner = NULL;
553566 static join_search_hook_type prev_join_search = NULL;
554567 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
568+static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
555569
556570 /* Hold reference to currently active hint */
557571 static HintState *current_hint_state = NULL;
@@ -681,6 +695,8 @@ _PG_init(void)
681695 join_search_hook = pg_hint_plan_join_search;
682696 prev_set_rel_pathlist = set_rel_pathlist_hook;
683697 set_rel_pathlist_hook = pg_hint_plan_set_rel_pathlist;
698+ prev_ProcessUtility_hook = ProcessUtility_hook;
699+ ProcessUtility_hook = pg_hint_plan_ProcessUtility;
684700
685701 /* setup PL/pgSQL plugin hook */
686702 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -703,6 +719,7 @@ _PG_fini(void)
703719 planner_hook = prev_planner;
704720 join_search_hook = prev_join_search;
705721 set_rel_pathlist_hook = prev_set_rel_pathlist;
722+ ProcessUtility_hook = prev_ProcessUtility_hook;
706723
707724 /* uninstall PL/pgSQL plugin hook */
708725 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -1804,17 +1821,15 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
18041821 /*
18051822 * If debug_query_string is set, it is the top level statement. But in some
18061823 * cases we reach here with debug_query_string set NULL for example in the
1807- * case of DESCRIBE message handling. We may still see a candidate
1808- * top-level query in pstate in the case.
1824+ * case of DESCRIBE message handling or EXECUTE command. We may still see a
1825+ * candidate top-level query in pstate in the case.
18091826 */
1810- if (!p)
1811- {
1812- /* We don't see a query string, return NULL */
1813- if (!pstate->p_sourcetext)
1814- return NULL;
1815-
1827+ if (!p && pstate)
18161828 p = pstate->p_sourcetext;
1817- }
1829+
1830+ /* We don't see a query string, return NULL */
1831+ if (!p)
1832+ return NULL;
18181833
18191834 if (jumblequery != NULL)
18201835 *jumblequery = query;
@@ -1888,8 +1903,12 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
18881903 if (jumblequery)
18891904 *jumblequery = target_query;
18901905 }
1891- /* Return NULL if the pstate is not identical to the top-level query */
1892- else if (strcmp(pstate->p_sourcetext, p) != 0)
1906+ /*
1907+ * Return NULL if pstate is not of top-level query. We don't need this
1908+ * when jumble info is not requested or cannot do this when pstate is NULL.
1909+ */
1910+ else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1911+ strcmp(pstate->p_sourcetext, p) != 0)
18931912 p = NULL;
18941913
18951914 return p;
@@ -2758,24 +2777,25 @@ pop_hint(void)
27582777 }
27592778
27602779 /*
2761- * Retrieve and store a hint string from given query or from the hint table.
2762- * If we are using the hint table, the query string is needed to be normalized.
2763- * However, ParseState, which is not available in planner_hook, is required to
2764- * check if the query tree (Query) is surely corresponding to the target query.
2780+ * Retrieve and store hint string from given query or from the hint table.
27652781 */
27662782 static void
2767-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2783+get_current_hint_string(ParseState *pstate, Query *query)
27682784 {
27692785 const char *query_str;
27702786 MemoryContext oldcontext;
27712787
2772- if (prev_post_parse_analyze_hook)
2773- prev_post_parse_analyze_hook(pstate, query);
2774-
27752788 /* do nothing under hint table search */
27762789 if (hint_inhibit_level > 0)
27772790 return;
27782791
2792+ /* We alredy have one, don't parse it again. */
2793+ if (current_hint_retrieved)
2794+ return;
2795+
2796+ /* Don't parse the current query hereafter */
2797+ current_hint_retrieved = true;
2798+
27792799 if (!pg_hint_plan_enable_hint)
27802800 {
27812801 if (current_hint_str)
@@ -2816,8 +2836,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28162836 if (jumblequery)
28172837 {
28182838 /*
2819- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2820- * careful to PostgreSQL's version up.
2839+ * XXX: normalization code is copied from pg_stat_statements.c.
2840+ * Make sure to keep up-to-date with it.
28212841 */
28222842 jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
28232843 jstate.jumble_len = 0;
@@ -2894,8 +2914,8 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
28942914 {
28952915 /*
28962916 * get hints from the comment. However we may have the same query
2897- * string with the previous call, but just retrieving hints is expected
2898- * to be faster than checking for identicalness before retrieval.
2917+ * string with the previous call, but the extra comparison seems no
2918+ * use..
28992919 */
29002920 if (current_hint_str)
29012921 pfree((void *)current_hint_str);
@@ -2927,6 +2947,41 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
29272947 }
29282948
29292949 /*
2950+ * Retrieve hint string from the current query.
2951+ */
2952+static void
2953+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2954+{
2955+ if (prev_post_parse_analyze_hook)
2956+ prev_post_parse_analyze_hook(pstate, query);
2957+
2958+ /* always retrieve hint from the top-level query string */
2959+ if (plpgsql_recurse_level == 0)
2960+ current_hint_retrieved = false;
2961+
2962+ get_current_hint_string(pstate, query);
2963+}
2964+
2965+/*
2966+ * We need to reset current_hint_retrieved flag always when a command execution
2967+ * is finished. This is true even for a pure utility command that doesn't
2968+ * involve planning phase.
2969+ */
2970+static void
2971+pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
2972+ ProcessUtilityContext context,
2973+ ParamListInfo params, QueryEnvironment *queryEnv,
2974+ DestReceiver *dest, char *completionTag)
2975+{
2976+ if (prev_ProcessUtility_hook)
2977+ prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
2978+ dest, completionTag);
2979+
2980+ if (plpgsql_recurse_level == 0)
2981+ current_hint_retrieved = false;
2982+}
2983+
2984+/*
29302985 * Read and set up hint information
29312986 */
29322987 static PlannedStmt *
@@ -2972,6 +3027,14 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
29723027 MemoryContextSwitchTo(oldcontext);
29733028 }
29743029
3030+ /*
3031+ * Query execution in extended protocol can be started without the analyze
3032+ * phase. In the case retrieve hint string here.
3033+ */
3034+ if (!current_hint_str)
3035+ get_current_hint_string(NULL, parse);
3036+
3037+ /* No hint, go the normal way */
29753038 if (!current_hint_str)
29763039 goto standard_planner_proc;
29773040
@@ -3044,6 +3107,17 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
30443107 }
30453108 PG_END_TRY();
30463109
3110+
3111+ /*
3112+ * current_hint_str is useless after planning of the top-level query.
3113+ */
3114+ if (plpgsql_recurse_level < 1 && current_hint_str)
3115+ {
3116+ pfree((void *)current_hint_str);
3117+ current_hint_str = NULL;
3118+ current_hint_retrieved = false;
3119+ }
3120+
30473121 /* Print hint in debug mode. */
30483122 if (debug_level == 1)
30493123 HintStateDump(current_hint_state);
Show on old repository browser