• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision48b7b15ef663232881995ce7fdba5ff31a7065e6 (tree)
Time2017-05-18 19:52:34
AuthorKyotaro Horiguchi <horiguchi.kyotaro@lab....>
CommiterKyotaro Horiguchi

Log Message

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.

Change Summary

Incremental Difference

--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -15,6 +15,7 @@
1515 #include "mb/pg_wchar.h"
1616 #include "miscadmin.h"
1717 #include "nodes/nodeFuncs.h"
18+#include "nodes/params.h"
1819 #include "optimizer/clauses.h"
1920 #include "optimizer/cost.h"
2021 #include "optimizer/geqo.h"
@@ -25,12 +26,14 @@
2526 #include "optimizer/planner.h"
2627 #include "optimizer/prep.h"
2728 #include "optimizer/restrictinfo.h"
29+#include "parser/analyze.h"
2830 #include "parser/scansup.h"
2931 #include "tcop/utility.h"
3032 #include "utils/builtins.h"
3133 #include "utils/lsyscache.h"
3234 #include "utils/memutils.h"
3335 #include "utils/rel.h"
36+#include "utils/snapmgr.h"
3437 #include "utils/syscache.h"
3538 #include "utils/resowner.h"
3639
@@ -94,10 +97,12 @@ PG_MODULE_MAGIC;
9497 #define HINT_ARRAY_DEFAULT_INITSIZE 8
9598
9699 #define hint_ereport(str, detail) \
97- ereport(pg_hint_plan_message_level, \
98- (errhidestmt(hidestmt), \
99- errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
100- errdetail detail))
100+ do { \
101+ ereport(pg_hint_plan_message_level, \
102+ (errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
103+ errdetail detail)); \
104+ msgqno = qno; \
105+ } while(0)
101106
102107 #define skip_space(str) \
103108 while (isspace(*str)) \
@@ -199,7 +204,9 @@ typedef enum HintStatus
199204 (hint)->base.state == HINT_STATE_USED)
200205
201206 static unsigned int qno = 0;
207+static unsigned int msgqno = 0;
202208 static char qnostr[32];
209+static const char *current_hint_str = NULL;
203210
204211 /* common data for all hints. */
205212 struct Hint
@@ -348,11 +355,7 @@ void _PG_fini(void);
348355 static void push_hint(HintState *hstate);
349356 static void pop_hint(void);
350357
351-static void pg_hint_plan_ProcessUtility(Node *parsetree,
352- const char *queryString,
353- ProcessUtilityContext context,
354- ParamListInfo params,
355- DestReceiver *dest, char *completionTag);
358+static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
356359 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
357360 ParamListInfo boundParams);
358361 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
@@ -443,9 +446,6 @@ static int pg_hint_plan_message_level = INFO;
443446 /* Default is off, to keep backward compatibility. */
444447 static bool pg_hint_plan_enable_hint_table = false;
445448
446-/* Internal static variables. */
447-static bool hidestmt = false; /* Allow or inhibit STATEMENT: output */
448-
449449 static int plpgsql_recurse_level = 0; /* PLpgSQL recursion level */
450450 static int hint_inhibit_level = 0; /* Inhibit hinting if this is above 0 */
451451 /* (This could not be above 1) */
@@ -486,7 +486,7 @@ static const struct config_enum_entry parse_debug_level_options[] = {
486486 };
487487
488488 /* Saved hook values in case of unload */
489-static ProcessUtility_hook_type prev_ProcessUtility = NULL;
489+static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
490490 static planner_hook_type prev_planner = NULL;
491491 static get_relation_info_hook_type prev_get_relation_info = NULL;
492492 static join_search_hook_type prev_join_search = NULL;
@@ -612,8 +612,8 @@ _PG_init(void)
612612 NULL);
613613
614614 /* Install hooks. */
615- prev_ProcessUtility = ProcessUtility_hook;
616- ProcessUtility_hook = pg_hint_plan_ProcessUtility;
615+ prev_post_parse_analyze_hook = post_parse_analyze_hook;
616+ post_parse_analyze_hook = pg_hint_plan_post_parse_analyze;
617617 prev_planner = planner_hook;
618618 planner_hook = pg_hint_plan_planner;
619619 prev_get_relation_info = get_relation_info_hook;
@@ -638,7 +638,7 @@ _PG_fini(void)
638638 PLpgSQL_plugin **var_ptr;
639639
640640 /* Uninstall hooks. */
641- ProcessUtility_hook = prev_ProcessUtility;
641+ post_parse_analyze_hook = prev_post_parse_analyze_hook;
642642 planner_hook = prev_planner;
643643 get_relation_info_hook = prev_get_relation_info;
644644 join_search_hook = prev_join_search;
@@ -1145,8 +1145,8 @@ HintStateDump2(HintState *hstate)
11451145 appendStringInfoChar(&buf, '}');
11461146
11471147 ereport(pg_hint_plan_message_level,
1148- (errhidestmt(true),
1149- errmsg("%s", buf.data)));
1148+ (errmsg("%s", buf.data),
1149+ errhidestmt(true)));
11501150
11511151 pfree(buf.data);
11521152 }
@@ -1595,7 +1595,15 @@ get_hints_from_table(const char *client_query, const char *client_application)
15951595
15961596 PG_TRY();
15971597 {
1598+ bool snapshot_set = false;
1599+
15981600 hint_inhibit_level++;
1601+
1602+ if (!ActiveSnapshotSet())
1603+ {
1604+ PushActiveSnapshot(GetTransactionSnapshot());
1605+ snapshot_set = true;
1606+ }
15991607
16001608 SPI_connect();
16011609
@@ -1632,7 +1640,10 @@ get_hints_from_table(const char *client_query, const char *client_application)
16321640 }
16331641
16341642 SPI_finish();
1635-
1643+
1644+ if (snapshot_set)
1645+ PopActiveSnapshot();
1646+
16361647 hint_inhibit_level--;
16371648 }
16381649 PG_CATCH();
@@ -1646,30 +1657,87 @@ get_hints_from_table(const char *client_query, const char *client_application)
16461657 }
16471658
16481659 /*
1649- * Get client-supplied query string.
1660+ * Get client-supplied query string. Addtion to that the jumbled query is
1661+ * supplied if the caller requested. From the restriction of JumbleQuery, some
1662+ * kind of query needs special amendments. Reutrns NULL if the current hint
1663+ * string is still valid.
16501664 */
16511665 static const char *
1652-get_query_string(void)
1666+get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16531667 {
1654- const char *p;
1668+ const char *p = debug_query_string;
16551669
1656- if (plpgsql_recurse_level > 0)
1657- {
1658- /*
1659- * This is quite ugly but this is the only point I could find where
1660- * we can get the query string.
1661- */
1662- p = (char*)error_context_stack->arg;
1663- }
1664- else if (stmt_name)
1670+ if (jumblequery != NULL)
1671+ *jumblequery = query;
1672+
1673+ Assert(plpgsql_recurse_level == 0);
1674+
1675+ if (query->commandType == CMD_UTILITY)
16651676 {
1666- PreparedStatement *entry;
1677+ Query *target_query = query;
1678+
1679+ /* Use the target query if EXPLAIN */
1680+ if (IsA(query->utilityStmt, ExplainStmt))
1681+ {
1682+ ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt);
1683+ Assert(IsA(stmt->query, Query));
1684+ target_query = (Query *)stmt->query;
1685+
1686+ if (target_query->commandType == CMD_UTILITY &&
1687+ target_query->utilityStmt != NULL)
1688+ target_query = (Query *)target_query->utilityStmt;
1689+
1690+ if (jumblequery)
1691+ *jumblequery = target_query;
1692+ }
1693+
1694+ if (IsA(target_query, CreateTableAsStmt))
1695+ {
1696+ /*
1697+ * Use the the body query for CREATE AS. The Query for jumble also
1698+ * replaced with the corresponding one.
1699+ */
1700+ CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
1701+ PreparedStatement *entry;
1702+ Query *ent_query;
1703+
1704+ Assert(IsA(stmt->query, Query));
1705+ target_query = (Query *) stmt->query;
16671706
1668- entry = FetchPreparedStatement(stmt_name, true);
1669- p = entry->plansource->query_string;
1707+ if (target_query->commandType == CMD_UTILITY &&
1708+ IsA(target_query->utilityStmt, ExecuteStmt))
1709+ {
1710+ ExecuteStmt *estmt = (ExecuteStmt *) target_query->utilityStmt;
1711+ entry = FetchPreparedStatement(estmt->name, true);
1712+ p = entry->plansource->query_string;
1713+ ent_query = (Query *) linitial (entry->plansource->query_list);
1714+ Assert(IsA(ent_query, Query));
1715+ if (jumblequery)
1716+ *jumblequery = ent_query;
1717+ }
1718+ }
1719+ else
1720+ if (IsA(target_query, ExecuteStmt))
1721+ {
1722+ /*
1723+ * Use the prepared query for EXECUTE. The Query for jumble also
1724+ * replaced with the corresponding one.
1725+ */
1726+ ExecuteStmt *stmt = (ExecuteStmt *)target_query;
1727+ PreparedStatement *entry;
1728+ Query *ent_query;
1729+
1730+ entry = FetchPreparedStatement(stmt->name, true);
1731+ p = entry->plansource->query_string;
1732+ ent_query = (Query *) linitial (entry->plansource->query_list);
1733+ Assert(IsA(ent_query, Query));
1734+ if (jumblequery)
1735+ *jumblequery = ent_query;
1736+ }
16701737 }
1671- else
1672- p = debug_query_string;
1738+ /* Return NULL if the pstate is not identical to the top-level query */
1739+ else if (strcmp(pstate->p_sourcetext, p) != 0)
1740+ p = NULL;
16731741
16741742 return p;
16751743 }
@@ -2235,10 +2303,10 @@ set_config_option_wrapper(const char *name, const char *value,
22352303
22362304 ereport(elevel,
22372305 (errcode(errdata->sqlerrcode),
2238- errhidestmt(hidestmt),
22392306 errmsg("%s", errdata->message),
22402307 errdata->detail ? errdetail("%s", errdata->detail) : 0,
22412308 errdata->hint ? errhint("%s", errdata->hint) : 0));
2309+ msgqno = qno;
22422310 FreeErrorData(errdata);
22432311 }
22442312 PG_END_TRY();
@@ -2316,132 +2384,6 @@ set_join_config_options(unsigned char enforce_mask, GucContext context)
23162384 }
23172385
23182386 /*
2319- * pg_hint_plan hook functions
2320- */
2321-
2322-static void
2323-pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2324- ProcessUtilityContext context,
2325- ParamListInfo params,
2326- DestReceiver *dest, char *completionTag)
2327-{
2328- Node *node;
2329-
2330- /*
2331- * Use standard planner if pg_hint_plan is disabled or current nesting
2332- * depth is nesting depth of SPI calls.
2333- */
2334- if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
2335- {
2336- if (debug_level > 1)
2337- ereport(pg_hint_plan_message_level,
2338- (errmsg ("pg_hint_plan: ProcessUtility:"
2339- " pg_hint_plan.enable_hint = off")));
2340- if (prev_ProcessUtility)
2341- (*prev_ProcessUtility) (parsetree, queryString,
2342- context, params,
2343- dest, completionTag);
2344- else
2345- standard_ProcessUtility(parsetree, queryString,
2346- context, params,
2347- dest, completionTag);
2348- return;
2349- }
2350-
2351- node = parsetree;
2352- if (IsA(node, ExplainStmt))
2353- {
2354- /*
2355- * Draw out parse tree of actual query from Query struct of EXPLAIN
2356- * statement.
2357- */
2358- ExplainStmt *stmt;
2359- Query *query;
2360-
2361- stmt = (ExplainStmt *) node;
2362-
2363- Assert(IsA(stmt->query, Query));
2364- query = (Query *) stmt->query;
2365-
2366- if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
2367- node = query->utilityStmt;
2368- }
2369-
2370- /*
2371- * If the query was a EXECUTE or CREATE TABLE AS EXECUTE, get query string
2372- * specified to preceding PREPARE command to use it as source of hints.
2373- */
2374- if (IsA(node, ExecuteStmt))
2375- {
2376- ExecuteStmt *stmt;
2377-
2378- stmt = (ExecuteStmt *) node;
2379- stmt_name = stmt->name;
2380- }
2381-
2382- /*
2383- * CREATE AS EXECUTE behavior has changed since 9.2, so we must handle it
2384- * specially here.
2385- */
2386- if (IsA(node, CreateTableAsStmt))
2387- {
2388- CreateTableAsStmt *stmt;
2389- Query *query;
2390-
2391- stmt = (CreateTableAsStmt *) node;
2392- Assert(IsA(stmt->query, Query));
2393- query = (Query *) stmt->query;
2394-
2395- if (query->commandType == CMD_UTILITY &&
2396- IsA(query->utilityStmt, ExecuteStmt))
2397- {
2398- ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
2399- stmt_name = estmt->name;
2400- }
2401- }
2402-
2403- if (stmt_name)
2404- {
2405- if (debug_level > 1)
2406- ereport(pg_hint_plan_message_level,
2407- (errmsg ("pg_hint_plan: ProcessUtility:"
2408- " stmt_name = \"%s\", statement=\"%s\"",
2409- stmt_name, queryString)));
2410-
2411- PG_TRY();
2412- {
2413- if (prev_ProcessUtility)
2414- (*prev_ProcessUtility) (parsetree, queryString,
2415- context, params,
2416- dest, completionTag);
2417- else
2418- standard_ProcessUtility(parsetree, queryString,
2419- context, params,
2420- dest, completionTag);
2421- }
2422- PG_CATCH();
2423- {
2424- stmt_name = NULL;
2425- PG_RE_THROW();
2426- }
2427- PG_END_TRY();
2428-
2429- stmt_name = NULL;
2430-
2431- return;
2432- }
2433-
2434- if (prev_ProcessUtility)
2435- (*prev_ProcessUtility) (parsetree, queryString,
2436- context, params,
2437- dest, completionTag);
2438- else
2439- standard_ProcessUtility(parsetree, queryString,
2440- context, params,
2441- dest, completionTag);
2442-}
2443-
2444-/*
24452387 * Push a hint into hint stack which is implemented with List struct. Head of
24462388 * list is top of stack.
24472389 */
@@ -2475,24 +2417,178 @@ pop_hint(void)
24752417 current_hint = (HintState *) lfirst(list_head(HintStateStack));
24762418 }
24772419
2420+/*
2421+ * Retrieve and store a hint string from given query or from the hint table.
2422+ * If we are using the hint table, the query string is needed to be normalized.
2423+ * However, ParseState, which is not available in planner_hook, is required to
2424+ * check if the query tree (Query) is surely corresponding to the target query.
2425+ */
2426+static void
2427+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2428+{
2429+ const char *query_str;
2430+ MemoryContext oldcontext;
2431+
2432+ if (prev_post_parse_analyze_hook)
2433+ prev_post_parse_analyze_hook(pstate, query);
2434+
2435+ /* do nothing under hint table search */
2436+ if (hint_inhibit_level > 0)
2437+ return;
2438+
2439+ if (!pg_hint_plan_enable_hint)
2440+ {
2441+ if (current_hint_str)
2442+ {
2443+ pfree((void *)current_hint_str);
2444+ current_hint_str = NULL;
2445+ }
2446+ return;
2447+ }
2448+
2449+ /* increment the query number */
2450+ qnostr[0] = 0;
2451+ if (debug_level > 1)
2452+ snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2453+ qno++;
2454+
2455+ /* search the hint table for a hint if requested */
2456+ if (pg_hint_plan_enable_hint_table)
2457+ {
2458+ int query_len;
2459+ pgssJumbleState jstate;
2460+ Query *jumblequery;
2461+ char *normalized_query = NULL;
2462+
2463+ query_str = get_query_string(pstate, query, &jumblequery);
2464+
2465+ /* If this query is not for hint, just return */
2466+ if (!query_str)
2467+ return;
2468+
2469+ /* clear the previous hint string */
2470+ if (current_hint_str)
2471+ {
2472+ pfree((void *)current_hint_str);
2473+ current_hint_str = NULL;
2474+ }
2475+
2476+ if (jumblequery)
2477+ {
2478+ /*
2479+ * XXX: normalizing code is copied from pg_stat_statements.c, so be
2480+ * careful to PostgreSQL's version up.
2481+ */
2482+ jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2483+ jstate.jumble_len = 0;
2484+ jstate.clocations_buf_size = 32;
2485+ jstate.clocations = (pgssLocationLen *)
2486+ palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2487+ jstate.clocations_count = 0;
2488+
2489+ JumbleQuery(&jstate, jumblequery);
2490+
2491+ /*
2492+ * Normalize the query string by replacing constants with '?'
2493+ */
2494+ /*
2495+ * Search hint string which is stored keyed by query string
2496+ * and application name. The query string is normalized to allow
2497+ * fuzzy matching.
2498+ *
2499+ * Adding 1 byte to query_len ensures that the returned string has
2500+ * a terminating NULL.
2501+ */
2502+ query_len = strlen(query_str) + 1;
2503+ normalized_query =
2504+ generate_normalized_query(&jstate, query_str,
2505+ &query_len,
2506+ GetDatabaseEncoding());
2507+
2508+ /*
2509+ * find a hint for the normalized query. the result should be in
2510+ * TopMemoryContext
2511+ */
2512+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2513+ current_hint_str =
2514+ get_hints_from_table(normalized_query, application_name);
2515+ MemoryContextSwitchTo(oldcontext);
2516+
2517+ if (debug_level > 1)
2518+ {
2519+ if (current_hint_str)
2520+ ereport(pg_hint_plan_message_level,
2521+ (errmsg("pg_hint_plan[qno=0x%x]: "
2522+ "post_parse_analyze_hook: "
2523+ "hints from table: \"%s\": "
2524+ "normalized_query=\"%s\", "
2525+ "application name =\"%s\"",
2526+ qno, current_hint_str,
2527+ normalized_query, application_name),
2528+ errhidestmt(msgqno != qno)));
2529+ else
2530+ ereport(pg_hint_plan_message_level,
2531+ (errmsg("pg_hint_plan[qno=0x%x]: "
2532+ "no match found in table: "
2533+ "application name = \"%s\", "
2534+ "normalized_query=\"%s\"",
2535+ qno, application_name,
2536+ normalized_query),
2537+ errhidestmt(msgqno != qno)));
2538+
2539+ msgqno = qno;
2540+ }
2541+ }
2542+
2543+ /* retrun if we have hint here*/
2544+ if (current_hint_str)
2545+ return;
2546+ }
2547+ else
2548+ query_str = get_query_string(pstate, query, NULL);
2549+
2550+ if (query_str)
2551+ {
2552+ /*
2553+ * get hints from the comment. However we may have the same query
2554+ * string with the previous call, but just retrieving hints is expected
2555+ * to be faster than checking for identicalness before retrieval.
2556+ */
2557+ if (current_hint_str)
2558+ pfree((void *)current_hint_str);
2559+
2560+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2561+ current_hint_str = get_hints_from_comment(query_str);
2562+ MemoryContextSwitchTo(oldcontext);
2563+ }
2564+
2565+ if (debug_level > 1)
2566+ {
2567+ if (debug_level == 1 &&
2568+ (stmt_name || strcmp(query_str, debug_query_string)))
2569+ ereport(pg_hint_plan_message_level,
2570+ (errmsg("hints in comment=\"%s\"",
2571+ current_hint_str ? current_hint_str : "(none)"),
2572+ errhidestmt(msgqno != qno)));
2573+ else
2574+ ereport(pg_hint_plan_message_level,
2575+ (errmsg("hints in comment=\"%s\", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2576+ current_hint_str ? current_hint_str : "(none)",
2577+ stmt_name, query_str, debug_query_string),
2578+ errhidestmt(msgqno != qno)));
2579+ msgqno = qno;
2580+ }
2581+}
2582+
2583+/*
2584+ * Read and set up hint information
2585+ */
24782586 static PlannedStmt *
24792587 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
24802588 {
2481- const char *hints = NULL;
2482- const char *query;
2483- char *norm_query;
2484- pgssJumbleState jstate;
2485- int query_len;
24862589 int save_nestlevel;
24872590 PlannedStmt *result;
24882591 HintState *hstate;
2489- char msgstr[1024];
2490-
2491- qnostr[0] = 0;
2492- strcpy(msgstr, "");
2493- if (debug_level > 1)
2494- snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2495- hidestmt = false;
24962592
24972593 /*
24982594 * Use standard planner if pg_hint_plan is disabled or current nesting
@@ -2502,103 +2598,44 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
25022598 if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
25032599 {
25042600 if (debug_level > 1)
2505- elog(pg_hint_plan_message_level,
2506- "pg_hint_plan%s: planner: enable_hint=%d,"
2507- " hint_inhibit_level=%d",
2508- qnostr, pg_hint_plan_enable_hint, hint_inhibit_level);
2509- hidestmt = true;
2601+ ereport(pg_hint_plan_message_level,
2602+ (errmsg ("pg_hint_plan%s: planner: enable_hint=%d,"
2603+ " hint_inhibit_level=%d",
2604+ qnostr, pg_hint_plan_enable_hint,
2605+ hint_inhibit_level),
2606+ errhidestmt(msgqno != qno)));
2607+ msgqno = qno;
25102608
25112609 goto standard_planner_proc;
25122610 }
25132611
2514- /* Create hint struct from client-supplied query string. */
2515- query = get_query_string();
2516-
25172612 /*
2518- * Create hintstate from hint specified for the query, if any.
2519- *
2520- * First we lookup hint in pg_hint.hints table by normalized query string,
2521- * unless pg_hint_plan.enable_hint_table is OFF.
2522- * This parameter provides option to avoid overhead of table lookup during
2523- * planning.
2524- *
2525- * If no hint was found, then we try to get hint from special query comment.
2613+ * Support for nested plpgsql functions. This is quite ugly but this is the
2614+ * only point I could find where I can get the query string.
25262615 */
2527- if (pg_hint_plan_enable_hint_table)
2528- {
2529- /*
2530- * Search hint information which is stored for the query and the
2531- * application. Query string is normalized before using in condition
2532- * in order to allow fuzzy matching.
2533- *
2534- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2535- * careful when supporting PostgreSQL's version up.
2536- */
2537- jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2538- jstate.jumble_len = 0;
2539- jstate.clocations_buf_size = 32;
2540- jstate.clocations = (pgssLocationLen *)
2541- palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2542- jstate.clocations_count = 0;
2543- JumbleQuery(&jstate, parse);
2544- /*
2545- * generate_normalized_query() copies exact given query_len bytes, so we
2546- * add 1 byte for null-termination here. As comments on
2547- * generate_normalized_query says, generate_normalized_query doesn't
2548- * take care of null-terminate, but additional 1 byte ensures that '\0'
2549- * byte in the source buffer to be copied into norm_query.
2550- */
2551- query_len = strlen(query) + 1;
2552- norm_query = generate_normalized_query(&jstate,
2553- query,
2554- &query_len,
2555- GetDatabaseEncoding());
2556- hints = get_hints_from_table(norm_query, application_name);
2557- if (debug_level > 1)
2558- {
2559- if (hints)
2560- snprintf(msgstr, 1024, "hints from table: \"%s\":"
2561- " normalzed_query=\"%s\", application name =\"%s\"",
2562- hints, norm_query, application_name);
2563- else
2564- {
2565- ereport(pg_hint_plan_message_level,
2566- (errhidestmt(hidestmt),
2567- errmsg("pg_hint_plan%s:"
2568- " no match found in table:"
2569- " application name = \"%s\","
2570- " normalzed_query=\"%s\"",
2571- qnostr, application_name, norm_query)));
2572- hidestmt = true;
2573- }
2574- }
2575- }
2576- if (hints == NULL)
2616+ if (plpgsql_recurse_level > 0)
25772617 {
2578- hints = get_hints_from_comment(query);
2618+ MemoryContext oldcontext;
25792619
2580- if (debug_level > 1)
2581- {
2582- snprintf(msgstr, 1024, "hints in comment=\"%s\"",
2583- hints ? hints : "(none)");
2584- if (debug_level > 2 ||
2585- stmt_name || strcmp(query, debug_query_string))
2586- snprintf(msgstr + strlen(msgstr), 1024- strlen(msgstr),
2587- ", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2588- stmt_name, query, debug_query_string);
2589- }
2620+ if (current_hint_str)
2621+ pfree((void *)current_hint_str);
2622+
2623+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2624+ current_hint_str =
2625+ get_hints_from_comment((char *)error_context_stack->arg);
2626+ MemoryContextSwitchTo(oldcontext);
25902627 }
25912628
2592- hstate = create_hintstate(parse, hints);
2629+ if (!current_hint_str)
2630+ goto standard_planner_proc;
25932631
2594- /*
2595- * Use standard planner if the statement has not valid hint. Other hook
2596- * functions try to change plan with current_hint if any, so set it to
2597- * NULL.
2598- */
2632+ /* parse the hint into hint state struct */
2633+ hstate = create_hintstate(parse, pstrdup(current_hint_str));
2634+
2635+ /* run standard planner if the statement has not valid hint */
25992636 if (!hstate)
26002637 goto standard_planner_proc;
2601-
2638+
26022639 /*
26032640 * Push new hint struct to the hint stack to disable previous hint context.
26042641 */
@@ -2629,10 +2666,9 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
26292666 if (debug_level > 1)
26302667 {
26312668 ereport(pg_hint_plan_message_level,
2632- (errhidestmt(hidestmt),
2633- errmsg("pg_hint_plan%s: planner: %s",
2634- qnostr, msgstr)));
2635- hidestmt = true;
2669+ (errhidestmt(msgqno != qno),
2670+ errmsg("pg_hint_plan%s: planner", qnostr)));
2671+ msgqno = qno;
26362672 }
26372673
26382674 /*
@@ -2677,10 +2713,10 @@ standard_planner_proc:
26772713 if (debug_level > 1)
26782714 {
26792715 ereport(pg_hint_plan_message_level,
2680- (errhidestmt(hidestmt),
2681- errmsg("pg_hint_plan%s: planner: no valid hint (%s)",
2682- qnostr, msgstr)));
2683- hidestmt = true;
2716+ (errhidestmt(msgqno != qno),
2717+ errmsg("pg_hint_plan%s: planner: no valid hint",
2718+ qnostr)));
2719+ msgqno = qno;
26842720 }
26852721 current_hint = NULL;
26862722 if (prev_planner)
Show on old repository browser