• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: コミット

firtst release


コミットメタ情報

リビジョンde449945fe23b70d284a2240085620b708d0ef57 (tree)
日時2017-07-27 19:19:57
作者Kyotaro Horiguchi <horiguchi.kyotaro@lab....>
コミッターKyotaro Horiguchi

ログメッセージ

Fixed a crash bug by DECLARE CURSOR and enable_hint_table = on

The previous version causes assertion failure by DECLARE CURSOR syntax
when table hint is activated. The cause is that the version forgot the
fact that DelcareCursorStmt is in a bit strange shape. Add support of
DECLARE CURSOR and regression test for table hinting.

変更サマリ

差分

--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
77 MODULES = pg_hint_plan
88 HINTPLANVER = 1.1.4
99
10-REGRESS = init base_plan pg_hint_plan ut-init ut-A ut-S ut-J ut-L ut-G ut-R ut-fdw ut-fini
10+REGRESS = init base_plan pg_hint_plan ut-init ut-A ut-S ut-J ut-L ut-G ut-R ut-fdw ut-T ut-fini
1111
1212 REGRESSION_EXPECTED = expected/init.out expected/base_plan.out expected/pg_hint_plan.out expected/ut-A.out expected/ut-S.out expected/ut-J.out expected/ut-L.out expected/ut-G.out
1313
--- /dev/null
+++ b/expected/ut-T.out
@@ -0,0 +1,131 @@
1+-- ut-T: tests for table hints
2+-- This test is focusing on hint retrieval from table
3+LOAD 'pg_hint_plan';
4+SET pg_hint_plan.enable_hint TO on;
5+SET pg_hint_plan.debug_print TO on;
6+SET client_min_messages TO LOG;
7+SET search_path TO public;
8+-- test for get_query_string
9+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
10+INSERT INTO hint_plan.hints VALUES(DEFAULT,'PREPARE p1 AS SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
11+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
12+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
13+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
14+-- These queries uses IndexScan without hints
15+SET pg_hint_plan.enable_hint_table to off;
16+EXPLAIN SELECT * FROM t1 WHERE id = 100;
17+ QUERY PLAN
18+------------------------------------------------------------------
19+ Index Scan using t1_pkey on t1 (cost=0.29..8.30 rows=1 width=8)
20+ Index Cond: (id = 100)
21+(2 rows)
22+
23+EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = 100;
24+ QUERY PLAN
25+------------------------------------------------------------------
26+ Index Scan using t1_pkey on t1 (cost=0.29..8.30 rows=1 width=8)
27+ Index Cond: (id = 100)
28+(2 rows)
29+
30+EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = 100;
31+ QUERY PLAN
32+------------------------------------------------------------------
33+ Index Scan using t1_pkey on t1 (cost=0.29..8.30 rows=1 width=8)
34+ Index Cond: (id = 100)
35+(2 rows)
36+
37+EXPLAIN EXECUTE p1;
38+ QUERY PLAN
39+------------------------------------------------------------------
40+ Index Scan using t1_pkey on t1 (cost=0.29..8.30 rows=1 width=8)
41+ Index Cond: (id = 100)
42+(2 rows)
43+
44+DEALLOCATE p1;
45+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
46+EXPLAIN CREATE TABLE ct1 AS EXECUTE p1;
47+ QUERY PLAN
48+------------------------------------------------------------------
49+ Index Scan using t1_pkey on t1 (cost=0.29..8.30 rows=1 width=8)
50+ Index Cond: (id = 100)
51+(2 rows)
52+
53+DEALLOCATE p1;
54+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
55+-- Forced to use SeqScan by table hints
56+SET pg_hint_plan.enable_hint_table to on;
57+EXPLAIN SELECT * FROM t1 WHERE id = 100;
58+LOG: pg_hint_plan:
59+used hint:
60+SeqScan(t1)
61+not used hint:
62+duplication hint:
63+error hint:
64+
65+ QUERY PLAN
66+----------------------------------------------------
67+ Seq Scan on t1 (cost=0.00..170.00 rows=1 width=8)
68+ Filter: (id = 100)
69+(2 rows)
70+
71+EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = 100;
72+LOG: pg_hint_plan:
73+used hint:
74+SeqScan(t1)
75+not used hint:
76+duplication hint:
77+error hint:
78+
79+ QUERY PLAN
80+----------------------------------------------------
81+ Seq Scan on t1 (cost=0.00..170.00 rows=1 width=8)
82+ Filter: (id = 100)
83+(2 rows)
84+
85+EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = 100;
86+LOG: pg_hint_plan:
87+used hint:
88+SeqScan(t1)
89+not used hint:
90+duplication hint:
91+error hint:
92+
93+ QUERY PLAN
94+----------------------------------------------------
95+ Seq Scan on t1 (cost=0.00..170.00 rows=1 width=8)
96+ Filter: (id = 100)
97+(2 rows)
98+
99+EXPLAIN EXECUTE p1;
100+LOG: pg_hint_plan:
101+used hint:
102+SeqScan(t1)
103+not used hint:
104+duplication hint:
105+error hint:
106+
107+ QUERY PLAN
108+----------------------------------------------------
109+ Seq Scan on t1 (cost=0.00..170.00 rows=1 width=8)
110+ Filter: (id = 100)
111+(2 rows)
112+
113+DEALLOCATE p1;
114+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
115+EXPLAIN CREATE TABLE ct1 AS EXECUTE p1;
116+LOG: pg_hint_plan:
117+used hint:
118+SeqScan(t1)
119+not used hint:
120+duplication hint:
121+error hint:
122+
123+ QUERY PLAN
124+----------------------------------------------------
125+ Seq Scan on t1 (cost=0.00..170.00 rows=1 width=8)
126+ Filter: (id = 100)
127+(2 rows)
128+
129+DEALLOCATE p1;
130+SET pg_hint_plan.enable_hint_table to off;
131+DELETE FROM hint_plan.hints;
--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -1650,8 +1650,9 @@ get_hints_from_table(const char *client_query, const char *client_application)
16501650 /*
16511651 * Get client-supplied query string. Addtion to that the jumbled query is
16521652 * supplied if the caller requested. From the restriction of JumbleQuery, some
1653- * kind of query needs special amendments. Reutrns NULL if the current hint
1654- * string is still valid.
1653+ * kind of query needs special amendments. Reutrns NULL if this query doesn't
1654+ * change the current hint. This function returns NULL also when something
1655+ * wrong has happend and let the caller continue using the current hints.
16551656 */
16561657 static const char *
16571658 get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
@@ -1661,15 +1662,22 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16611662 if (jumblequery != NULL)
16621663 *jumblequery = query;
16631664
1664- if (query->commandType == CMD_UTILITY)
1665+ /* Query for DeclareCursorStmt is CMD_SELECT and has query->utilityStmt */
1666+ if (query->commandType == CMD_UTILITY || query->utilityStmt)
16651667 {
16661668 Query *target_query = query;
16671669
1668- /* Use the target query if EXPLAIN */
1669- if (IsA(query->utilityStmt, ExplainStmt))
1670+ /*
1671+ * Some utility statements have a subquery that we can hint on. Since
1672+ * EXPLAIN can be placed before other kind of utility statements and
1673+ * EXECUTE can be contained other kind of utility statements, these
1674+ * conditions are not mutually exclusive and should be considered in
1675+ * this order.
1676+ */
1677+ if (IsA(target_query->utilityStmt, ExplainStmt))
16701678 {
1671- ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt);
1672-
1679+ ExplainStmt *stmt = (ExplainStmt *)target_query->utilityStmt;
1680+
16731681 Assert(IsA(stmt->query, Query));
16741682 target_query = (Query *)stmt->query;
16751683
@@ -1679,34 +1687,43 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
16791687 target_query = (Query *)target_query->utilityStmt;
16801688 }
16811689
1682- if (IsA(target_query, CreateTableAsStmt))
1690+ /*
1691+ * JumbleQuery does not accept a Query that has utilityStmt. On the
1692+ * other hand DeclareCursorStmt is in a bit strange shape that is
1693+ * flipped upside down.
1694+ */
1695+ if (IsA(target_query, Query) &&
1696+ target_query->utilityStmt &&
1697+ IsA(target_query->utilityStmt, DeclareCursorStmt))
16831698 {
16841699 /*
1685- * Use the the body query for CREATE AS. The Query for jumble also
1686- * replaced with the corresponding one.
1700+ * The given Query cannot be modified so copy it and modify so that
1701+ * JumbleQuery can accept it.
16871702 */
1703+ Assert(IsA(target_query, Query) &&
1704+ target_query->commandType == CMD_SELECT);
1705+ target_query = copyObject(target_query);
1706+ target_query->utilityStmt = NULL;
1707+ }
1708+
1709+ if (IsA(target_query, CreateTableAsStmt))
1710+ {
16881711 CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
1689- PreparedStatement *entry;
1690- Query *tmp_query;
16911712
16921713 Assert(IsA(stmt->query, Query));
1693- tmp_query = (Query *) stmt->query;
1714+ target_query = (Query *) stmt->query;
16941715
1695- if (tmp_query->commandType == CMD_UTILITY &&
1696- IsA(tmp_query->utilityStmt, ExecuteStmt))
1697- {
1698- ExecuteStmt *estmt = (ExecuteStmt *) tmp_query->utilityStmt;
1699- entry = FetchPreparedStatement(estmt->name, true);
1700- p = entry->plansource->query_string;
1701- target_query = (Query *) linitial (entry->plansource->query_list);
1702- }
1716+ /* strip out the top-level query for further processing */
1717+ if (target_query->commandType == CMD_UTILITY &&
1718+ target_query->utilityStmt != NULL)
1719+ target_query = (Query *)target_query->utilityStmt;
17031720 }
1704- else
1721+
17051722 if (IsA(target_query, ExecuteStmt))
17061723 {
17071724 /*
1708- * Use the prepared query for EXECUTE. The Query for jumble also
1709- * replaced with the corresponding one.
1725+ * Use the prepared query for EXECUTE. The Query for jumble
1726+ * also replaced with the corresponding one.
17101727 */
17111728 ExecuteStmt *stmt = (ExecuteStmt *)target_query;
17121729 PreparedStatement *entry;
@@ -1715,15 +1732,16 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
17151732 p = entry->plansource->query_string;
17161733 target_query = (Query *) linitial (entry->plansource->query_list);
17171734 }
1718-
1719- /* We don't accept other than a Query other than a CMD_UTILITY */
1735+
1736+ /* JumbleQuery accespts only a non-utility Query */
17201737 if (!IsA(target_query, Query) ||
1721- target_query->commandType == CMD_UTILITY)
1738+ target_query->utilityStmt != NULL)
17221739 target_query = NULL;
17231740
17241741 if (jumblequery)
17251742 *jumblequery = target_query;
17261743 }
1744+
17271745 /* Return NULL if the pstate is not identical to the top-level query */
17281746 else if (strcmp(pstate->p_sourcetext, p) != 0)
17291747 p = NULL;
@@ -2531,7 +2549,7 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
25312549 }
25322550 }
25332551
2534- /* retrun if we have hint here*/
2552+ /* retrun if we have hint here */
25352553 if (current_hint_str)
25362554 return;
25372555 }
--- /dev/null
+++ b/sql/ut-T.sql
@@ -0,0 +1,45 @@
1+-- ut-T: tests for table hints
2+-- This test is focusing on hint retrieval from table
3+
4+LOAD 'pg_hint_plan';
5+SET pg_hint_plan.enable_hint TO on;
6+SET pg_hint_plan.debug_print TO on;
7+SET client_min_messages TO LOG;
8+SET search_path TO public;
9+
10+-- test for get_query_string
11+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
12+INSERT INTO hint_plan.hints VALUES(DEFAULT,'PREPARE p1 AS SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
13+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
14+INSERT INTO hint_plan.hints VALUES(DEFAULT,'EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = ?;', '', 'SeqScan(t1)');
15+
16+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
17+
18+-- These queries uses IndexScan without hints
19+SET pg_hint_plan.enable_hint_table to off;
20+EXPLAIN SELECT * FROM t1 WHERE id = 100;
21+EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = 100;
22+EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = 100;
23+
24+EXPLAIN EXECUTE p1;
25+DEALLOCATE p1;
26+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
27+EXPLAIN CREATE TABLE ct1 AS EXECUTE p1;
28+
29+DEALLOCATE p1;
30+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
31+
32+-- Forced to use SeqScan by table hints
33+SET pg_hint_plan.enable_hint_table to on;
34+EXPLAIN SELECT * FROM t1 WHERE id = 100;
35+EXPLAIN DECLARE c1 CURSOR FOR SELECT * FROM t1 WHERE id = 100;
36+EXPLAIN CREATE TABLE ct1 AS SELECT * FROM t1 WHERE id = 100;
37+EXPLAIN EXECUTE p1;
38+DEALLOCATE p1;
39+PREPARE p1 AS SELECT * FROM t1 WHERE id = 100;
40+EXPLAIN CREATE TABLE ct1 AS EXECUTE p1;
41+
42+DEALLOCATE p1;
43+
44+SET pg_hint_plan.enable_hint_table to off;
45+DELETE FROM hint_plan.hints;
旧リポジトリブラウザで表示