• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: コミット

firtst release


コミットメタ情報

リビジョンc05bb31d1e15f2f04f6da842eee9bfef868ec466 (tree)
日時2017-05-18 19:52:13
作者Kyotaro Horiguchi <horiguchi.kyotaro@lab....>
コミッターKyotaro Horiguchi

ログメッセージ

Fix a crash bug on complex views when enable_hint_table is on

The Query that planner receives sometimes irrelevant to
debug_query_string. If enable_hint_table is on, pg_hint_plan_planner
normalizes debug_query_string using query-jumble information created
from the irrelevant Query the can lead to crash. To avoid this
situation, retrieve hints in post_parse_analyze_hook, where
corresponding pairs of a query string and a parsed Query.

変更サマリ

差分

--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -16,6 +16,7 @@
1616 #include "mb/pg_wchar.h"
1717 #include "miscadmin.h"
1818 #include "nodes/nodeFuncs.h"
19+#include "nodes/params.h"
1920 #include "optimizer/clauses.h"
2021 #include "optimizer/cost.h"
2122 #include "optimizer/geqo.h"
@@ -26,12 +27,14 @@
2627 #include "optimizer/planner.h"
2728 #include "optimizer/prep.h"
2829 #include "optimizer/restrictinfo.h"
30+#include "parser/analyze.h"
2931 #include "parser/scansup.h"
3032 #include "tcop/utility.h"
3133 #include "utils/builtins.h"
3234 #include "utils/lsyscache.h"
3335 #include "utils/memutils.h"
3436 #include "utils/rel.h"
37+#include "utils/snapmgr.h"
3538 #include "utils/syscache.h"
3639 #include "utils/resowner.h"
3740
@@ -87,10 +90,12 @@ PG_MODULE_MAGIC;
8790 #define HINT_ARRAY_DEFAULT_INITSIZE 8
8891
8992 #define hint_ereport(str, detail) \
90- ereport(pg_hint_plan_message_level, \
91- (errhidestmt(hidestmt), \
92- errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
93- errdetail detail))
93+ do { \
94+ ereport(pg_hint_plan_message_level, \
95+ (errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
96+ errdetail detail)); \
97+ msgqno = qno; \
98+ } while(0)
9499
95100 #define skip_space(str) \
96101 while (isspace(*str)) \
@@ -213,7 +218,9 @@ typedef enum HintStatus
213218 (hint)->base.state == HINT_STATE_USED)
214219
215220 static unsigned int qno = 0;
221+static unsigned int msgqno = 0;
216222 static char qnostr[32];
223+static const char *current_hint_str = NULL;
217224
218225 /* common data for all hints. */
219226 struct Hint
@@ -373,11 +380,7 @@ void _PG_fini(void);
373380 static void push_hint(HintState *hstate);
374381 static void pop_hint(void);
375382
376-static void pg_hint_plan_ProcessUtility(Node *parsetree,
377- const char *queryString,
378- ProcessUtilityContext context,
379- ParamListInfo params,
380- DestReceiver *dest, char *completionTag);
383+static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
381384 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
382385 ParamListInfo boundParams);
383386 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
@@ -496,9 +499,6 @@ static int pg_hint_plan_message_level = INFO;
496499 /* Default is off, to keep backward compatibility. */
497500 static bool pg_hint_plan_enable_hint_table = false;
498501
499-/* Internal static variables. */
500-static bool hidestmt = false; /* Allow or inhibit STATEMENT: output */
501-
502502 static int plpgsql_recurse_level = 0; /* PLpgSQL recursion level */
503503 static int hint_inhibit_level = 0; /* Inhibit hinting if this is above 0 */
504504 /* (This could not be above 1) */
@@ -540,7 +540,7 @@ static const struct config_enum_entry parse_debug_level_options[] = {
540540 };
541541
542542 /* Saved hook values in case of unload */
543-static ProcessUtility_hook_type prev_ProcessUtility = NULL;
543+static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
544544 static planner_hook_type prev_planner = NULL;
545545 static join_search_hook_type prev_join_search = NULL;
546546 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
@@ -671,8 +671,8 @@ _PG_init(void)
671671 NULL);
672672
673673 /* Install hooks. */
674- prev_ProcessUtility = ProcessUtility_hook;
675- ProcessUtility_hook = pg_hint_plan_ProcessUtility;
674+ prev_post_parse_analyze_hook = post_parse_analyze_hook;
675+ post_parse_analyze_hook = pg_hint_plan_post_parse_analyze;
676676 prev_planner = planner_hook;
677677 planner_hook = pg_hint_plan_planner;
678678 prev_join_search = join_search_hook;
@@ -697,7 +697,7 @@ _PG_fini(void)
697697 PLpgSQL_plugin **var_ptr;
698698
699699 /* Uninstall hooks. */
700- ProcessUtility_hook = prev_ProcessUtility;
700+ post_parse_analyze_hook = prev_post_parse_analyze_hook;
701701 planner_hook = prev_planner;
702702 join_search_hook = prev_join_search;
703703 set_rel_pathlist_hook = prev_set_rel_pathlist;
@@ -1266,8 +1266,9 @@ HintStateDump2(HintState *hstate)
12661266 appendStringInfoChar(&buf, '}');
12671267
12681268 ereport(pg_hint_plan_message_level,
1269- (errhidestmt(true),
1270- errmsg("%s", buf.data)));
1269+ (errmsg("%s", buf.data),
1270+ errhidestmt(true),
1271+ errhidecontext(true)));
12711272
12721273 pfree(buf.data);
12731274 }
@@ -1722,7 +1723,15 @@ get_hints_from_table(const char *client_query, const char *client_application)
17221723
17231724 PG_TRY();
17241725 {
1726+ bool snapshot_set = false;
1727+
17251728 hint_inhibit_level++;
1729+
1730+ if (!ActiveSnapshotSet())
1731+ {
1732+ PushActiveSnapshot(GetTransactionSnapshot());
1733+ snapshot_set = true;
1734+ }
17261735
17271736 SPI_connect();
17281737
@@ -1759,7 +1768,10 @@ get_hints_from_table(const char *client_query, const char *client_application)
17591768 }
17601769
17611770 SPI_finish();
1762-
1771+
1772+ if (snapshot_set)
1773+ PopActiveSnapshot();
1774+
17631775 hint_inhibit_level--;
17641776 }
17651777 PG_CATCH();
@@ -1773,30 +1785,87 @@ get_hints_from_table(const char *client_query, const char *client_application)
17731785 }
17741786
17751787 /*
1776- * Get client-supplied query string.
1788+ * Get client-supplied query string. Addtion to that the jumbled query is
1789+ * supplied if the caller requested. From the restriction of JumbleQuery, some
1790+ * kind of query needs special amendments. Reutrns NULL if the current hint
1791+ * string is still valid.
17771792 */
17781793 static const char *
1779-get_query_string(void)
1794+get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17801795 {
1781- const char *p;
1796+ const char *p = debug_query_string;
17821797
1783- if (plpgsql_recurse_level > 0)
1784- {
1785- /*
1786- * This is quite ugly but this is the only point I could find where
1787- * we can get the query string.
1788- */
1789- p = (char*)error_context_stack->arg;
1790- }
1791- else if (stmt_name)
1798+ if (jumblequery != NULL)
1799+ *jumblequery = query;
1800+
1801+ Assert(plpgsql_recurse_level == 0);
1802+
1803+ if (query->commandType == CMD_UTILITY)
17921804 {
1793- PreparedStatement *entry;
1805+ Query *target_query = query;
1806+
1807+ /* Use the target query if EXPLAIN */
1808+ if (IsA(query->utilityStmt, ExplainStmt))
1809+ {
1810+ ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt);
1811+ Assert(IsA(stmt->query, Query));
1812+ target_query = (Query *)stmt->query;
1813+
1814+ if (target_query->commandType == CMD_UTILITY &&
1815+ target_query->utilityStmt != NULL)
1816+ target_query = (Query *)target_query->utilityStmt;
1817+
1818+ if (jumblequery)
1819+ *jumblequery = target_query;
1820+ }
1821+
1822+ if (IsA(target_query, CreateTableAsStmt))
1823+ {
1824+ /*
1825+ * Use the the body query for CREATE AS. The Query for jumble also
1826+ * replaced with the corresponding one.
1827+ */
1828+ CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
1829+ PreparedStatement *entry;
1830+ Query *ent_query;
1831+
1832+ Assert(IsA(stmt->query, Query));
1833+ target_query = (Query *) stmt->query;
17941834
1795- entry = FetchPreparedStatement(stmt_name, true);
1796- p = entry->plansource->query_string;
1835+ if (target_query->commandType == CMD_UTILITY &&
1836+ IsA(target_query->utilityStmt, ExecuteStmt))
1837+ {
1838+ ExecuteStmt *estmt = (ExecuteStmt *) target_query->utilityStmt;
1839+ entry = FetchPreparedStatement(estmt->name, true);
1840+ p = entry->plansource->query_string;
1841+ ent_query = (Query *) linitial (entry->plansource->query_list);
1842+ Assert(IsA(ent_query, Query));
1843+ if (jumblequery)
1844+ *jumblequery = ent_query;
1845+ }
1846+ }
1847+ else
1848+ if (IsA(target_query, ExecuteStmt))
1849+ {
1850+ /*
1851+ * Use the prepared query for EXECUTE. The Query for jumble also
1852+ * replaced with the corresponding one.
1853+ */
1854+ ExecuteStmt *stmt = (ExecuteStmt *)target_query;
1855+ PreparedStatement *entry;
1856+ Query *ent_query;
1857+
1858+ entry = FetchPreparedStatement(stmt->name, true);
1859+ p = entry->plansource->query_string;
1860+ ent_query = (Query *) linitial (entry->plansource->query_list);
1861+ Assert(IsA(ent_query, Query));
1862+ if (jumblequery)
1863+ *jumblequery = ent_query;
1864+ }
17971865 }
1798- else
1799- p = debug_query_string;
1866+ /* Return NULL if the pstate is not identical to the top-level query */
1867+ else if (strcmp(pstate->p_sourcetext, p) != 0)
1868+ p = NULL;
18001869
18011870 return p;
18021871 }
@@ -2473,10 +2542,10 @@ set_config_option_noerror(const char *name, const char *value,
24732542
24742543 ereport(elevel,
24752544 (errcode(errdata->sqlerrcode),
2476- errhidestmt(hidestmt),
24772545 errmsg("%s", errdata->message),
24782546 errdata->detail ? errdetail("%s", errdata->detail) : 0,
24792547 errdata->hint ? errhint("%s", errdata->hint) : 0));
2548+ msgqno = qno;
24802549 FreeErrorData(errdata);
24812550 }
24822551 PG_END_TRY();
@@ -2625,132 +2694,6 @@ set_join_config_options(unsigned char enforce_mask, GucContext context)
26252694 }
26262695
26272696 /*
2628- * pg_hint_plan hook functions
2629- */
2630-
2631-static void
2632-pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2633- ProcessUtilityContext context,
2634- ParamListInfo params,
2635- DestReceiver *dest, char *completionTag)
2636-{
2637- Node *node;
2638-
2639- /*
2640- * Use standard planner if pg_hint_plan is disabled or current nesting
2641- * depth is nesting depth of SPI calls.
2642- */
2643- if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
2644- {
2645- if (debug_level > 1)
2646- ereport(pg_hint_plan_message_level,
2647- (errmsg ("pg_hint_plan: ProcessUtility:"
2648- " pg_hint_plan.enable_hint = off")));
2649- if (prev_ProcessUtility)
2650- (*prev_ProcessUtility) (parsetree, queryString,
2651- context, params,
2652- dest, completionTag);
2653- else
2654- standard_ProcessUtility(parsetree, queryString,
2655- context, params,
2656- dest, completionTag);
2657- return;
2658- }
2659-
2660- node = parsetree;
2661- if (IsA(node, ExplainStmt))
2662- {
2663- /*
2664- * Draw out parse tree of actual query from Query struct of EXPLAIN
2665- * statement.
2666- */
2667- ExplainStmt *stmt;
2668- Query *query;
2669-
2670- stmt = (ExplainStmt *) node;
2671-
2672- Assert(IsA(stmt->query, Query));
2673- query = (Query *) stmt->query;
2674-
2675- if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
2676- node = query->utilityStmt;
2677- }
2678-
2679- /*
2680- * If the query was a EXECUTE or CREATE TABLE AS EXECUTE, get query string
2681- * specified to preceding PREPARE command to use it as source of hints.
2682- */
2683- if (IsA(node, ExecuteStmt))
2684- {
2685- ExecuteStmt *stmt;
2686-
2687- stmt = (ExecuteStmt *) node;
2688- stmt_name = stmt->name;
2689- }
2690-
2691- /*
2692- * CREATE AS EXECUTE behavior has changed since 9.2, so we must handle it
2693- * specially here.
2694- */
2695- if (IsA(node, CreateTableAsStmt))
2696- {
2697- CreateTableAsStmt *stmt;
2698- Query *query;
2699-
2700- stmt = (CreateTableAsStmt *) node;
2701- Assert(IsA(stmt->query, Query));
2702- query = (Query *) stmt->query;
2703-
2704- if (query->commandType == CMD_UTILITY &&
2705- IsA(query->utilityStmt, ExecuteStmt))
2706- {
2707- ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
2708- stmt_name = estmt->name;
2709- }
2710- }
2711-
2712- if (stmt_name)
2713- {
2714- if (debug_level > 1)
2715- ereport(pg_hint_plan_message_level,
2716- (errmsg ("pg_hint_plan: ProcessUtility:"
2717- " stmt_name = \"%s\", statement=\"%s\"",
2718- stmt_name, queryString)));
2719-
2720- PG_TRY();
2721- {
2722- if (prev_ProcessUtility)
2723- (*prev_ProcessUtility) (parsetree, queryString,
2724- context, params,
2725- dest, completionTag);
2726- else
2727- standard_ProcessUtility(parsetree, queryString,
2728- context, params,
2729- dest, completionTag);
2730- }
2731- PG_CATCH();
2732- {
2733- stmt_name = NULL;
2734- PG_RE_THROW();
2735- }
2736- PG_END_TRY();
2737-
2738- stmt_name = NULL;
2739-
2740- return;
2741- }
2742-
2743- if (prev_ProcessUtility)
2744- (*prev_ProcessUtility) (parsetree, queryString,
2745- context, params,
2746- dest, completionTag);
2747- else
2748- standard_ProcessUtility(parsetree, queryString,
2749- context, params,
2750- dest, completionTag);
2751-}
2752-
2753-/*
27542697 * Push a hint into hint stack which is implemented with List struct. Head of
27552698 * list is top of stack.
27562699 */
@@ -2784,24 +2727,182 @@ pop_hint(void)
27842727 current_hint_state = (HintState *) lfirst(list_head(HintStateStack));
27852728 }
27862729
2730+/*
2731+ * Retrieve and store a hint string from given query or from the hint table.
2732+ * If we are using the hint table, the query string is needed to be normalized.
2733+ * However, ParseState, which is not available in planner_hook, is required to
2734+ * check if the query tree (Query) is surely corresponding to the target query.
2735+ */
2736+static void
2737+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2738+{
2739+ const char *query_str;
2740+ MemoryContext oldcontext;
2741+
2742+ if (prev_post_parse_analyze_hook)
2743+ prev_post_parse_analyze_hook(pstate, query);
2744+
2745+ /* do nothing under hint table search */
2746+ if (hint_inhibit_level > 0)
2747+ return;
2748+
2749+ if (!pg_hint_plan_enable_hint)
2750+ {
2751+ if (current_hint_str)
2752+ {
2753+ pfree((void *)current_hint_str);
2754+ current_hint_str = NULL;
2755+ }
2756+ return;
2757+ }
2758+
2759+ /* increment the query number */
2760+ qnostr[0] = 0;
2761+ if (debug_level > 1)
2762+ snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2763+ qno++;
2764+
2765+ /* search the hint table for a hint if requested */
2766+ if (pg_hint_plan_enable_hint_table)
2767+ {
2768+ int query_len;
2769+ pgssJumbleState jstate;
2770+ Query *jumblequery;
2771+ char *normalized_query = NULL;
2772+
2773+ query_str = get_query_string(pstate, query, &jumblequery);
2774+
2775+ /* If this query is not for hint, just return */
2776+ if (!query_str)
2777+ return;
2778+
2779+ /* clear the previous hint string */
2780+ if (current_hint_str)
2781+ {
2782+ pfree((void *)current_hint_str);
2783+ current_hint_str = NULL;
2784+ }
2785+
2786+ if (jumblequery)
2787+ {
2788+ /*
2789+ * XXX: normalizing code is copied from pg_stat_statements.c, so be
2790+ * careful to PostgreSQL's version up.
2791+ */
2792+ jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2793+ jstate.jumble_len = 0;
2794+ jstate.clocations_buf_size = 32;
2795+ jstate.clocations = (pgssLocationLen *)
2796+ palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2797+ jstate.clocations_count = 0;
2798+
2799+ JumbleQuery(&jstate, jumblequery);
2800+
2801+ /*
2802+ * Normalize the query string by replacing constants with '?'
2803+ */
2804+ /*
2805+ * Search hint string which is stored keyed by query string
2806+ * and application name. The query string is normalized to allow
2807+ * fuzzy matching.
2808+ *
2809+ * Adding 1 byte to query_len ensures that the returned string has
2810+ * a terminating NULL.
2811+ */
2812+ query_len = strlen(query_str) + 1;
2813+ normalized_query =
2814+ generate_normalized_query(&jstate, query_str,
2815+ &query_len,
2816+ GetDatabaseEncoding());
2817+
2818+ /*
2819+ * find a hint for the normalized query. the result should be in
2820+ * TopMemoryContext
2821+ */
2822+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2823+ current_hint_str =
2824+ get_hints_from_table(normalized_query, application_name);
2825+ MemoryContextSwitchTo(oldcontext);
2826+
2827+ if (debug_level > 1)
2828+ {
2829+ if (current_hint_str)
2830+ ereport(pg_hint_plan_message_level,
2831+ (errmsg("pg_hint_plan[qno=0x%x]: "
2832+ "post_parse_analyze_hook: "
2833+ "hints from table: \"%s\": "
2834+ "normalized_query=\"%s\", "
2835+ "application name =\"%s\"",
2836+ qno, current_hint_str,
2837+ normalized_query, application_name),
2838+ errhidestmt(msgqno != qno),
2839+ errhidecontext(msgqno != qno)));
2840+ else
2841+ ereport(pg_hint_plan_message_level,
2842+ (errmsg("pg_hint_plan[qno=0x%x]: "
2843+ "no match found in table: "
2844+ "application name = \"%s\", "
2845+ "normalized_query=\"%s\"",
2846+ qno, application_name,
2847+ normalized_query),
2848+ errhidestmt(msgqno != qno),
2849+ errhidecontext(msgqno != qno)));
2850+
2851+ msgqno = qno;
2852+ }
2853+ }
2854+
2855+ /* retrun if we have hint here*/
2856+ if (current_hint_str)
2857+ return;
2858+ }
2859+ else
2860+ query_str = get_query_string(pstate, query, NULL);
2861+
2862+ if (query_str)
2863+ {
2864+ /*
2865+ * get hints from the comment. However we may have the same query
2866+ * string with the previous call, but just retrieving hints is expected
2867+ * to be faster than checking for identicalness before retrieval.
2868+ */
2869+ if (current_hint_str)
2870+ pfree((void *)current_hint_str);
2871+
2872+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2873+ current_hint_str = get_hints_from_comment(query_str);
2874+ MemoryContextSwitchTo(oldcontext);
2875+ }
2876+
2877+ if (debug_level > 1)
2878+ {
2879+ if (debug_level == 1 &&
2880+ (stmt_name || strcmp(query_str, debug_query_string)))
2881+ ereport(pg_hint_plan_message_level,
2882+ (errmsg("hints in comment=\"%s\"",
2883+ current_hint_str ? current_hint_str : "(none)"),
2884+ errhidestmt(msgqno != qno),
2885+ errhidecontext(msgqno != qno)));
2886+ else
2887+ ereport(pg_hint_plan_message_level,
2888+ (errmsg("hints in comment=\"%s\", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2889+ current_hint_str ? current_hint_str : "(none)",
2890+ stmt_name, query_str, debug_query_string),
2891+ errhidestmt(msgqno != qno),
2892+ errhidecontext(msgqno != qno)));
2893+ msgqno = qno;
2894+ }
2895+}
2896+
2897+/*
2898+ * Read and set up hint information
2899+ */
27872900 static PlannedStmt *
27882901 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
27892902 {
2790- const char *hints = NULL;
2791- const char *query;
2792- char *norm_query;
2793- pgssJumbleState jstate;
2794- int query_len;
27952903 int save_nestlevel;
27962904 PlannedStmt *result;
27972905 HintState *hstate;
2798- char msgstr[1024];
2799-
2800- qnostr[0] = 0;
2801- strcpy(msgstr, "");
2802- if (debug_level > 1)
2803- snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2804- hidestmt = false;
28052906
28062907 /*
28072908 * Use standard planner if pg_hint_plan is disabled or current nesting
@@ -2811,103 +2912,44 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
28112912 if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
28122913 {
28132914 if (debug_level > 1)
2814- elog(pg_hint_plan_message_level,
2815- "pg_hint_plan%s: planner: enable_hint=%d,"
2816- " hint_inhibit_level=%d",
2817- qnostr, pg_hint_plan_enable_hint, hint_inhibit_level);
2818- hidestmt = true;
2915+ ereport(pg_hint_plan_message_level,
2916+ (errmsg ("pg_hint_plan%s: planner: enable_hint=%d,"
2917+ " hint_inhibit_level=%d",
2918+ qnostr, pg_hint_plan_enable_hint,
2919+ hint_inhibit_level),
2920+ errhidestmt(msgqno != qno)));
2921+ msgqno = qno;
28192922
28202923 goto standard_planner_proc;
28212924 }
28222925
2823- /* Create hint struct from client-supplied query string. */
2824- query = get_query_string();
2825-
28262926 /*
2827- * Create hintstate from hint specified for the query, if any.
2828- *
2829- * First we lookup hint in pg_hint.hints table by normalized query string,
2830- * unless pg_hint_plan.enable_hint_table is OFF.
2831- * This parameter provides option to avoid overhead of table lookup during
2832- * planning.
2833- *
2834- * If no hint was found, then we try to get hint from special query comment.
2927+ * Support for nested plpgsql functions. This is quite ugly but this is the
2928+ * only point I could find where I can get the query string.
28352929 */
2836- if (pg_hint_plan_enable_hint_table)
2837- {
2838- /*
2839- * Search hint information which is stored for the query and the
2840- * application. Query string is normalized before using in condition
2841- * in order to allow fuzzy matching.
2842- *
2843- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2844- * careful when supporting PostgreSQL's version up.
2845- */
2846- jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2847- jstate.jumble_len = 0;
2848- jstate.clocations_buf_size = 32;
2849- jstate.clocations = (pgssLocationLen *)
2850- palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2851- jstate.clocations_count = 0;
2852- JumbleQuery(&jstate, parse);
2853- /*
2854- * generate_normalized_query() copies exact given query_len bytes, so we
2855- * add 1 byte for null-termination here. As comments on
2856- * generate_normalized_query says, generate_normalized_query doesn't
2857- * take care of null-terminate, but additional 1 byte ensures that '\0'
2858- * byte in the source buffer to be copied into norm_query.
2859- */
2860- query_len = strlen(query) + 1;
2861- norm_query = generate_normalized_query(&jstate,
2862- query,
2863- &query_len,
2864- GetDatabaseEncoding());
2865- hints = get_hints_from_table(norm_query, application_name);
2866- if (debug_level > 1)
2867- {
2868- if (hints)
2869- snprintf(msgstr, 1024, "hints from table: \"%s\":"
2870- " normalzed_query=\"%s\", application name =\"%s\"",
2871- hints, norm_query, application_name);
2872- else
2873- {
2874- ereport(pg_hint_plan_message_level,
2875- (errhidestmt(hidestmt),
2876- errmsg("pg_hint_plan%s:"
2877- " no match found in table:"
2878- " application name = \"%s\","
2879- " normalzed_query=\"%s\"",
2880- qnostr, application_name, norm_query)));
2881- hidestmt = true;
2882- }
2883- }
2884- }
2885- if (hints == NULL)
2930+ if (plpgsql_recurse_level > 0)
28862931 {
2887- hints = get_hints_from_comment(query);
2932+ MemoryContext oldcontext;
28882933
2889- if (debug_level > 1)
2890- {
2891- snprintf(msgstr, 1024, "hints in comment=\"%s\"",
2892- hints ? hints : "(none)");
2893- if (debug_level > 2 ||
2894- stmt_name || strcmp(query, debug_query_string))
2895- snprintf(msgstr + strlen(msgstr), 1024- strlen(msgstr),
2896- ", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2897- stmt_name, query, debug_query_string);
2898- }
2934+ if (current_hint_str)
2935+ pfree((void *)current_hint_str);
2936+
2937+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2938+ current_hint_str =
2939+ get_hints_from_comment((char *)error_context_stack->arg);
2940+ MemoryContextSwitchTo(oldcontext);
28992941 }
29002942
2901- hstate = create_hintstate(parse, hints);
2943+ if (!current_hint_str)
2944+ goto standard_planner_proc;
29022945
2903- /*
2904- * Use standard planner if the statement has not valid hint. Other hook
2905- * functions try to change plan with current_hint_state if any, so set it
2906- * to NULL.
2907- */
2946+ /* parse the hint into hint state struct */
2947+ hstate = create_hintstate(parse, pstrdup(current_hint_str));
2948+
2949+ /* run standard planner if the statement has not valid hint */
29082950 if (!hstate)
29092951 goto standard_planner_proc;
2910-
2952+
29112953 /*
29122954 * Push new hint struct to the hint stack to disable previous hint context.
29132955 */
@@ -2939,10 +2981,9 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
29392981 if (debug_level > 1)
29402982 {
29412983 ereport(pg_hint_plan_message_level,
2942- (errhidestmt(hidestmt),
2943- errmsg("pg_hint_plan%s: planner: %s",
2944- qnostr, msgstr)));
2945- hidestmt = true;
2984+ (errhidestmt(msgqno != qno),
2985+ errmsg("pg_hint_plan%s: planner", qnostr)));
2986+ msgqno = qno;
29462987 }
29472988
29482989 /*
@@ -2987,10 +3028,10 @@ standard_planner_proc:
29873028 if (debug_level > 1)
29883029 {
29893030 ereport(pg_hint_plan_message_level,
2990- (errhidestmt(hidestmt),
2991- errmsg("pg_hint_plan%s: planner: no valid hint (%s)",
2992- qnostr, msgstr)));
2993- hidestmt = true;
3031+ (errhidestmt(msgqno != qno),
3032+ errmsg("pg_hint_plan%s: planner: no valid hint",
3033+ qnostr)));
3034+ msgqno = qno;
29943035 }
29953036 current_hint_state = NULL;
29963037 if (prev_planner)
旧リポジトリブラウザで表示