• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision3f6c9838430bc6899644bb4bd3c991b605b144ad (tree)
Time2017-05-18 19:01:20
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
@@ -84,10 +87,12 @@ PG_MODULE_MAGIC;
8487 #define HINT_ARRAY_DEFAULT_INITSIZE 8
8588
8689 #define hint_ereport(str, detail) \
87- ereport(pg_hint_plan_message_level, \
88- (errhidestmt(hidestmt), \
89- errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
90- errdetail detail))
90+ do { \
91+ ereport(pg_hint_plan_message_level, \
92+ (errmsg("pg_hint_plan%s: hint syntax error at or near \"%s\"", qnostr, (str)), \
93+ errdetail detail)); \
94+ msgqno = qno; \
95+ } while(0)
9196
9297 #define skip_space(str) \
9398 while (isspace(*str)) \
@@ -189,7 +194,9 @@ typedef enum HintStatus
189194 (hint)->base.state == HINT_STATE_USED)
190195
191196 static unsigned int qno = 0;
197+static unsigned int msgqno = 0;
192198 static char qnostr[32];
199+static const char *current_hint_str = NULL;
193200
194201 /* common data for all hints. */
195202 struct Hint
@@ -338,11 +345,7 @@ void _PG_fini(void);
338345 static void push_hint(HintState *hstate);
339346 static void pop_hint(void);
340347
341-static void pg_hint_plan_ProcessUtility(Node *parsetree,
342- const char *queryString,
343- ProcessUtilityContext context,
344- ParamListInfo params,
345- DestReceiver *dest, char *completionTag);
348+static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
346349 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
347350 ParamListInfo boundParams);
348351 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
@@ -433,9 +436,6 @@ static int pg_hint_plan_message_level = INFO;
433436 /* Default is off, to keep backward compatibility. */
434437 static bool pg_hint_plan_enable_hint_table = false;
435438
436-/* Internal static variables. */
437-static bool hidestmt = false; /* Allow or inhibit STATEMENT: output */
438-
439439 static int plpgsql_recurse_level = 0; /* PLpgSQL recursion level */
440440 static int hint_inhibit_level = 0; /* Inhibit hinting if this is above 0 */
441441 /* (This could not be above 1) */
@@ -476,7 +476,7 @@ static const struct config_enum_entry parse_debug_level_options[] = {
476476 };
477477
478478 /* Saved hook values in case of unload */
479-static ProcessUtility_hook_type prev_ProcessUtility = NULL;
479+static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
480480 static planner_hook_type prev_planner = NULL;
481481 static get_relation_info_hook_type prev_get_relation_info = NULL;
482482 static join_search_hook_type prev_join_search = NULL;
@@ -602,8 +602,8 @@ _PG_init(void)
602602 NULL);
603603
604604 /* Install hooks. */
605- prev_ProcessUtility = ProcessUtility_hook;
606- ProcessUtility_hook = pg_hint_plan_ProcessUtility;
605+ prev_post_parse_analyze_hook = post_parse_analyze_hook;
606+ post_parse_analyze_hook = pg_hint_plan_post_parse_analyze;
607607 prev_planner = planner_hook;
608608 planner_hook = pg_hint_plan_planner;
609609 prev_get_relation_info = get_relation_info_hook;
@@ -628,7 +628,7 @@ _PG_fini(void)
628628 PLpgSQL_plugin **var_ptr;
629629
630630 /* Uninstall hooks. */
631- ProcessUtility_hook = prev_ProcessUtility;
631+ post_parse_analyze_hook = prev_post_parse_analyze_hook;
632632 planner_hook = prev_planner;
633633 get_relation_info_hook = prev_get_relation_info;
634634 join_search_hook = prev_join_search;
@@ -1135,8 +1135,8 @@ HintStateDump2(HintState *hstate)
11351135 appendStringInfoChar(&buf, '}');
11361136
11371137 ereport(pg_hint_plan_message_level,
1138- (errhidestmt(true),
1139- errmsg("%s", buf.data)));
1138+ (errmsg("%s", buf.data),
1139+ errhidestmt(true)));
11401140
11411141 pfree(buf.data);
11421142 }
@@ -1585,7 +1585,15 @@ get_hints_from_table(const char *client_query, const char *client_application)
15851585
15861586 PG_TRY();
15871587 {
1588+ bool snapshot_set = false;
1589+
15881590 hint_inhibit_level++;
1591+
1592+ if (!ActiveSnapshotSet())
1593+ {
1594+ PushActiveSnapshot(GetTransactionSnapshot());
1595+ snapshot_set = true;
1596+ }
15891597
15901598 SPI_connect();
15911599
@@ -1622,7 +1630,10 @@ get_hints_from_table(const char *client_query, const char *client_application)
16221630 }
16231631
16241632 SPI_finish();
1625-
1633+
1634+ if (snapshot_set)
1635+ PopActiveSnapshot();
1636+
16261637 hint_inhibit_level--;
16271638 }
16281639 PG_CATCH();
@@ -1639,27 +1650,81 @@ get_hints_from_table(const char *client_query, const char *client_application)
16391650 * Get client-supplied query string.
16401651 */
16411652 static const char *
1642-get_query_string(void)
1653+get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16431654 {
1644- const char *p;
1655+ const char *p = debug_query_string;
16451656
1646- if (plpgsql_recurse_level > 0)
1647- {
1648- /*
1649- * This is quite ugly but this is the only point I could find where
1650- * we can get the query string.
1651- */
1652- p = (char*)error_context_stack->arg;
1653- }
1654- else if (stmt_name)
1657+ if (jumblequery != NULL)
1658+ *jumblequery = query;
1659+
1660+ Assert(plpgsql_recurse_level == 0);
1661+
1662+ if (query->commandType == CMD_UTILITY)
16551663 {
1656- PreparedStatement *entry;
1664+ Query *target_query = query;
1665+
1666+ /* Use the target query if EXPLAIN */
1667+ if (IsA(query->utilityStmt, ExplainStmt))
1668+ {
1669+ ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt);
1670+ Assert(IsA(stmt->query, Query));
1671+ target_query = (Query *)stmt->query;
1672+
1673+ if (target_query->commandType == CMD_UTILITY &&
1674+ target_query->utilityStmt != NULL)
1675+ target_query = (Query *)target_query->utilityStmt;
1676+
1677+ if (jumblequery)
1678+ *jumblequery = target_query;
1679+ }
1680+
1681+ if (IsA(target_query, CreateTableAsStmt))
1682+ {
1683+ /*
1684+ * Use the the body query for CREATE AS. The Query for jumble also
1685+ * replaced with the corresponding one.
1686+ */
1687+ CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
1688+ PreparedStatement *entry;
1689+ Query *ent_query;
1690+
1691+ Assert(IsA(stmt->query, Query));
1692+ target_query = (Query *) stmt->query;
16571693
1658- entry = FetchPreparedStatement(stmt_name, true);
1659- p = entry->plansource->query_string;
1694+ if (target_query->commandType == CMD_UTILITY &&
1695+ IsA(target_query->utilityStmt, ExecuteStmt))
1696+ {
1697+ ExecuteStmt *estmt = (ExecuteStmt *) target_query->utilityStmt;
1698+ entry = FetchPreparedStatement(estmt->name, true);
1699+ p = entry->plansource->query_string;
1700+ ent_query = (Query *) linitial (entry->plansource->query_list);
1701+ Assert(IsA(ent_query, Query));
1702+ if (jumblequery)
1703+ *jumblequery = ent_query;
1704+ }
1705+ }
1706+ else
1707+ if (IsA(target_query, ExecuteStmt))
1708+ {
1709+ /*
1710+ * Use the prepared query for EXECUTE. The Query for jumble also
1711+ * replaced with the corresponding one.
1712+ */
1713+ ExecuteStmt *stmt = (ExecuteStmt *)target_query;
1714+ PreparedStatement *entry;
1715+ Query *ent_query;
1716+
1717+ entry = FetchPreparedStatement(stmt->name, true);
1718+ p = entry->plansource->query_string;
1719+ ent_query = (Query *) linitial (entry->plansource->query_list);
1720+ Assert(IsA(ent_query, Query));
1721+ if (jumblequery)
1722+ *jumblequery = ent_query;
1723+ }
16601724 }
1661- else
1662- p = debug_query_string;
1725+ /* Return NULL if the pstate is not identical to the top-level query */
1726+ else if (strcmp(pstate->p_sourcetext, p) != 0)
1727+ p = NULL;
16631728
16641729 return p;
16651730 }
@@ -2225,10 +2290,10 @@ set_config_option_wrapper(const char *name, const char *value,
22252290
22262291 ereport(elevel,
22272292 (errcode(errdata->sqlerrcode),
2228- errhidestmt(hidestmt),
22292293 errmsg("%s", errdata->message),
22302294 errdata->detail ? errdetail("%s", errdata->detail) : 0,
22312295 errdata->hint ? errhint("%s", errdata->hint) : 0));
2296+ msgqno = qno;
22322297 FreeErrorData(errdata);
22332298 }
22342299 PG_END_TRY();
@@ -2306,132 +2371,6 @@ set_join_config_options(unsigned char enforce_mask, GucContext context)
23062371 }
23072372
23082373 /*
2309- * pg_hint_plan hook functions
2310- */
2311-
2312-static void
2313-pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
2314- ProcessUtilityContext context,
2315- ParamListInfo params,
2316- DestReceiver *dest, char *completionTag)
2317-{
2318- Node *node;
2319-
2320- /*
2321- * Use standard planner if pg_hint_plan is disabled or current nesting
2322- * depth is nesting depth of SPI calls.
2323- */
2324- if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
2325- {
2326- if (debug_level > 1)
2327- ereport(pg_hint_plan_message_level,
2328- (errmsg ("pg_hint_plan: ProcessUtility:"
2329- " pg_hint_plan.enable_hint = off")));
2330- if (prev_ProcessUtility)
2331- (*prev_ProcessUtility) (parsetree, queryString,
2332- context, params,
2333- dest, completionTag);
2334- else
2335- standard_ProcessUtility(parsetree, queryString,
2336- context, params,
2337- dest, completionTag);
2338- return;
2339- }
2340-
2341- node = parsetree;
2342- if (IsA(node, ExplainStmt))
2343- {
2344- /*
2345- * Draw out parse tree of actual query from Query struct of EXPLAIN
2346- * statement.
2347- */
2348- ExplainStmt *stmt;
2349- Query *query;
2350-
2351- stmt = (ExplainStmt *) node;
2352-
2353- Assert(IsA(stmt->query, Query));
2354- query = (Query *) stmt->query;
2355-
2356- if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
2357- node = query->utilityStmt;
2358- }
2359-
2360- /*
2361- * If the query was a EXECUTE or CREATE TABLE AS EXECUTE, get query string
2362- * specified to preceding PREPARE command to use it as source of hints.
2363- */
2364- if (IsA(node, ExecuteStmt))
2365- {
2366- ExecuteStmt *stmt;
2367-
2368- stmt = (ExecuteStmt *) node;
2369- stmt_name = stmt->name;
2370- }
2371-
2372- /*
2373- * CREATE AS EXECUTE behavior has changed since 9.2, so we must handle it
2374- * specially here.
2375- */
2376- if (IsA(node, CreateTableAsStmt))
2377- {
2378- CreateTableAsStmt *stmt;
2379- Query *query;
2380-
2381- stmt = (CreateTableAsStmt *) node;
2382- Assert(IsA(stmt->query, Query));
2383- query = (Query *) stmt->query;
2384-
2385- if (query->commandType == CMD_UTILITY &&
2386- IsA(query->utilityStmt, ExecuteStmt))
2387- {
2388- ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
2389- stmt_name = estmt->name;
2390- }
2391- }
2392-
2393- if (stmt_name)
2394- {
2395- if (debug_level > 1)
2396- ereport(pg_hint_plan_message_level,
2397- (errmsg ("pg_hint_plan: ProcessUtility:"
2398- " stmt_name = \"%s\", statement=\"%s\"",
2399- stmt_name, queryString)));
2400-
2401- PG_TRY();
2402- {
2403- if (prev_ProcessUtility)
2404- (*prev_ProcessUtility) (parsetree, queryString,
2405- context, params,
2406- dest, completionTag);
2407- else
2408- standard_ProcessUtility(parsetree, queryString,
2409- context, params,
2410- dest, completionTag);
2411- }
2412- PG_CATCH();
2413- {
2414- stmt_name = NULL;
2415- PG_RE_THROW();
2416- }
2417- PG_END_TRY();
2418-
2419- stmt_name = NULL;
2420-
2421- return;
2422- }
2423-
2424- if (prev_ProcessUtility)
2425- (*prev_ProcessUtility) (parsetree, queryString,
2426- context, params,
2427- dest, completionTag);
2428- else
2429- standard_ProcessUtility(parsetree, queryString,
2430- context, params,
2431- dest, completionTag);
2432-}
2433-
2434-/*
24352374 * Push a hint into hint stack which is implemented with List struct. Head of
24362375 * list is top of stack.
24372376 */
@@ -2465,24 +2404,178 @@ pop_hint(void)
24652404 current_hint = (HintState *) lfirst(list_head(HintStateStack));
24662405 }
24672406
2407+/*
2408+ * We need only jumbled query for the root query, acquired by
2409+ * get_query_string(). We cannot check that in pg_hint_plan_planner since it
2410+ * cannot see the truely corresponding query string to the given Query.
2411+ * We check that here instead using ParseState.
2412+ */
2413+static void
2414+pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
2415+{
2416+ const char *query_str;
2417+ MemoryContext oldcontext;
2418+
2419+ if (prev_post_parse_analyze_hook)
2420+ prev_post_parse_analyze_hook(pstate, query);
2421+
2422+ /* do nothing under hint table search */
2423+ if (hint_inhibit_level > 0)
2424+ return;
2425+
2426+ if (!pg_hint_plan_enable_hint)
2427+ {
2428+ if (current_hint_str)
2429+ {
2430+ pfree((void *)current_hint_str);
2431+ current_hint_str = NULL;
2432+ }
2433+ return;
2434+ }
2435+
2436+ /* increment the query number */
2437+ qnostr[0] = 0;
2438+ if (debug_level > 1)
2439+ snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2440+ qno++;
2441+
2442+ /* search the hint table for a hint if requested */
2443+ if (pg_hint_plan_enable_hint_table)
2444+ {
2445+ int query_len;
2446+ pgssJumbleState jstate;
2447+ Query *jumblequery;
2448+ char *normalized_query = NULL;
2449+
2450+ query_str = get_query_string(pstate, query, &jumblequery);
2451+
2452+ /* If this query is not for hint, just return */
2453+ if (!query_str)
2454+ return;
2455+
2456+ /* clear the previous hint string */
2457+ if (current_hint_str)
2458+ {
2459+ pfree((void *)current_hint_str);
2460+ current_hint_str = NULL;
2461+ }
2462+
2463+ if (jumblequery)
2464+ {
2465+ /*
2466+ * XXX: normalizing code is copied from pg_stat_statements.c, so be
2467+ * careful to PostgreSQL's version up.
2468+ */
2469+ jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2470+ jstate.jumble_len = 0;
2471+ jstate.clocations_buf_size = 32;
2472+ jstate.clocations = (pgssLocationLen *)
2473+ palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2474+ jstate.clocations_count = 0;
2475+
2476+ JumbleQuery(&jstate, jumblequery);
2477+
2478+ /*
2479+ * Normalize the query string by replacing constants with '?'
2480+ */
2481+ /*
2482+ * Search hint string which is stored keyed by query string
2483+ * and application name. The query string is normalized to allow
2484+ * fuzzy matching.
2485+ *
2486+ * Adding 1 byte to query_len ensures that the returned string has
2487+ * a terminating NULL.
2488+ */
2489+ query_len = strlen(query_str) + 1;
2490+ normalized_query =
2491+ generate_normalized_query(&jstate, query_str,
2492+ &query_len,
2493+ GetDatabaseEncoding());
2494+
2495+ /*
2496+ * find a hint for the normalized query. the result should be in
2497+ * TopMemoryContext
2498+ */
2499+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2500+ current_hint_str =
2501+ get_hints_from_table(normalized_query, application_name);
2502+ MemoryContextSwitchTo(oldcontext);
2503+
2504+ if (debug_level > 1)
2505+ {
2506+ if (current_hint_str)
2507+ ereport(pg_hint_plan_message_level,
2508+ (errmsg("pg_hint_plan[qno=0x%x]: "
2509+ "post_parse_analyze_hook: "
2510+ "hints from table: \"%s\": "
2511+ "normalized_query=\"%s\", "
2512+ "application name =\"%s\"",
2513+ qno, current_hint_str,
2514+ normalized_query, application_name),
2515+ errhidestmt(msgqno != qno)));
2516+ else
2517+ ereport(pg_hint_plan_message_level,
2518+ (errmsg("pg_hint_plan[qno=0x%x]: "
2519+ "no match found in table: "
2520+ "application name = \"%s\", "
2521+ "normalized_query=\"%s\"",
2522+ qno, application_name,
2523+ normalized_query),
2524+ errhidestmt(msgqno != qno)));
2525+
2526+ msgqno = qno;
2527+ }
2528+ }
2529+
2530+ /* retrun if we have hint here*/
2531+ if (current_hint_str)
2532+ return;
2533+ }
2534+ else
2535+ query_str = get_query_string(pstate, query, NULL);
2536+
2537+ if (query_str)
2538+ {
2539+ /*
2540+ * get hints from the comment. However we may have the same query
2541+ * string with the previous call, but just retrieving hints is expected
2542+ * to be faster than checking for identicalness before retrieval.
2543+ */
2544+ if (current_hint_str)
2545+ pfree((void *)current_hint_str);
2546+
2547+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2548+ current_hint_str = get_hints_from_comment(query_str);
2549+ MemoryContextSwitchTo(oldcontext);
2550+ }
2551+
2552+ if (debug_level > 1)
2553+ {
2554+ if (debug_level == 1 &&
2555+ (stmt_name || strcmp(query_str, debug_query_string)))
2556+ ereport(pg_hint_plan_message_level,
2557+ (errmsg("hints in comment=\"%s\"",
2558+ current_hint_str ? current_hint_str : "(none)"),
2559+ errhidestmt(msgqno != qno)));
2560+ else
2561+ ereport(pg_hint_plan_message_level,
2562+ (errmsg("hints in comment=\"%s\", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2563+ current_hint_str ? current_hint_str : "(none)",
2564+ stmt_name, query_str, debug_query_string),
2565+ errhidestmt(msgqno != qno)));
2566+ msgqno = qno;
2567+ }
2568+}
2569+
2570+/*
2571+ * Read and set up hint information
2572+ */
24682573 static PlannedStmt *
24692574 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
24702575 {
2471- const char *hints = NULL;
2472- const char *query;
2473- char *norm_query;
2474- pgssJumbleState jstate;
2475- int query_len;
24762576 int save_nestlevel;
24772577 PlannedStmt *result;
24782578 HintState *hstate;
2479- char msgstr[1024];
2480-
2481- qnostr[0] = 0;
2482- strcpy(msgstr, "");
2483- if (debug_level > 1)
2484- snprintf(qnostr, sizeof(qnostr), "[qno=0x%x]", qno++);
2485- hidestmt = false;
24862579
24872580 /*
24882581 * Use standard planner if pg_hint_plan is disabled or current nesting
@@ -2492,103 +2585,44 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
24922585 if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0)
24932586 {
24942587 if (debug_level > 1)
2495- elog(pg_hint_plan_message_level,
2496- "pg_hint_plan%s: planner: enable_hint=%d,"
2497- " hint_inhibit_level=%d",
2498- qnostr, pg_hint_plan_enable_hint, hint_inhibit_level);
2499- hidestmt = true;
2588+ ereport(pg_hint_plan_message_level,
2589+ (errmsg ("pg_hint_plan%s: planner: enable_hint=%d,"
2590+ " hint_inhibit_level=%d",
2591+ qnostr, pg_hint_plan_enable_hint,
2592+ hint_inhibit_level),
2593+ errhidestmt(msgqno != qno)));
2594+ msgqno = qno;
25002595
25012596 goto standard_planner_proc;
25022597 }
25032598
2504- /* Create hint struct from client-supplied query string. */
2505- query = get_query_string();
2506-
25072599 /*
2508- * Create hintstate from hint specified for the query, if any.
2509- *
2510- * First we lookup hint in pg_hint.hints table by normalized query string,
2511- * unless pg_hint_plan.enable_hint_table is OFF.
2512- * This parameter provides option to avoid overhead of table lookup during
2513- * planning.
2514- *
2515- * If no hint was found, then we try to get hint from special query comment.
2600+ * Support for nested plpgsql functions. This is quite ugly but this is the
2601+ * only point I could find where I can get the query string.
25162602 */
2517- if (pg_hint_plan_enable_hint_table)
2518- {
2519- /*
2520- * Search hint information which is stored for the query and the
2521- * application. Query string is normalized before using in condition
2522- * in order to allow fuzzy matching.
2523- *
2524- * XXX: normalizing code is copied from pg_stat_statements.c, so be
2525- * careful when supporting PostgreSQL's version up.
2526- */
2527- jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2528- jstate.jumble_len = 0;
2529- jstate.clocations_buf_size = 32;
2530- jstate.clocations = (pgssLocationLen *)
2531- palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2532- jstate.clocations_count = 0;
2533- JumbleQuery(&jstate, parse);
2534- /*
2535- * generate_normalized_query() copies exact given query_len bytes, so we
2536- * add 1 byte for null-termination here. As comments on
2537- * generate_normalized_query says, generate_normalized_query doesn't
2538- * take care of null-terminate, but additional 1 byte ensures that '\0'
2539- * byte in the source buffer to be copied into norm_query.
2540- */
2541- query_len = strlen(query) + 1;
2542- norm_query = generate_normalized_query(&jstate,
2543- query,
2544- &query_len,
2545- GetDatabaseEncoding());
2546- hints = get_hints_from_table(norm_query, application_name);
2547- if (debug_level > 1)
2548- {
2549- if (hints)
2550- snprintf(msgstr, 1024, "hints from table: \"%s\":"
2551- " normalzed_query=\"%s\", application name =\"%s\"",
2552- hints, norm_query, application_name);
2553- else
2554- {
2555- ereport(pg_hint_plan_message_level,
2556- (errhidestmt(hidestmt),
2557- errmsg("pg_hint_plan%s:"
2558- " no match found in table:"
2559- " application name = \"%s\","
2560- " normalzed_query=\"%s\"",
2561- qnostr, application_name, norm_query)));
2562- hidestmt = true;
2563- }
2564- }
2565- }
2566- if (hints == NULL)
2603+ if (plpgsql_recurse_level > 0)
25672604 {
2568- hints = get_hints_from_comment(query);
2605+ MemoryContext oldcontext;
25692606
2570- if (debug_level > 1)
2571- {
2572- snprintf(msgstr, 1024, "hints in comment=\"%s\"",
2573- hints ? hints : "(none)");
2574- if (debug_level > 2 ||
2575- stmt_name || strcmp(query, debug_query_string))
2576- snprintf(msgstr + strlen(msgstr), 1024- strlen(msgstr),
2577- ", stmt=\"%s\", query=\"%s\", debug_query_string=\"%s\"",
2578- stmt_name, query, debug_query_string);
2579- }
2607+ if (current_hint_str)
2608+ pfree((void *)current_hint_str);
2609+
2610+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2611+ current_hint_str =
2612+ get_hints_from_comment((char *)error_context_stack->arg);
2613+ MemoryContextSwitchTo(oldcontext);
25802614 }
25812615
2582- hstate = create_hintstate(parse, hints);
2616+ if (!current_hint_str)
2617+ goto standard_planner_proc;
25832618
2584- /*
2585- * Use standard planner if the statement has not valid hint. Other hook
2586- * functions try to change plan with current_hint if any, so set it to
2587- * NULL.
2588- */
2619+ /* parse the hint into hint state struct */
2620+ hstate = create_hintstate(parse, pstrdup(current_hint_str));
2621+
2622+ /* run standard planner if the statement has not valid hint */
25892623 if (!hstate)
25902624 goto standard_planner_proc;
2591-
2625+
25922626 /*
25932627 * Push new hint struct to the hint stack to disable previous hint context.
25942628 */
@@ -2619,10 +2653,9 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
26192653 if (debug_level > 1)
26202654 {
26212655 ereport(pg_hint_plan_message_level,
2622- (errhidestmt(hidestmt),
2623- errmsg("pg_hint_plan%s: planner: %s",
2624- qnostr, msgstr)));
2625- hidestmt = true;
2656+ (errhidestmt(msgqno != qno),
2657+ errmsg("pg_hint_plan%s: planner", qnostr)));
2658+ msgqno = qno;
26262659 }
26272660
26282661 /*
@@ -2667,10 +2700,10 @@ standard_planner_proc:
26672700 if (debug_level > 1)
26682701 {
26692702 ereport(pg_hint_plan_message_level,
2670- (errhidestmt(hidestmt),
2671- errmsg("pg_hint_plan%s: planner: no valid hint (%s)",
2672- qnostr, msgstr)));
2673- hidestmt = true;
2703+ (errhidestmt(msgqno != qno),
2704+ errmsg("pg_hint_plan%s: planner: no valid hint",
2705+ qnostr)));
2706+ msgqno = qno;
26742707 }
26752708 current_hint = NULL;
26762709 if (prev_planner)
Show on old repository browser