firtst release
Revision | de449945fe23b70d284a2240085620b708d0ef57 (tree) |
---|---|
Time | 2017-07-27 19:19:57 |
Author | Kyotaro Horiguchi <horiguchi.kyotaro@lab....> |
Commiter | 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.
@@ -7,7 +7,7 @@ | ||
7 | 7 | MODULES = pg_hint_plan |
8 | 8 | HINTPLANVER = 1.1.4 |
9 | 9 | |
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 | |
11 | 11 | |
12 | 12 | 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 |
13 | 13 |
@@ -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; |
@@ -1650,8 +1650,9 @@ get_hints_from_table(const char *client_query, const char *client_application) | ||
1650 | 1650 | /* |
1651 | 1651 | * Get client-supplied query string. Addtion to that the jumbled query is |
1652 | 1652 | * 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. | |
1655 | 1656 | */ |
1656 | 1657 | static const char * |
1657 | 1658 | get_query_string(ParseState *pstate, Query *query, Query **jumblequery) |
@@ -1661,15 +1662,22 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery) | ||
1661 | 1662 | if (jumblequery != NULL) |
1662 | 1663 | *jumblequery = query; |
1663 | 1664 | |
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) | |
1665 | 1667 | { |
1666 | 1668 | Query *target_query = query; |
1667 | 1669 | |
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)) | |
1670 | 1678 | { |
1671 | - ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt); | |
1672 | - | |
1679 | + ExplainStmt *stmt = (ExplainStmt *)target_query->utilityStmt; | |
1680 | + | |
1673 | 1681 | Assert(IsA(stmt->query, Query)); |
1674 | 1682 | target_query = (Query *)stmt->query; |
1675 | 1683 |
@@ -1679,34 +1687,43 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery) | ||
1679 | 1687 | target_query = (Query *)target_query->utilityStmt; |
1680 | 1688 | } |
1681 | 1689 | |
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)) | |
1683 | 1698 | { |
1684 | 1699 | /* |
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. | |
1687 | 1702 | */ |
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 | + { | |
1688 | 1711 | CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query; |
1689 | - PreparedStatement *entry; | |
1690 | - Query *tmp_query; | |
1691 | 1712 | |
1692 | 1713 | Assert(IsA(stmt->query, Query)); |
1693 | - tmp_query = (Query *) stmt->query; | |
1714 | + target_query = (Query *) stmt->query; | |
1694 | 1715 | |
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; | |
1703 | 1720 | } |
1704 | - else | |
1721 | + | |
1705 | 1722 | if (IsA(target_query, ExecuteStmt)) |
1706 | 1723 | { |
1707 | 1724 | /* |
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. | |
1710 | 1727 | */ |
1711 | 1728 | ExecuteStmt *stmt = (ExecuteStmt *)target_query; |
1712 | 1729 | PreparedStatement *entry; |
@@ -1715,15 +1732,16 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery) | ||
1715 | 1732 | p = entry->plansource->query_string; |
1716 | 1733 | target_query = (Query *) linitial (entry->plansource->query_list); |
1717 | 1734 | } |
1718 | - | |
1719 | - /* We don't accept other than a Query other than a CMD_UTILITY */ | |
1735 | + | |
1736 | + /* JumbleQuery accespts only a non-utility Query */ | |
1720 | 1737 | if (!IsA(target_query, Query) || |
1721 | - target_query->commandType == CMD_UTILITY) | |
1738 | + target_query->utilityStmt != NULL) | |
1722 | 1739 | target_query = NULL; |
1723 | 1740 | |
1724 | 1741 | if (jumblequery) |
1725 | 1742 | *jumblequery = target_query; |
1726 | 1743 | } |
1744 | + | |
1727 | 1745 | /* Return NULL if the pstate is not identical to the top-level query */ |
1728 | 1746 | else if (strcmp(pstate->p_sourcetext, p) != 0) |
1729 | 1747 | p = NULL; |
@@ -2531,7 +2549,7 @@ pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query) | ||
2531 | 2549 | } |
2532 | 2550 | } |
2533 | 2551 | |
2534 | - /* retrun if we have hint here*/ | |
2552 | + /* retrun if we have hint here */ | |
2535 | 2553 | if (current_hint_str) |
2536 | 2554 | return; |
2537 | 2555 | } |
@@ -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; |