firtst release
Revision | 0576a9485c3289d9a3bc49b63654cf8dcbe3fd04 (tree) |
---|---|
Time | 2020-02-14 13:00:01 |
Author | Kyotaro Horiguchi <horikyota.ntt@gmai...> |
Commiter | Kyotaro Horiguchi |
Fix crash bug caused by plancache invalidation
https://github.com/ossc-db/pg_hint_plan/issues/41
After plancache is invalidated then revaliated, get_query_string
accesses query_list of invalid plansource then crash. Ignore invalid
plancache and get the correct hint string and Query node at the next
planning time during revalidation.
On the way fixing this, a bug related to planner reentrance is
fixed. That fix causes behavioral change for nested
planning. Previously outer-level hint (wrongly) overrides inner-level
query but currenlty outer-level hint no longer affects inner-level
query.
@@ -4436,7 +4436,7 @@ EXPLAIN (COSTS false) | ||
4436 | 4436 | ORDER BY t_1.c1; |
4437 | 4437 | LOG: pg_hint_plan: |
4438 | 4438 | used hint: |
4439 | -HashJoin(t_1 t_2) | |
4439 | +IndexScan(t_1) | |
4440 | 4440 | not used hint: |
4441 | 4441 | duplication hint: |
4442 | 4442 | error hint: |
@@ -4460,6 +4460,11 @@ error hint: | ||
4460 | 4460 | (7 rows) |
4461 | 4461 | |
4462 | 4462 | --No.13-4-2 |
4463 | +-- recall_planner() is reduced to constant while planning using the | |
4464 | +-- hint defined in the function. Then the outer query is planned based | |
4465 | +-- on the following hint. pg_hint_plan shows the log for the function | |
4466 | +-- but the resulting explain output doesn't contain the corresponding | |
4467 | +-- plan. | |
4463 | 4468 | /*+HashJoin(st_1 st_2)*/ |
4464 | 4469 | EXPLAIN (COSTS false) |
4465 | 4470 | SELECT recall_planner() FROM s1.t1 st_1 |
@@ -4467,8 +4472,8 @@ EXPLAIN (COSTS false) | ||
4467 | 4472 | ORDER BY st_1.c1; |
4468 | 4473 | LOG: pg_hint_plan: |
4469 | 4474 | used hint: |
4475 | +IndexScan(t_1) | |
4470 | 4476 | not used hint: |
4471 | -HashJoin(st_1 st_2) | |
4472 | 4477 | duplication hint: |
4473 | 4478 | error hint: |
4474 | 4479 |
@@ -4491,6 +4496,7 @@ error hint: | ||
4491 | 4496 | (7 rows) |
4492 | 4497 | |
4493 | 4498 | --No.13-4-3 |
4499 | +--See description for No.13-4-2 | |
4494 | 4500 | /*+HashJoin(t_1 t_2)*/ |
4495 | 4501 | EXPLAIN (COSTS false) |
4496 | 4502 | SELECT recall_planner() FROM s1.t1 st_1 |
@@ -4498,7 +4504,7 @@ EXPLAIN (COSTS false) | ||
4498 | 4504 | ORDER BY st_1.c1; |
4499 | 4505 | LOG: pg_hint_plan: |
4500 | 4506 | used hint: |
4501 | -HashJoin(t_1 t_2) | |
4507 | +IndexScan(t_1) | |
4502 | 4508 | not used hint: |
4503 | 4509 | duplication hint: |
4504 | 4510 | error hint: |
@@ -4521,6 +4527,7 @@ error hint: | ||
4521 | 4527 | (6 rows) |
4522 | 4528 | |
4523 | 4529 | --No.13-4-4 |
4530 | +--See description for No.13-4-2 | |
4524 | 4531 | /*+HashJoin(st_1 st_2)*/ |
4525 | 4532 | EXPLAIN (COSTS false) |
4526 | 4533 | SELECT recall_planner() FROM s1.t1 t_1 |
@@ -4528,8 +4535,8 @@ EXPLAIN (COSTS false) | ||
4528 | 4535 | ORDER BY t_1.c1; |
4529 | 4536 | LOG: pg_hint_plan: |
4530 | 4537 | used hint: |
4538 | +IndexScan(t_1) | |
4531 | 4539 | not used hint: |
4532 | -HashJoin(st_1 st_2) | |
4533 | 4540 | duplication hint: |
4534 | 4541 | error hint: |
4535 | 4542 |
@@ -4551,18 +4558,18 @@ error hint: | ||
4551 | 4558 | (6 rows) |
4552 | 4559 | |
4553 | 4560 | --No.13-4-5 |
4561 | +-- See description for No.13-4-2. No joins in ths plan, so | |
4562 | +-- pg_hint_plan doesn't complain on the wrongly written error hint. | |
4554 | 4563 | /*+HashJoin(t_1 t_1)*/ |
4555 | 4564 | EXPLAIN (COSTS false) |
4556 | 4565 | SELECT recall_planner() FROM s1.t1 t_1 |
4557 | 4566 | ORDER BY t_1.c1; |
4558 | -INFO: pg_hint_plan: hint syntax error at or near "HashJoin(t_1 t_1)" | |
4559 | -DETAIL: Relation name "t_1" is duplicated. | |
4560 | 4567 | LOG: pg_hint_plan: |
4561 | 4568 | used hint: |
4569 | +IndexScan(t_1) | |
4562 | 4570 | not used hint: |
4563 | 4571 | duplication hint: |
4564 | 4572 | error hint: |
4565 | -HashJoin(t_1 t_1) | |
4566 | 4573 | |
4567 | 4574 | LOG: pg_hint_plan: |
4568 | 4575 | used hint: |
@@ -4586,6 +4593,13 @@ EXPLAIN (COSTS false) | ||
4586 | 4593 | SELECT recall_planner_one_t() FROM s1.t1 t_1 |
4587 | 4594 | JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) |
4588 | 4595 | ORDER BY t_1.c1; |
4596 | +LOG: pg_hint_plan: | |
4597 | +used hint: | |
4598 | +IndexScan(t_1) | |
4599 | +not used hint: | |
4600 | +duplication hint: | |
4601 | +error hint: | |
4602 | + | |
4589 | 4603 | QUERY PLAN |
4590 | 4604 | --------------------------------------------- |
4591 | 4605 | Merge Join |
@@ -4603,8 +4617,8 @@ EXPLAIN (COSTS false) | ||
4603 | 4617 | ORDER BY t_1.c1; |
4604 | 4618 | LOG: pg_hint_plan: |
4605 | 4619 | used hint: |
4620 | +IndexScan(t_1) | |
4606 | 4621 | not used hint: |
4607 | -HashJoin(t_1 t_1) | |
4608 | 4622 | duplication hint: |
4609 | 4623 | error hint: |
4610 | 4624 |
@@ -4630,19 +4644,18 @@ HashJoin(t_1 t_1) | ||
4630 | 4644 | DROP FUNCTION recall_planner_one_t(int); |
4631 | 4645 | ERROR: function recall_planner_one_t(integer) does not exist |
4632 | 4646 | --No.13-4-7 |
4647 | +-- See description for No.13-4-2. Complains on the wrongly wrtten hit. | |
4633 | 4648 | /*+HashJoin(t_1 t_1)*/ |
4634 | 4649 | EXPLAIN (COSTS false) |
4635 | 4650 | SELECT recall_planner() FROM s1.t1 t_1 |
4636 | 4651 | JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) |
4637 | 4652 | ORDER BY t_1.c1; |
4638 | -INFO: pg_hint_plan: hint syntax error at or near "HashJoin(t_1 t_1)" | |
4639 | -DETAIL: Relation name "t_1" is duplicated. | |
4640 | 4653 | LOG: pg_hint_plan: |
4641 | 4654 | used hint: |
4655 | +IndexScan(t_1) | |
4642 | 4656 | not used hint: |
4643 | 4657 | duplication hint: |
4644 | 4658 | error hint: |
4645 | -HashJoin(t_1 t_1) | |
4646 | 4659 | |
4647 | 4660 | INFO: pg_hint_plan: hint syntax error at or near "HashJoin(t_1 t_1)" |
4648 | 4661 | DETAIL: Relation name "t_1" is duplicated. |
@@ -4671,14 +4684,11 @@ EXPLAIN (COSTS false) | ||
4671 | 4684 | ORDER BY t_1.c1; |
4672 | 4685 | INFO: pg_hint_plan: hint syntax error at or near "MergeJoin(t_1 t_2)HashJoin(t_1 t_2)" |
4673 | 4686 | DETAIL: Conflict join method hint. |
4674 | -INFO: pg_hint_plan: hint syntax error at or near "MergeJoin(t_1 t_2)HashJoin(t_1 t_2)" | |
4675 | -DETAIL: Conflict join method hint. | |
4676 | 4687 | LOG: pg_hint_plan: |
4677 | 4688 | used hint: |
4678 | -HashJoin(t_1 t_2) | |
4689 | +IndexScan(t_1) | |
4679 | 4690 | not used hint: |
4680 | 4691 | duplication hint: |
4681 | -MergeJoin(t_1 t_2) | |
4682 | 4692 | error hint: |
4683 | 4693 | |
4684 | 4694 | LOG: pg_hint_plan: |
@@ -4700,3 +4710,87 @@ error hint: | ||
4700 | 4710 | -> Seq Scan on t2 t_2 |
4701 | 4711 | (7 rows) |
4702 | 4712 | |
4713 | +--No.14-1-1 plancache invalidation | |
4714 | +CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a; | |
4715 | +CREATE INDEX ON s1.tpc(a); | |
4716 | +PREPARE p1 AS SELECT * FROM s1.tpc WHERE a < 999; | |
4717 | +/*+ IndexScan(tpc) */PREPARE p2 AS SELECT * FROM s1.tpc WHERE a < 999; | |
4718 | +/*+ SeqScan(tpc) */PREPARE p3(int) AS SELECT * FROM s1.tpc WHERE a = $1; | |
4719 | +EXPLAIN EXECUTE p1; | |
4720 | + QUERY PLAN | |
4721 | +------------------------------------------------------ | |
4722 | + Seq Scan on tpc (cost=0.00..17.50 rows=333 width=4) | |
4723 | + Filter: (a < 999) | |
4724 | +(2 rows) | |
4725 | + | |
4726 | +EXPLAIN EXECUTE p2; | |
4727 | +LOG: pg_hint_plan: | |
4728 | +used hint: | |
4729 | +IndexScan(tpc) | |
4730 | +not used hint: | |
4731 | +duplication hint: | |
4732 | +error hint: | |
4733 | + | |
4734 | + QUERY PLAN | |
4735 | +------------------------------------------------------------------------ | |
4736 | + Index Scan using tpc_a_idx on tpc (cost=0.28..34.10 rows=333 width=4) | |
4737 | + Index Cond: (a < 999) | |
4738 | +(2 rows) | |
4739 | + | |
4740 | +EXPLAIN EXECUTE p3(500); | |
4741 | +LOG: pg_hint_plan: | |
4742 | +used hint: | |
4743 | +SeqScan(tpc) | |
4744 | +not used hint: | |
4745 | +duplication hint: | |
4746 | +error hint: | |
4747 | + | |
4748 | + QUERY PLAN | |
4749 | +---------------------------------------------------- | |
4750 | + Seq Scan on tpc (cost=0.00..17.50 rows=5 width=4) | |
4751 | + Filter: (a = 500) | |
4752 | +(2 rows) | |
4753 | + | |
4754 | +-- The DROP invalidates the plan caches | |
4755 | +DROP TABLE s1.tpc; | |
4756 | +CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a; | |
4757 | +CREATE INDEX ON s1.tpc(a); | |
4758 | +EXPLAIN EXECUTE p1; | |
4759 | + QUERY PLAN | |
4760 | +------------------------------------------------------ | |
4761 | + Seq Scan on tpc (cost=0.00..17.50 rows=333 width=4) | |
4762 | + Filter: (a < 999) | |
4763 | +(2 rows) | |
4764 | + | |
4765 | +EXPLAIN EXECUTE p2; | |
4766 | +LOG: pg_hint_plan: | |
4767 | +used hint: | |
4768 | +IndexScan(tpc) | |
4769 | +not used hint: | |
4770 | +duplication hint: | |
4771 | +error hint: | |
4772 | + | |
4773 | + QUERY PLAN | |
4774 | +------------------------------------------------------------------------ | |
4775 | + Index Scan using tpc_a_idx on tpc (cost=0.28..34.10 rows=333 width=4) | |
4776 | + Index Cond: (a < 999) | |
4777 | +(2 rows) | |
4778 | + | |
4779 | +EXPLAIN EXECUTE p3(500); | |
4780 | +LOG: pg_hint_plan: | |
4781 | +used hint: | |
4782 | +SeqScan(tpc) | |
4783 | +not used hint: | |
4784 | +duplication hint: | |
4785 | +error hint: | |
4786 | + | |
4787 | + QUERY PLAN | |
4788 | +---------------------------------------------------- | |
4789 | + Seq Scan on tpc (cost=0.00..17.50 rows=5 width=4) | |
4790 | + Filter: (a = 500) | |
4791 | +(2 rows) | |
4792 | + | |
4793 | +DEALLOCATE p1; | |
4794 | +DEALLOCATE p2; | |
4795 | +DEALLOCATE p3; | |
4796 | +DROP TABLE s1.tpc; |
@@ -1820,7 +1820,7 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery) | ||
1820 | 1820 | * case of DESCRIBE message handling or EXECUTE command. We may still see a |
1821 | 1821 | * candidate top-level query in pstate in the case. |
1822 | 1822 | */ |
1823 | - if (!p && pstate) | |
1823 | + if (pstate && pstate->p_sourcetext) | |
1824 | 1824 | p = pstate->p_sourcetext; |
1825 | 1825 | |
1826 | 1826 | /* We don't see a query string, return NULL */ |
@@ -1887,13 +1887,24 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery) | ||
1887 | 1887 | PreparedStatement *entry; |
1888 | 1888 | |
1889 | 1889 | entry = FetchPreparedStatement(stmt->name, true); |
1890 | - p = entry->plansource->query_string; | |
1891 | - target_query = (Query *) linitial (entry->plansource->query_list); | |
1890 | + | |
1891 | + if (entry->plansource->is_valid) | |
1892 | + { | |
1893 | + p = entry->plansource->query_string; | |
1894 | + target_query = (Query *) linitial (entry->plansource->query_list); | |
1895 | + } | |
1896 | + else | |
1897 | + { | |
1898 | + /* igonre the hint for EXECUTE if invalidated */ | |
1899 | + p = NULL; | |
1900 | + target_query = NULL; | |
1901 | + } | |
1892 | 1902 | } |
1893 | 1903 | |
1894 | 1904 | /* JumbleQuery accespts only a non-utility Query */ |
1895 | - if (!IsA(target_query, Query) || | |
1896 | - target_query->utilityStmt != NULL) | |
1905 | + if (target_query && | |
1906 | + (!IsA(target_query, Query) || | |
1907 | + target_query->utilityStmt != NULL)) | |
1897 | 1908 | target_query = NULL; |
1898 | 1909 | |
1899 | 1910 | if (jumblequery) |
@@ -2920,6 +2931,14 @@ get_current_hint_string(ParseState *pstate, Query *query) | ||
2920 | 2931 | current_hint_str = get_hints_from_comment(query_str); |
2921 | 2932 | MemoryContextSwitchTo(oldcontext); |
2922 | 2933 | } |
2934 | + else | |
2935 | + { | |
2936 | + /* | |
2937 | + * Failed to get query. We would be in fetching invalidated | |
2938 | + * plancache. Try the next chance. | |
2939 | + */ | |
2940 | + current_hint_retrieved = false; | |
2941 | + } | |
2923 | 2942 | |
2924 | 2943 | if (debug_level > 1) |
2925 | 2944 | { |
@@ -2989,7 +3008,7 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) | ||
2989 | 3008 | int save_nestlevel; |
2990 | 3009 | PlannedStmt *result; |
2991 | 3010 | HintState *hstate; |
2992 | - const char *prev_hint_str; | |
3011 | + const char *prev_hint_str = NULL; | |
2993 | 3012 | |
2994 | 3013 | /* |
2995 | 3014 | * Use standard planner if pg_hint_plan is disabled or current nesting |
@@ -3089,6 +3108,7 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) | ||
3089 | 3108 | */ |
3090 | 3109 | recurse_level++; |
3091 | 3110 | prev_hint_str = current_hint_str; |
3111 | + current_hint_str = NULL; | |
3092 | 3112 | |
3093 | 3113 | /* |
3094 | 3114 | * Use PG_TRY mechanism to recover GUC parameters and current_hint_state to |
@@ -1159,6 +1159,11 @@ EXPLAIN (COSTS false) | ||
1159 | 1159 | ORDER BY t_1.c1; |
1160 | 1160 | |
1161 | 1161 | --No.13-4-2 |
1162 | +-- recall_planner() is reduced to constant while planning using the | |
1163 | +-- hint defined in the function. Then the outer query is planned based | |
1164 | +-- on the following hint. pg_hint_plan shows the log for the function | |
1165 | +-- but the resulting explain output doesn't contain the corresponding | |
1166 | +-- plan. | |
1162 | 1167 | /*+HashJoin(st_1 st_2)*/ |
1163 | 1168 | EXPLAIN (COSTS false) |
1164 | 1169 | SELECT recall_planner() FROM s1.t1 st_1 |
@@ -1166,6 +1171,7 @@ EXPLAIN (COSTS false) | ||
1166 | 1171 | ORDER BY st_1.c1; |
1167 | 1172 | |
1168 | 1173 | --No.13-4-3 |
1174 | +--See description for No.13-4-2 | |
1169 | 1175 | /*+HashJoin(t_1 t_2)*/ |
1170 | 1176 | EXPLAIN (COSTS false) |
1171 | 1177 | SELECT recall_planner() FROM s1.t1 st_1 |
@@ -1173,6 +1179,7 @@ EXPLAIN (COSTS false) | ||
1173 | 1179 | ORDER BY st_1.c1; |
1174 | 1180 | |
1175 | 1181 | --No.13-4-4 |
1182 | +--See description for No.13-4-2 | |
1176 | 1183 | /*+HashJoin(st_1 st_2)*/ |
1177 | 1184 | EXPLAIN (COSTS false) |
1178 | 1185 | SELECT recall_planner() FROM s1.t1 t_1 |
@@ -1180,6 +1187,8 @@ EXPLAIN (COSTS false) | ||
1180 | 1187 | ORDER BY t_1.c1; |
1181 | 1188 | |
1182 | 1189 | --No.13-4-5 |
1190 | +-- See description for No.13-4-2. No joins in ths plan, so | |
1191 | +-- pg_hint_plan doesn't complain on the wrongly written error hint. | |
1183 | 1192 | /*+HashJoin(t_1 t_1)*/ |
1184 | 1193 | EXPLAIN (COSTS false) |
1185 | 1194 | SELECT recall_planner() FROM s1.t1 t_1 |
@@ -1205,6 +1214,7 @@ EXPLAIN (COSTS false) | ||
1205 | 1214 | DROP FUNCTION recall_planner_one_t(int); |
1206 | 1215 | |
1207 | 1216 | --No.13-4-7 |
1217 | +-- See description for No.13-4-2. Complains on the wrongly wrtten hit. | |
1208 | 1218 | /*+HashJoin(t_1 t_1)*/ |
1209 | 1219 | EXPLAIN (COSTS false) |
1210 | 1220 | SELECT recall_planner() FROM s1.t1 t_1 |
@@ -1217,3 +1227,24 @@ EXPLAIN (COSTS false) | ||
1217 | 1227 | SELECT recall_planner() FROM s1.t1 t_1 |
1218 | 1228 | JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) |
1219 | 1229 | ORDER BY t_1.c1; |
1230 | + | |
1231 | +--No.14-1-1 plancache invalidation | |
1232 | +CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a; | |
1233 | +CREATE INDEX ON s1.tpc(a); | |
1234 | +PREPARE p1 AS SELECT * FROM s1.tpc WHERE a < 999; | |
1235 | +/*+ IndexScan(tpc) */PREPARE p2 AS SELECT * FROM s1.tpc WHERE a < 999; | |
1236 | +/*+ SeqScan(tpc) */PREPARE p3(int) AS SELECT * FROM s1.tpc WHERE a = $1; | |
1237 | +EXPLAIN EXECUTE p1; | |
1238 | +EXPLAIN EXECUTE p2; | |
1239 | +EXPLAIN EXECUTE p3(500); | |
1240 | +-- The DROP invalidates the plan caches | |
1241 | +DROP TABLE s1.tpc; | |
1242 | +CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a; | |
1243 | +CREATE INDEX ON s1.tpc(a); | |
1244 | +EXPLAIN EXECUTE p1; | |
1245 | +EXPLAIN EXECUTE p2; | |
1246 | +EXPLAIN EXECUTE p3(500); | |
1247 | +DEALLOCATE p1; | |
1248 | +DEALLOCATE p2; | |
1249 | +DEALLOCATE p3; | |
1250 | +DROP TABLE s1.tpc; |