firtst release
リビジョン | d56ce3df2b4198e6e0fd4c6e6865caead9d48ca9 (tree) |
---|---|
日時 | 2013-12-09 16:35:24 |
作者 | Shigeru HANADA <shigeru.hanada@gmai...> |
コミッター | Shigeru HANADA |
PG92との差分から生成したパッチを適用。
ただし、PG9.3対応として加えた修正はそのまま残した。
@@ -6,7 +6,6 @@ | ||
6 | 6 | * src/backend/optimizer/path/allpaths.c |
7 | 7 | * set_append_rel_pathlist() |
8 | 8 | * generate_mergeappend_paths() |
9 | - * get_cheapest_parameterized_child_path() | |
10 | 9 | * accumulate_append_subpath() |
11 | 10 | * standard_join_search() |
12 | 11 | * |
@@ -37,7 +36,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
37 | 36 | int parentRTindex = rti; |
38 | 37 | List *live_childrels = NIL; |
39 | 38 | List *subpaths = NIL; |
40 | - bool subpaths_valid = true; | |
41 | 39 | List *all_child_pathkeys = NIL; |
42 | 40 | List *all_child_outers = NIL; |
43 | 41 | ListCell *l; |
@@ -77,20 +75,14 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
77 | 75 | continue; |
78 | 76 | |
79 | 77 | /* |
80 | - * Child is live, so add it to the live_childrels list for use below. | |
78 | + * Child is live, so add its cheapest access path to the Append path | |
79 | + * we are constructing for the parent. | |
81 | 80 | */ |
82 | - live_childrels = lappend(live_childrels, childrel); | |
81 | + subpaths = accumulate_append_subpath(subpaths, | |
82 | + childrel->cheapest_total_path); | |
83 | 83 | |
84 | - /* | |
85 | - * If child has an unparameterized cheapest-total path, add that to | |
86 | - * the unparameterized Append path we are constructing for the parent. | |
87 | - * If not, there's no workable unparameterized path. | |
88 | - */ | |
89 | - if (childrel->cheapest_total_path->param_info == NULL) | |
90 | - subpaths = accumulate_append_subpath(subpaths, | |
91 | - childrel->cheapest_total_path); | |
92 | - else | |
93 | - subpaths_valid = false; | |
84 | + /* Remember which childrels are live, for logic below */ | |
85 | + live_childrels = lappend(live_childrels, childrel); | |
94 | 86 | |
95 | 87 | /* |
96 | 88 | * Collect lists of all the available path orderings and |
@@ -158,20 +150,17 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
158 | 150 | } |
159 | 151 | |
160 | 152 | /* |
161 | - * If we found unparameterized paths for all children, build an unordered, | |
162 | - * unparameterized Append path for the rel. (Note: this is correct even | |
163 | - * if we have zero or one live subpath due to constraint exclusion.) | |
153 | + * Next, build an unordered, unparameterized Append path for the rel. | |
154 | + * (Note: this is correct even if we have zero or one live subpath due to | |
155 | + * constraint exclusion.) | |
164 | 156 | */ |
165 | - if (subpaths_valid) | |
166 | - add_path(rel, (Path *) create_append_path(rel, subpaths, NULL)); | |
157 | + add_path(rel, (Path *) create_append_path(rel, subpaths, NULL)); | |
167 | 158 | |
168 | 159 | /* |
169 | - * Also build unparameterized MergeAppend paths based on the collected | |
170 | - * list of child pathkeys. | |
160 | + * Build unparameterized MergeAppend paths based on the collected list of | |
161 | + * child pathkeys. | |
171 | 162 | */ |
172 | - if (subpaths_valid) | |
173 | - generate_mergeappend_paths(root, rel, live_childrels, | |
174 | - all_child_pathkeys); | |
163 | + generate_mergeappend_paths(root, rel, live_childrels, all_child_pathkeys); | |
175 | 164 | |
176 | 165 | /* |
177 | 166 | * Build Append paths for each parameterization seen among the child rels. |
@@ -189,29 +178,39 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
189 | 178 | foreach(l, all_child_outers) |
190 | 179 | { |
191 | 180 | Relids required_outer = (Relids) lfirst(l); |
181 | + bool ok = true; | |
192 | 182 | ListCell *lcr; |
193 | 183 | |
194 | 184 | /* Select the child paths for an Append with this parameterization */ |
195 | 185 | subpaths = NIL; |
196 | - subpaths_valid = true; | |
197 | 186 | foreach(lcr, live_childrels) |
198 | 187 | { |
199 | 188 | RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr); |
200 | - Path *subpath; | |
189 | + Path *cheapest_total; | |
190 | + | |
191 | + cheapest_total = | |
192 | + get_cheapest_path_for_pathkeys(childrel->pathlist, | |
193 | + NIL, | |
194 | + required_outer, | |
195 | + TOTAL_COST); | |
196 | + Assert(cheapest_total != NULL); | |
201 | 197 | |
202 | - subpath = get_cheapest_parameterized_child_path(root, | |
203 | - childrel, | |
204 | - required_outer); | |
205 | - if (subpath == NULL) | |
198 | + /* Children must have exactly the desired parameterization */ | |
199 | + if (!bms_equal(PATH_REQ_OUTER(cheapest_total), required_outer)) | |
206 | 200 | { |
207 | - /* failed to make a suitable path for this child */ | |
208 | - subpaths_valid = false; | |
209 | - break; | |
201 | + cheapest_total = reparameterize_path(root, cheapest_total, | |
202 | + required_outer, 1.0); | |
203 | + if (cheapest_total == NULL) | |
204 | + { | |
205 | + ok = false; | |
206 | + break; | |
207 | + } | |
210 | 208 | } |
211 | - subpaths = accumulate_append_subpath(subpaths, subpath); | |
209 | + | |
210 | + subpaths = accumulate_append_subpath(subpaths, cheapest_total); | |
212 | 211 | } |
213 | 212 | |
214 | - if (subpaths_valid) | |
213 | + if (ok) | |
215 | 214 | add_path(rel, (Path *) |
216 | 215 | create_append_path(rel, subpaths, required_outer)); |
217 | 216 | } |
@@ -285,8 +284,7 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, | ||
285 | 284 | { |
286 | 285 | cheapest_startup = cheapest_total = |
287 | 286 | childrel->cheapest_total_path; |
288 | - /* Assert we do have an unparameterized path for this child */ | |
289 | - Assert(cheapest_total->param_info == NULL); | |
287 | + Assert(cheapest_total != NULL); | |
290 | 288 | } |
291 | 289 | |
292 | 290 | /* |
@@ -319,79 +317,6 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, | ||
319 | 317 | } |
320 | 318 | |
321 | 319 | /* |
322 | - * get_cheapest_parameterized_child_path | |
323 | - * Get cheapest path for this relation that has exactly the requested | |
324 | - * parameterization. | |
325 | - * | |
326 | - * Returns NULL if unable to create such a path. | |
327 | - */ | |
328 | -static Path * | |
329 | -get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel, | |
330 | - Relids required_outer) | |
331 | -{ | |
332 | - Path *cheapest; | |
333 | - ListCell *lc; | |
334 | - | |
335 | - /* | |
336 | - * Look up the cheapest existing path with no more than the needed | |
337 | - * parameterization. If it has exactly the needed parameterization, we're | |
338 | - * done. | |
339 | - */ | |
340 | - cheapest = get_cheapest_path_for_pathkeys(rel->pathlist, | |
341 | - NIL, | |
342 | - required_outer, | |
343 | - TOTAL_COST); | |
344 | - Assert(cheapest != NULL); | |
345 | - if (bms_equal(PATH_REQ_OUTER(cheapest), required_outer)) | |
346 | - return cheapest; | |
347 | - | |
348 | - /* | |
349 | - * Otherwise, we can "reparameterize" an existing path to match the given | |
350 | - * parameterization, which effectively means pushing down additional | |
351 | - * joinquals to be checked within the path's scan. However, some existing | |
352 | - * paths might check the available joinquals already while others don't; | |
353 | - * therefore, it's not clear which existing path will be cheapest after | |
354 | - * reparameterization. We have to go through them all and find out. | |
355 | - */ | |
356 | - cheapest = NULL; | |
357 | - foreach(lc, rel->pathlist) | |
358 | - { | |
359 | - Path *path = (Path *) lfirst(lc); | |
360 | - | |
361 | - /* Can't use it if it needs more than requested parameterization */ | |
362 | - if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer)) | |
363 | - continue; | |
364 | - | |
365 | - /* | |
366 | - * Reparameterization can only increase the path's cost, so if it's | |
367 | - * already more expensive than the current cheapest, forget it. | |
368 | - */ | |
369 | - if (cheapest != NULL && | |
370 | - compare_path_costs(cheapest, path, TOTAL_COST) <= 0) | |
371 | - continue; | |
372 | - | |
373 | - /* Reparameterize if needed, then recheck cost */ | |
374 | - if (!bms_equal(PATH_REQ_OUTER(path), required_outer)) | |
375 | - { | |
376 | - path = reparameterize_path(root, path, required_outer, 1.0); | |
377 | - if (path == NULL) | |
378 | - continue; /* failed to reparameterize this one */ | |
379 | - Assert(bms_equal(PATH_REQ_OUTER(path), required_outer)); | |
380 | - | |
381 | - if (cheapest != NULL && | |
382 | - compare_path_costs(cheapest, path, TOTAL_COST) <= 0) | |
383 | - continue; | |
384 | - } | |
385 | - | |
386 | - /* We have a new best path */ | |
387 | - cheapest = path; | |
388 | - } | |
389 | - | |
390 | - /* Return the best path, or NULL if we found no suitable candidate */ | |
391 | - return cheapest; | |
392 | -} | |
393 | - | |
394 | -/* | |
395 | 320 | * accumulate_append_subpath |
396 | 321 | * Add a subpath to the list being built for an Append or MergeAppend |
397 | 322 | * |
@@ -701,14 +626,11 @@ join_search_one_level(PlannerInfo *root, int level) | ||
701 | 626 | * to accept failure at level 4 and go on to discover a workable |
702 | 627 | * bushy plan at level 5. |
703 | 628 | * |
704 | - * However, if there are no special joins and no lateral references | |
705 | - * then join_is_legal() should never fail, and so the following sanity | |
706 | - * check is useful. | |
629 | + * However, if there are no special joins then join_is_legal() should | |
630 | + * never fail, and so the following sanity check is useful. | |
707 | 631 | *---------- |
708 | 632 | */ |
709 | - if (joinrels[level] == NIL && | |
710 | - root->join_info_list == NIL && | |
711 | - root->lateral_info_list == NIL) | |
633 | + if (joinrels[level] == NIL && root->join_info_list == NIL) | |
712 | 634 | elog(ERROR, "failed to build any %d-way joins", level); |
713 | 635 | } |
714 | 636 | } |
@@ -808,8 +730,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, | ||
808 | 730 | bool reversed; |
809 | 731 | bool unique_ified; |
810 | 732 | bool is_valid_inner; |
811 | - bool lateral_fwd; | |
812 | - bool lateral_rev; | |
813 | 733 | ListCell *l; |
814 | 734 | |
815 | 735 | /* |
@@ -989,47 +909,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, | ||
989 | 909 | (match_sjinfo == NULL || unique_ified)) |
990 | 910 | return false; /* invalid join path */ |
991 | 911 | |
992 | - /* | |
993 | - * We also have to check for constraints imposed by LATERAL references. | |
994 | - * The proposed rels could each contain lateral references to the other, | |
995 | - * in which case the join is impossible. If there are lateral references | |
996 | - * in just one direction, then the join has to be done with a nestloop | |
997 | - * with the lateral referencer on the inside. If the join matches an SJ | |
998 | - * that cannot be implemented by such a nestloop, the join is impossible. | |
999 | - */ | |
1000 | - lateral_fwd = lateral_rev = false; | |
1001 | - foreach(l, root->lateral_info_list) | |
1002 | - { | |
1003 | - LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); | |
1004 | - | |
1005 | - if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) && | |
1006 | - bms_overlap(ljinfo->lateral_lhs, rel1->relids)) | |
1007 | - { | |
1008 | - /* has to be implemented as nestloop with rel1 on left */ | |
1009 | - if (lateral_rev) | |
1010 | - return false; /* have lateral refs in both directions */ | |
1011 | - lateral_fwd = true; | |
1012 | - if (!bms_is_subset(ljinfo->lateral_lhs, rel1->relids)) | |
1013 | - return false; /* rel1 can't compute the required parameter */ | |
1014 | - if (match_sjinfo && | |
1015 | - (reversed || match_sjinfo->jointype == JOIN_FULL)) | |
1016 | - return false; /* not implementable as nestloop */ | |
1017 | - } | |
1018 | - if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) && | |
1019 | - bms_overlap(ljinfo->lateral_lhs, rel2->relids)) | |
1020 | - { | |
1021 | - /* has to be implemented as nestloop with rel2 on left */ | |
1022 | - if (lateral_fwd) | |
1023 | - return false; /* have lateral refs in both directions */ | |
1024 | - lateral_rev = true; | |
1025 | - if (!bms_is_subset(ljinfo->lateral_lhs, rel2->relids)) | |
1026 | - return false; /* rel2 can't compute the required parameter */ | |
1027 | - if (match_sjinfo && | |
1028 | - (!reversed || match_sjinfo->jointype == JOIN_FULL)) | |
1029 | - return false; /* not implementable as nestloop */ | |
1030 | - } | |
1031 | - } | |
1032 | - | |
1033 | 912 | /* Otherwise, it's a valid join */ |
1034 | 913 | *sjinfo_p = match_sjinfo; |
1035 | 914 | *reversed_p = reversed; |
@@ -1038,9 +917,8 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, | ||
1038 | 917 | |
1039 | 918 | /* |
1040 | 919 | * has_join_restriction |
1041 | - * Detect whether the specified relation has join-order restrictions, | |
1042 | - * due to being inside an outer join or an IN (sub-SELECT), | |
1043 | - * or participating in any LATERAL references. | |
920 | + * Detect whether the specified relation has join-order restrictions | |
921 | + * due to being inside an outer join or an IN (sub-SELECT). | |
1044 | 922 | * |
1045 | 923 | * Essentially, this tests whether have_join_order_restriction() could |
1046 | 924 | * succeed with this rel and some other one. It's OK if we sometimes |
@@ -1052,15 +930,6 @@ has_join_restriction(PlannerInfo *root, RelOptInfo *rel) | ||
1052 | 930 | { |
1053 | 931 | ListCell *l; |
1054 | 932 | |
1055 | - foreach(l, root->lateral_info_list) | |
1056 | - { | |
1057 | - LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); | |
1058 | - | |
1059 | - if (bms_is_subset(ljinfo->lateral_rhs, rel->relids) || | |
1060 | - bms_overlap(ljinfo->lateral_lhs, rel->relids)) | |
1061 | - return true; | |
1062 | - } | |
1063 | - | |
1064 | 933 | foreach(l, root->join_info_list) |
1065 | 934 | { |
1066 | 935 | SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l); |
@@ -74,6 +74,17 @@ PostgreSQL 9.2以降で動作します。</td></tr> | ||
74 | 74 | <tr><td nowrap>Leading((テーブル集合<Sup>注1</Sup> テーブル集合<Sup>注1</Sup>))</td> |
75 | 75 | <td>1つ目に指定したテーブル集合を外部表として、2つ目に指定したテーブル集合を内部表として結合します。</td></tr> |
76 | 76 | |
77 | +<tr><td>見積もり件数補正</td> | |
78 | + <td nowrap>Rows(テーブル テーブル[ テーブル...] 件数補正)</td> | |
79 | + <td>指定したテーブル間の結合結果の見積もり件数を補正します。件数補正として指定できるのは以下の4パターンです。 | |
80 | + <p><dl> | |
81 | + <dt>#件数</dt><dd>指定した件数で本来の見積もり件を上書きします。</dd> | |
82 | + <dt>+件数</dt><dd>指定した件数を本来の見積もり件数に加算します。</dd> | |
83 | + <dt>-件数</dt><dd>指定した件数を本来の見積もり件数から減算します。</dd> | |
84 | + <dt>*倍率</dt><dd>本来の見積もり件数を指定した倍率に補正します。「*2」とすると見積もり件数が2倍になり、「*0.01」とすると見積もり件数が1/100になります。</dd> | |
85 | + </dl></p> | |
86 | +</td></tr> | |
87 | + | |
77 | 88 | <tr><td>GUCパラメータ</td> |
78 | 89 | <td nowrap>Set(GUCパラメータ 値)</td> |
79 | 90 | <td>そのクエリの実行計画を作成している間だけ、指定したGUCパラメータを指定した値に変更します。</td></tr> |
@@ -240,7 +240,7 @@ postgres-# ORDER BY a.aid; | ||
240 | 240 | </div> |
241 | 241 | |
242 | 242 | <h3 id="hint-group">ヒント句のグループ</h3> |
243 | -<p>pg_hint_planで使えるヒント句の種類は、スキャン方式と結合方式、結合順序、GUCパラメータの4グループです。各グループの具体的なヒント句は、<a href="hint_list-ja.html">ヒント句一覧</a>を参照してください。</p> | |
243 | +<p>pg_hint_planで使えるヒント句の種類は、スキャン方式と結合方式、結合順序、見積もり件数補正、GUCパラメータの5グループです。各グループの具体的なヒント句は、<a href="hint_list-ja.html">ヒント句一覧</a>を参照してください。</p> | |
244 | 244 | |
245 | 245 | <h4>スキャン方式</h4> |
246 | 246 | <p>あるオブジェクトでどのスキャン方式を選択するかを指定できるヒント句のグループです。「SeqScan」や「IndexScan」などを含みます。</p> |
@@ -273,6 +273,10 @@ postgres-# JOIN table table3 t3 ON (t2.key = t3.key); | ||
273 | 273 | </pre> |
274 | 274 | </div> |
275 | 275 | |
276 | +<h4>見積もり件数補正</h4> | |
277 | +<p>あるオブジェクトの結合結果の件数を補正できるヒント句のグループです。「Rows」のみを含みます。</p> | |
278 | +<p>見積もり件数補正対象として指定できるオブジェクトは結合方式と同じです。補正できるのは結合結果の見積もり件数だけで、スキャンの見積もり件数を補正することはできません。</p> | |
279 | + | |
276 | 280 | <h4>GUCパラメータ</h4> |
277 | 281 | <p>そのクエリの実行計画を作成している間だけGUCパラメータを変更できるヒント句のグループです。「Set」のみを含みます。</p> |
278 | 282 | <p>設定したいGUCパラメータとそのパラメータの値を指定します。指定できるGUCパラメータは<a href="http://www.postgresql.jp/document/current/html/runtime-config-query.html">問い合わせ計画</a>のGUCパラメータのみです。同じGUCパラメータのヒント句を2回以上指定した場合は、最後に指定したヒント句が適用されます。</p> |
@@ -6,7 +6,8 @@ | ||
6 | 6 | * src/backend/optimizer/path/joinrels.c |
7 | 7 | * make_join_rel() |
8 | 8 | * |
9 | - * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group | |
9 | + * Portions Copyright (c) 2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION | |
10 | + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group | |
10 | 11 | * Portions Copyright (c) 1994, Regents of the University of California |
11 | 12 | * |
12 | 13 | *------------------------------------------------------------------------- |
@@ -24,6 +25,29 @@ | ||
24 | 25 | * when working with outer joins, or with IN or EXISTS clauses that have been |
25 | 26 | * turned into joins. |
26 | 27 | */ |
28 | +static double | |
29 | +adjust_rows(double rows, RowsHint *hint) | |
30 | +{ | |
31 | + double result = 0.0; /* keep compiler quiet */ | |
32 | + | |
33 | + if (hint->value_type == RVT_ABSOLUTE) | |
34 | + result = hint->rows; | |
35 | + else if (hint->value_type == RVT_ADD) | |
36 | + result = rows + hint->rows; | |
37 | + else if (hint->value_type == RVT_SUB) | |
38 | + result = rows - hint->rows; | |
39 | + else if (hint->value_type == RVT_MULTI) | |
40 | + result = rows * hint->rows; | |
41 | + else | |
42 | + Assert(false); /* unrecognized rows value type */ | |
43 | + | |
44 | + hint->base.state = HINT_STATE_USED; | |
45 | + result = clamp_row_est(result); | |
46 | + elog(DEBUG1, "adjusted rows %d to %d", (int) rows, (int) result); | |
47 | + | |
48 | + return result; | |
49 | +} | |
50 | + | |
27 | 51 | RelOptInfo * |
28 | 52 | make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) |
29 | 53 | { |
@@ -34,6 +58,9 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) | ||
34 | 58 | RelOptInfo *joinrel; |
35 | 59 | List *restrictlist; |
36 | 60 | |
61 | + RowsHint *rows_hint = NULL; | |
62 | + int i; | |
63 | + | |
37 | 64 | /* We should never try to join two overlapping sets of rels. */ |
38 | 65 | Assert(!bms_overlap(rel1->relids, rel2->relids)); |
39 | 66 |
@@ -85,6 +112,54 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) | ||
85 | 112 | joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo, |
86 | 113 | &restrictlist); |
87 | 114 | |
115 | + /* Apply appropriate Rows hint to the join node, if any. */ | |
116 | + for (i = 0; i < current_hint->num_hints[HINT_TYPE_ROWS]; i++) | |
117 | + { | |
118 | + rows_hint = current_hint->rows_hints[i]; | |
119 | + | |
120 | + if (bms_equal(joinrelids, rows_hint->joinrelids)) | |
121 | + { | |
122 | + /* | |
123 | + * This join RelOptInfo is exactly a Rows hint specifies, so adjust | |
124 | + * rows estimateion with the hint's content. Here we never have | |
125 | + * another hint which has same relation combination, so we can skip | |
126 | + * rest of hints. | |
127 | + */ | |
128 | + if (rows_hint->base.state == HINT_STATE_NOTUSED) | |
129 | + joinrel->rows = adjust_rows(joinrel->rows, rows_hint); | |
130 | + } | |
131 | + else if (bms_is_subset(rows_hint->joinrelids, rel1->relids) || | |
132 | + bms_is_subset(rows_hint->joinrelids, rel2->relids)) | |
133 | + { | |
134 | + /* | |
135 | + * Otherwise if the relation combination specified in thee Rows | |
136 | + * hint is subset of the set of join elements, re-estimate rows and | |
137 | + * costs again to reflect the adjustment done in down. This is | |
138 | + * necessary for the first permutation of the combination the | |
139 | + * relations, but it's difficult to determine that this is the | |
140 | + * first, so do this everytime. | |
141 | + */ | |
142 | + set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo, | |
143 | + restrictlist); | |
144 | + } | |
145 | + else if (bms_is_subset(rows_hint->joinrelids, joinrelids)) | |
146 | + { | |
147 | + /* | |
148 | + * If the combination specifed in the Rows hints is subset of the | |
149 | + * join relation and spreads over both children, | |
150 | + * | |
151 | + * We do adjust rows estimation only when the value type was | |
152 | + * multiplication, because other value types are meanless. | |
153 | + */ | |
154 | + if (rows_hint->value_type == RVT_MULTI) | |
155 | + { | |
156 | + set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo, | |
157 | + restrictlist); | |
158 | + joinrel->rows = adjust_rows(joinrel->rows, rows_hint); | |
159 | + } | |
160 | + } | |
161 | + } | |
162 | + | |
88 | 163 | /* |
89 | 164 | * If we've already proven this join is empty, we needn't consider any |
90 | 165 | * more paths for it. |
@@ -37,6 +37,7 @@ | ||
37 | 37 | |
38 | 38 | #include "executor/spi.h" |
39 | 39 | #include "catalog/pg_type.h" |
40 | + | |
40 | 41 | /* |
41 | 42 | * We have our own header file "plpgsql-9.1", which is necessary to support |
42 | 43 | * hints for queries in PL/pgSQL blocks, in pg_hint_plan source package, |
@@ -87,6 +88,7 @@ PG_MODULE_MAGIC; | ||
87 | 88 | #define HINT_NOHASHJOIN "NoHashJoin" |
88 | 89 | #define HINT_LEADING "Leading" |
89 | 90 | #define HINT_SET "Set" |
91 | +#define HINT_ROWS "Rows" | |
90 | 92 | |
91 | 93 | #define HINT_ARRAY_DEFAULT_INITSIZE 8 |
92 | 94 |
@@ -146,6 +148,7 @@ typedef enum HintKeyword | ||
146 | 148 | HINT_KEYWORD_NOHASHJOIN, |
147 | 149 | HINT_KEYWORD_LEADING, |
148 | 150 | HINT_KEYWORD_SET, |
151 | + HINT_KEYWORD_ROWS, | |
149 | 152 | HINT_KEYWORD_UNRECOGNIZED |
150 | 153 | } HintKeyword; |
151 | 154 |
@@ -162,20 +165,22 @@ typedef const char *(*HintParseFunction) (Hint *hint, HintState *hstate, | ||
162 | 165 | Query *parse, const char *str); |
163 | 166 | |
164 | 167 | /* hint types */ |
165 | -#define NUM_HINT_TYPE 4 | |
168 | +#define NUM_HINT_TYPE 5 | |
166 | 169 | typedef enum HintType |
167 | 170 | { |
168 | 171 | HINT_TYPE_SCAN_METHOD, |
169 | 172 | HINT_TYPE_JOIN_METHOD, |
170 | 173 | HINT_TYPE_LEADING, |
171 | - HINT_TYPE_SET | |
174 | + HINT_TYPE_SET, | |
175 | + HINT_TYPE_ROWS, | |
172 | 176 | } HintType; |
173 | 177 | |
174 | 178 | static const char *HintTypeName[] = { |
175 | 179 | "scan method", |
176 | 180 | "join method", |
177 | 181 | "leading", |
178 | - "set" | |
182 | + "set", | |
183 | + "rows", | |
179 | 184 | }; |
180 | 185 | |
181 | 186 | /* hint status */ |
@@ -262,6 +267,26 @@ typedef struct SetHint | ||
262 | 267 | List *words; |
263 | 268 | } SetHint; |
264 | 269 | |
270 | +/* rows hints */ | |
271 | +typedef enum RowsValueType { | |
272 | + RVT_ABSOLUTE, /* Rows(... #1000) */ | |
273 | + RVT_ADD, /* Rows(... +1000) */ | |
274 | + RVT_SUB, /* Rows(... -1000) */ | |
275 | + RVT_MULTI, /* Rows(... *1.2) */ | |
276 | +} RowsValueType; | |
277 | +typedef struct RowsHint | |
278 | +{ | |
279 | + Hint base; | |
280 | + int nrels; | |
281 | + int inner_nrels; | |
282 | + char **relnames; | |
283 | + Relids joinrelids; | |
284 | + Relids inner_joinrelids; | |
285 | + char *rows_str; | |
286 | + RowsValueType value_type; | |
287 | + double rows; | |
288 | +} RowsHint; | |
289 | + | |
265 | 290 | /* |
266 | 291 | * Describes a context of hint processing. |
267 | 292 | */ |
@@ -297,6 +322,9 @@ struct HintState | ||
297 | 322 | /* for Set hints */ |
298 | 323 | SetHint **set_hints; /* parsed Set hints */ |
299 | 324 | GucContext context; /* which GUC parameters can we set? */ |
325 | + | |
326 | + /* for Rows hints */ | |
327 | + RowsHint **rows_hints; /* parsed Rows hints */ | |
300 | 328 | }; |
301 | 329 | |
302 | 330 | /* |
@@ -358,6 +386,15 @@ static void SetHintDesc(SetHint *hint, StringInfo buf); | ||
358 | 386 | static int SetHintCmp(const SetHint *a, const SetHint *b); |
359 | 387 | static const char *SetHintParse(SetHint *hint, HintState *hstate, Query *parse, |
360 | 388 | const char *str); |
389 | +static Hint *RowsHintCreate(const char *hint_str, const char *keyword, | |
390 | + HintKeyword hint_keyword); | |
391 | +static void RowsHintDelete(RowsHint *hint); | |
392 | +static void RowsHintDesc(RowsHint *hint, StringInfo buf); | |
393 | +static int RowsHintCmp(const RowsHint *a, const RowsHint *b); | |
394 | +static const char *RowsHintParse(RowsHint *hint, HintState *hstate, | |
395 | + Query *parse, const char *str); | |
396 | +static Hint *LeadingHintCreate(const char *hint_str, const char *keyword, | |
397 | + HintKeyword hint_keyword); | |
361 | 398 | |
362 | 399 | static void quote_value(StringInfo buf, const char *value); |
363 | 400 |
@@ -379,9 +416,6 @@ static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
379 | 416 | static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, |
380 | 417 | List *live_childrels, |
381 | 418 | List *all_child_pathkeys); |
382 | -static Path *get_cheapest_parameterized_child_path(PlannerInfo *root, | |
383 | - RelOptInfo *rel, | |
384 | - Relids required_outer); | |
385 | 419 | static List *accumulate_append_subpath(List *subpaths, Path *path); |
386 | 420 | RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, |
387 | 421 | RelOptInfo *rel2); |
@@ -462,6 +496,7 @@ static const HintParser parsers[] = { | ||
462 | 496 | {HINT_NOHASHJOIN, JoinMethodHintCreate, HINT_KEYWORD_NOHASHJOIN}, |
463 | 497 | {HINT_LEADING, LeadingHintCreate, HINT_KEYWORD_LEADING}, |
464 | 498 | {HINT_SET, SetHintCreate, HINT_KEYWORD_SET}, |
499 | + {HINT_ROWS, RowsHintCreate, HINT_KEYWORD_ROWS}, | |
465 | 500 | {NULL, NULL, HINT_KEYWORD_UNRECOGNIZED} |
466 | 501 | }; |
467 | 502 |
@@ -730,6 +765,54 @@ SetHintDelete(SetHint *hint) | ||
730 | 765 | pfree(hint); |
731 | 766 | } |
732 | 767 | |
768 | +static Hint * | |
769 | +RowsHintCreate(const char *hint_str, const char *keyword, | |
770 | + HintKeyword hint_keyword) | |
771 | +{ | |
772 | + RowsHint *hint; | |
773 | + | |
774 | + hint = palloc(sizeof(RowsHint)); | |
775 | + hint->base.hint_str = hint_str; | |
776 | + hint->base.keyword = keyword; | |
777 | + hint->base.hint_keyword = hint_keyword; | |
778 | + hint->base.type = HINT_TYPE_ROWS; | |
779 | + hint->base.state = HINT_STATE_NOTUSED; | |
780 | + hint->base.delete_func = (HintDeleteFunction) RowsHintDelete; | |
781 | + hint->base.desc_func = (HintDescFunction) RowsHintDesc; | |
782 | + hint->base.cmp_func = (HintCmpFunction) RowsHintCmp; | |
783 | + hint->base.parse_func = (HintParseFunction) RowsHintParse; | |
784 | + hint->nrels = 0; | |
785 | + hint->inner_nrels = 0; | |
786 | + hint->relnames = NULL; | |
787 | + hint->joinrelids = NULL; | |
788 | + hint->inner_joinrelids = NULL; | |
789 | + hint->rows_str = NULL; | |
790 | + hint->value_type = RVT_ABSOLUTE; | |
791 | + hint->rows = 0; | |
792 | + | |
793 | + return (Hint *) hint; | |
794 | +} | |
795 | + | |
796 | +static void | |
797 | +RowsHintDelete(RowsHint *hint) | |
798 | +{ | |
799 | + if (!hint) | |
800 | + return; | |
801 | + | |
802 | + if (hint->relnames) | |
803 | + { | |
804 | + int i; | |
805 | + | |
806 | + for (i = 0; i < hint->nrels; i++) | |
807 | + pfree(hint->relnames[i]); | |
808 | + pfree(hint->relnames); | |
809 | + } | |
810 | + | |
811 | + bms_free(hint->joinrelids); | |
812 | + bms_free(hint->inner_joinrelids); | |
813 | + pfree(hint); | |
814 | +} | |
815 | + | |
733 | 816 | static HintState * |
734 | 817 | HintStateCreate(void) |
735 | 818 | { |
@@ -753,6 +836,7 @@ HintStateCreate(void) | ||
753 | 836 | hstate->leading_hint = NULL; |
754 | 837 | hstate->context = superuser() ? PGC_SUSET : PGC_USERSET; |
755 | 838 | hstate->set_hints = NULL; |
839 | + hstate->rows_hints = NULL; | |
756 | 840 | |
757 | 841 | return hstate; |
758 | 842 | } |
@@ -917,6 +1001,26 @@ SetHintDesc(SetHint *hint, StringInfo buf) | ||
917 | 1001 | appendStringInfo(buf, ")\n"); |
918 | 1002 | } |
919 | 1003 | |
1004 | +static void | |
1005 | +RowsHintDesc(RowsHint *hint, StringInfo buf) | |
1006 | +{ | |
1007 | + int i; | |
1008 | + | |
1009 | + appendStringInfo(buf, "%s(", hint->base.keyword); | |
1010 | + if (hint->relnames != NULL) | |
1011 | + { | |
1012 | + quote_value(buf, hint->relnames[0]); | |
1013 | + for (i = 1; i < hint->nrels; i++) | |
1014 | + { | |
1015 | + appendStringInfoCharMacro(buf, ' '); | |
1016 | + quote_value(buf, hint->relnames[i]); | |
1017 | + } | |
1018 | + } | |
1019 | + appendStringInfo(buf, " %s", hint->rows_str); | |
1020 | + appendStringInfoString(buf, ")\n"); | |
1021 | + | |
1022 | +} | |
1023 | + | |
920 | 1024 | /* |
921 | 1025 | * Append string which represents all hints in a given state to buf, with |
922 | 1026 | * preceding title with them. |
@@ -1014,6 +1118,24 @@ SetHintCmp(const SetHint *a, const SetHint *b) | ||
1014 | 1118 | } |
1015 | 1119 | |
1016 | 1120 | static int |
1121 | +RowsHintCmp(const RowsHint *a, const RowsHint *b) | |
1122 | +{ | |
1123 | + int i; | |
1124 | + | |
1125 | + if (a->nrels != b->nrels) | |
1126 | + return a->nrels - b->nrels; | |
1127 | + | |
1128 | + for (i = 0; i < a->nrels; i++) | |
1129 | + { | |
1130 | + int result; | |
1131 | + if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0) | |
1132 | + return result; | |
1133 | + } | |
1134 | + | |
1135 | + return 0; | |
1136 | +} | |
1137 | + | |
1138 | +static int | |
1017 | 1139 | HintCmp(const void *a, const void *b) |
1018 | 1140 | { |
1019 | 1141 | const Hint *hinta = *((const Hint **) a); |
@@ -1612,6 +1734,8 @@ create_hintstate(Query *parse, const char *hints) | ||
1612 | 1734 | hstate->num_hints[HINT_TYPE_JOIN_METHOD]); |
1613 | 1735 | hstate->set_hints = (SetHint **) (hstate->leading_hint + |
1614 | 1736 | hstate->num_hints[HINT_TYPE_LEADING]); |
1737 | + hstate->rows_hints = (RowsHint **) (hstate->set_hints + | |
1738 | + hstate->num_hints[HINT_TYPE_SET]); | |
1615 | 1739 | |
1616 | 1740 | return hstate; |
1617 | 1741 | } |
@@ -1903,6 +2027,97 @@ SetHintParse(SetHint *hint, HintState *hstate, Query *parse, const char *str) | ||
1903 | 2027 | return str; |
1904 | 2028 | } |
1905 | 2029 | |
2030 | +static const char * | |
2031 | +RowsHintParse(RowsHint *hint, HintState *hstate, Query *parse, | |
2032 | + const char *str) | |
2033 | +{ | |
2034 | + HintKeyword hint_keyword = hint->base.hint_keyword; | |
2035 | + List *name_list = NIL; | |
2036 | + char *rows_str; | |
2037 | + char *end_ptr; | |
2038 | + | |
2039 | + if ((str = parse_parentheses(str, &name_list, hint_keyword)) == NULL) | |
2040 | + return NULL; | |
2041 | + | |
2042 | + /* Last element must be rows specification */ | |
2043 | + hint->nrels = list_length(name_list) - 1; | |
2044 | + | |
2045 | + if (hint->nrels > 0) | |
2046 | + { | |
2047 | + ListCell *l; | |
2048 | + int i = 0; | |
2049 | + | |
2050 | + /* | |
2051 | + * Transform relation names from list to array to sort them with qsort | |
2052 | + * after. | |
2053 | + */ | |
2054 | + hint->relnames = palloc(sizeof(char *) * hint->nrels); | |
2055 | + foreach (l, name_list) | |
2056 | + { | |
2057 | + if (hint->nrels <= i) | |
2058 | + break; | |
2059 | + hint->relnames[i] = lfirst(l); | |
2060 | + i++; | |
2061 | + } | |
2062 | + } | |
2063 | + | |
2064 | + /* Retieve rows estimation */ | |
2065 | + rows_str = list_nth(name_list, hint->nrels); | |
2066 | + hint->rows_str = rows_str; /* store as-is for error logging */ | |
2067 | + if (rows_str[0] == '#') | |
2068 | + { | |
2069 | + hint->value_type = RVT_ABSOLUTE; | |
2070 | + rows_str++; | |
2071 | + } | |
2072 | + else if (rows_str[0] == '+') | |
2073 | + { | |
2074 | + hint->value_type = RVT_ADD; | |
2075 | + rows_str++; | |
2076 | + } | |
2077 | + else if (rows_str[0] == '-') | |
2078 | + { | |
2079 | + hint->value_type = RVT_SUB; | |
2080 | + rows_str++; | |
2081 | + } | |
2082 | + else if (rows_str[0] == '*') | |
2083 | + { | |
2084 | + hint->value_type = RVT_MULTI; | |
2085 | + rows_str++; | |
2086 | + } | |
2087 | + else | |
2088 | + { | |
2089 | + hint_ereport(rows_str, ("unrecognized rows value type notation.")); | |
2090 | + hint->base.state = HINT_STATE_ERROR; | |
2091 | + return str; | |
2092 | + } | |
2093 | + hint->rows = strtod(rows_str, &end_ptr); | |
2094 | + if (*end_ptr) | |
2095 | + { | |
2096 | + hint_ereport(rows_str, | |
2097 | + ("%s hint requires valid number as rows estimation.", | |
2098 | + hint->base.keyword)); | |
2099 | + hint->base.state = HINT_STATE_ERROR; | |
2100 | + return str; | |
2101 | + } | |
2102 | + | |
2103 | + /* A join hint requires at least two relations */ | |
2104 | + if (hint->nrels < 2) | |
2105 | + { | |
2106 | + hint_ereport(str, | |
2107 | + ("%s hint requires at least two relations.", | |
2108 | + hint->base.keyword)); | |
2109 | + hint->base.state = HINT_STATE_ERROR; | |
2110 | + return str; | |
2111 | + } | |
2112 | + | |
2113 | + list_free(name_list); | |
2114 | + | |
2115 | + /* Sort relnames in alphabetical order. */ | |
2116 | + qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp); | |
2117 | + | |
2118 | + return str; | |
2119 | +} | |
2120 | + | |
1906 | 2121 | /* |
1907 | 2122 | * set GUC parameter functions |
1908 | 2123 | */ |
@@ -2973,6 +3188,40 @@ OuterInnerJoinCreate(OuterInnerRels *outer_inner, LeadingHint *leading_hint, | ||
2973 | 3188 | return join_relids; |
2974 | 3189 | } |
2975 | 3190 | |
3191 | +static Relids | |
3192 | +create_bms_of_relids(Hint *base, PlannerInfo *root, List *initial_rels, | |
3193 | + int nrels, char **relnames) | |
3194 | +{ | |
3195 | + int relid; | |
3196 | + Relids relids = NULL; | |
3197 | + int j; | |
3198 | + char *relname; | |
3199 | + | |
3200 | + for (j = 0; j < nrels; j++) | |
3201 | + { | |
3202 | + relname = relnames[j]; | |
3203 | + | |
3204 | + relid = find_relid_aliasname(root, relname, initial_rels, | |
3205 | + base->hint_str); | |
3206 | + | |
3207 | + if (relid == -1) | |
3208 | + base->state = HINT_STATE_ERROR; | |
3209 | + | |
3210 | + if (relid <= 0) | |
3211 | + break; | |
3212 | + | |
3213 | + if (bms_is_member(relid, relids)) | |
3214 | + { | |
3215 | + hint_ereport(base->hint_str, | |
3216 | + ("Relation name \"%s\" is duplicated.", relname)); | |
3217 | + base->state = HINT_STATE_ERROR; | |
3218 | + break; | |
3219 | + } | |
3220 | + | |
3221 | + relids = bms_add_member(relids, relid); | |
3222 | + } | |
3223 | + return relids; | |
3224 | +} | |
2976 | 3225 | /* |
2977 | 3226 | * Transform join method hint into handy form. |
2978 | 3227 | * |
@@ -3000,43 +3249,36 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel, | ||
3000 | 3249 | for (i = 0; i < hstate->num_hints[HINT_TYPE_JOIN_METHOD]; i++) |
3001 | 3250 | { |
3002 | 3251 | JoinMethodHint *hint = hstate->join_hints[i]; |
3003 | - int j; | |
3004 | 3252 | |
3005 | 3253 | if (!hint_state_enabled(hint) || hint->nrels > nbaserel) |
3006 | 3254 | continue; |
3007 | 3255 | |
3008 | - bms_free(hint->joinrelids); | |
3009 | - hint->joinrelids = NULL; | |
3010 | - relid = 0; | |
3011 | - for (j = 0; j < hint->nrels; j++) | |
3012 | - { | |
3013 | - relname = hint->relnames[j]; | |
3256 | + hint->joinrelids = create_bms_of_relids(&(hint->base), root, | |
3257 | + initial_rels, hint->nrels, hint->relnames); | |
3014 | 3258 | |
3015 | - relid = find_relid_aliasname(root, relname, initial_rels, | |
3016 | - hint->base.hint_str); | |
3259 | + if (hint->joinrelids == NULL || hint->base.state == HINT_STATE_ERROR) | |
3260 | + continue; | |
3017 | 3261 | |
3018 | - if (relid == -1) | |
3019 | - hint->base.state = HINT_STATE_ERROR; | |
3262 | + hstate->join_hint_level[hint->nrels] = | |
3263 | + lappend(hstate->join_hint_level[hint->nrels], hint); | |
3264 | + } | |
3020 | 3265 | |
3021 | - if (relid <= 0) | |
3022 | - break; | |
3266 | + /* | |
3267 | + * Create bitmap of relids from alias names for each rows hint. | |
3268 | + * Bitmaps are more handy than strings in join searching. | |
3269 | + */ | |
3270 | + for (i = 0; i < hstate->num_hints[HINT_TYPE_ROWS]; i++) | |
3271 | + { | |
3272 | + RowsHint *hint = hstate->rows_hints[i]; | |
3023 | 3273 | |
3024 | - if (bms_is_member(relid, hint->joinrelids)) | |
3025 | - { | |
3026 | - hint_ereport(hint->base.hint_str, | |
3027 | - ("Relation name \"%s\" is duplicated.", relname)); | |
3028 | - hint->base.state = HINT_STATE_ERROR; | |
3029 | - break; | |
3030 | - } | |
3274 | + if (!hint_state_enabled(hint) || hint->nrels > nbaserel) | |
3275 | + continue; | |
3031 | 3276 | |
3032 | - hint->joinrelids = bms_add_member(hint->joinrelids, relid); | |
3033 | - } | |
3277 | + hint->joinrelids = create_bms_of_relids(&(hint->base), root, | |
3278 | + initial_rels, hint->nrels, hint->relnames); | |
3034 | 3279 | |
3035 | - if (relid <= 0 || hint->base.state == HINT_STATE_ERROR) | |
3280 | + if (hint->joinrelids == NULL || hint->base.state == HINT_STATE_ERROR) | |
3036 | 3281 | continue; |
3037 | - | |
3038 | - hstate->join_hint_level[hint->nrels] = | |
3039 | - lappend(hstate->join_hint_level[hint->nrels], hint); | |
3040 | 3282 | } |
3041 | 3283 | |
3042 | 3284 | /* Do nothing if no Leading hint was supplied. */ |
@@ -3242,17 +3484,7 @@ static void | ||
3242 | 3484 | set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) |
3243 | 3485 | { |
3244 | 3486 | /* Consider sequential scan */ |
3245 | - Relids required_outer; | |
3246 | - | |
3247 | - /* | |
3248 | - * We don't support pushing join clauses into the quals of a seqscan, but | |
3249 | - * it could still have required parameterization due to LATERAL refs in | |
3250 | - * its tlist. | |
3251 | - */ | |
3252 | - required_outer = rel->lateral_relids; | |
3253 | - | |
3254 | - /* Consider sequential scan */ | |
3255 | - add_path(rel, create_seqscan_path(root, rel, required_outer)); | |
3487 | + add_path(rel, create_seqscan_path(root, rel, NULL)); | |
3256 | 3488 | |
3257 | 3489 | /* Consider index scans */ |
3258 | 3490 | create_index_paths(root, rel); |
@@ -845,3 +845,32 @@ BEGIN | ||
845 | 845 | END; |
846 | 846 | $$; |
847 | 847 | DROP EXTENSION pg_hint_plan; |
848 | + | |
849 | +-- | |
850 | +-- Rows hint tests | |
851 | +-- | |
852 | +-- value types | |
853 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
854 | +/*+ Rows(t1 t2 #99) */ | |
855 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
856 | +/*+ Rows(t1 t2 +99) */ | |
857 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
858 | +/*+ Rows(t1 t2 -99) */ | |
859 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
860 | +/*+ Rows(t1 t2 *99) */ | |
861 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
862 | +/*+ Rows(t1 t2 *0.01) */ | |
863 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
864 | +/*+ Rows(t1 t2 #aa) */ | |
865 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); -- ERROR | |
866 | +/*+ Rows(t1 t2 /99) */ | |
867 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); -- ERROR | |
868 | +-- round up to 1 | |
869 | +/*+ Rows(t1 t2 -99999) */ | |
870 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id); | |
871 | +-- complex join tree | |
872 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id) JOIN t3 ON (t3.id = t2.id); | |
873 | +/*+ Rows(t1 t2 #22) */ | |
874 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id) JOIN t3 ON (t3.id = t2.id); | |
875 | +/*+ Rows(t1 t3 *10) */ | |
876 | +EXPLAIN SELECT * FROM t1 JOIN t2 ON (t1.id = t2.id) JOIN t3 ON (t3.id = t2.id); |
@@ -1107,6 +1107,9 @@ EXPLAIN (COSTS false) SELECT nested_planner(2) FROM s1.t1 t_1 ORDER BY t_1.c1; | ||
1107 | 1107 | EXPLAIN (COSTS false) SELECT nested_planner(2) FROM s1.t1 t_1 ORDER BY t_1.c1; |
1108 | 1108 | |
1109 | 1109 | --No.13-3-3 |
1110 | +-- | |
1111 | +-- Redefine not to use cached plan | |
1112 | +-- | |
1110 | 1113 | CREATE OR REPLACE FUNCTION nested_planner(cnt int) RETURNS int AS $$ |
1111 | 1114 | DECLARE |
1112 | 1115 | new_cnt int; |
@@ -152,6 +152,10 @@ EXPLAIN (COSTS false) SELECT * FROM s1.v1 t1, s1.v1_ t2 WHERE t1.c1 = t2.c1; | ||
152 | 152 | EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1 AND t1.c1 = (SELECT max(st1.c1) FROM s1.t1 st1, s1.t2 st2 WHERE st1.c1 = st2.c1); |
153 | 153 | /*+MergeJoin(t1 t2)NestLoop(st1 st2)*/ |
154 | 154 | EXPLAIN (COSTS true) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1 AND t1.c1 = (SELECT max(st1.c1) FROM s1.t1 st1, s1.t2 st2 WHERE st1.c1 = st2.c1); |
155 | +-- | |
156 | +-- There are cases where difference in the measured value and predicted value | |
157 | +-- depending upon the version of PostgreSQL | |
158 | +-- | |
155 | 159 | |
156 | 160 | EXPLAIN (COSTS false) SELECT * FROM s1.t1, (SELECT t2.c1 FROM s1.t2) st2 WHERE t1.c1 = st2.c1; |
157 | 161 | /*+HashJoin(t1 st2)*/ |
@@ -614,7 +614,7 @@ EXPLAIN (COSTS false) SELECT c2 FROM s1.ti1 WHERE ti1.c2 >= 1; | ||
614 | 614 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; |
615 | 615 | |
616 | 616 | -- No. S-3-3-2 |
617 | -/*+IndexScan(ti1 ti1_i3 ti1_i4)*/ | |
617 | +/*+IndexScan(ti1 ti1_i3 ti1_i2)*/ | |
618 | 618 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; |
619 | 619 | |
620 | 620 | -- No. S-3-3-3 |
@@ -626,7 +626,7 @@ EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; | ||
626 | 626 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; |
627 | 627 | |
628 | 628 | -- No. S-3-3-5 |
629 | -/*+BitmapScan(ti1 ti1_i3 ti1_i4)*/ | |
629 | +/*+BitmapScan(ti1 ti1_i3 ti1_i2)*/ | |
630 | 630 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; |
631 | 631 | |
632 | 632 | -- No. S-3-3-6 |
@@ -638,7 +638,7 @@ EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE ti1.c2 = 1 AND ctid = '(1,1)'; | ||
638 | 638 | EXPLAIN (COSTS false) SELECT c2 FROM s1.ti1 WHERE ti1.c2 >= 1; |
639 | 639 | |
640 | 640 | -- No. S-3-3-8 |
641 | -/*+IndexOnlyScan(ti1 ti1_i3 ti1_i4)*/ | |
641 | +/*+IndexOnlyScan(ti1 ti1_i3 ti1_i2)*/ | |
642 | 642 | EXPLAIN (COSTS false) SELECT c2 FROM s1.ti1 WHERE ti1.c2 >= 1; |
643 | 643 | |
644 | 644 | -- No. S-3-3-9 |
@@ -962,195 +962,195 @@ EXPLAIN SELECT c2 FROM s1.p1 WHERE c2 = 1; | ||
962 | 962 | EXPLAIN (COSTS true) SELECT * FROM s1.p2 WHERE c1 = 1; |
963 | 963 | |
964 | 964 | ---- |
965 | ----- No. S-3-12 specified same table | |
965 | +---- No. S-3-11 specified same table | |
966 | 966 | ---- |
967 | 967 | |
968 | --- No. S-3-12-1 | |
968 | +-- No. S-3-11-1 | |
969 | 969 | /*+IndexScan(ti1) BitmapScan(ti1)*/ |
970 | 970 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
971 | 971 | |
972 | --- No. S-3-12-2 | |
972 | +-- No. S-3-11-2 | |
973 | 973 | /*+IndexScan(ti1 ti1_pkey) BitmapScan(ti1 ti1_btree)*/ |
974 | 974 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
975 | 975 | |
976 | --- No. S-3-12-3 | |
976 | +-- No. S-3-11-3 | |
977 | 977 | /*+BitmapScan(ti1) IndexScan(ti1) BitmapScan(ti1)*/ |
978 | 978 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
979 | 979 | |
980 | --- No. S-3-12-4 | |
980 | +-- No. S-3-11-4 | |
981 | 981 | /*+BitmapScan(ti1 ti1_hash) IndexScan(ti1 ti1_pkey) BitmapScan(ti1 ti1_btree)*/ |
982 | 982 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
983 | 983 | |
984 | 984 | ---- |
985 | ----- No. S-3-13 message output | |
985 | +---- No. S-3-12 message output | |
986 | 986 | ---- |
987 | 987 | |
988 | --- No. S-3-13-1 | |
988 | +-- No. S-3-12-1 | |
989 | 989 | /*+SeqScan(ti1)*/ |
990 | 990 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
991 | 991 | |
992 | --- No. S-3-13-2 | |
992 | +-- No. S-3-12-2 | |
993 | 993 | /*+SeqScan(ti1 ti1_pkey)*/ |
994 | 994 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
995 | 995 | |
996 | --- No. S-3-13-3 | |
996 | +-- No. S-3-12-3 | |
997 | 997 | /*+SeqScan(ti1 ti1_pkey ti1_btree)*/ |
998 | 998 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
999 | 999 | |
1000 | --- No. S-3-13-4 | |
1000 | +-- No. S-3-12-4 | |
1001 | 1001 | /*+IndexScan(ti1)*/ |
1002 | 1002 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1003 | 1003 | |
1004 | --- No. S-3-13-5 | |
1004 | +-- No. S-3-12-5 | |
1005 | 1005 | /*+IndexScan(ti1 ti1_pkey)*/ |
1006 | 1006 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1007 | 1007 | |
1008 | --- No. S-3-13-6 | |
1008 | +-- No. S-3-12-6 | |
1009 | 1009 | /*+IndexScan(ti1 ti1_pkey ti1_btree)*/ |
1010 | 1010 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1011 | 1011 | |
1012 | --- No. S-3-13-7 | |
1012 | +-- No. S-3-12-7 | |
1013 | 1013 | /*+BitmapScan(ti1)*/ |
1014 | 1014 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1015 | 1015 | |
1016 | --- No. S-3-13-8 | |
1016 | +-- No. S-3-12-8 | |
1017 | 1017 | /*+BitmapScan(ti1 ti1_pkey)*/ |
1018 | 1018 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1019 | 1019 | |
1020 | --- No. S-3-13-9 | |
1020 | +-- No. S-3-12-9 | |
1021 | 1021 | /*+BitmapScan(ti1 ti1_pkey ti1_btree)*/ |
1022 | 1022 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1023 | 1023 | |
1024 | --- No. S-3-13-10 | |
1024 | +-- No. S-3-12-10 | |
1025 | 1025 | /*+TidScan(ti1)*/ |
1026 | 1026 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1027 | 1027 | |
1028 | --- No. S-3-13-11 | |
1028 | +-- No. S-3-12-11 | |
1029 | 1029 | /*+TidScan(ti1 ti1_pkey)*/ |
1030 | 1030 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1031 | 1031 | |
1032 | --- No. S-3-13-12 | |
1032 | +-- No. S-3-12-12 | |
1033 | 1033 | /*+TidScan(ti1 ti1_pkey ti1_btree)*/ |
1034 | 1034 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1035 | 1035 | |
1036 | --- No. S-3-13-13 | |
1036 | +-- No. S-3-12-13 | |
1037 | 1037 | /*+NoSeqScan(ti1)*/ |
1038 | 1038 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1039 | 1039 | |
1040 | --- No. S-3-13-14 | |
1040 | +-- No. S-3-12-14 | |
1041 | 1041 | /*+NoSeqScan(ti1 ti1_pkey)*/ |
1042 | 1042 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1043 | 1043 | |
1044 | --- No. S-3-13-15 | |
1044 | +-- No. S-3-12-15 | |
1045 | 1045 | /*+NoSeqScan(ti1 ti1_pkey ti1_btree)*/ |
1046 | 1046 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1047 | 1047 | |
1048 | --- No. S-3-13-16 | |
1048 | +-- No. S-3-12-16 | |
1049 | 1049 | /*+NoIndexScan(ti1)*/ |
1050 | 1050 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1051 | 1051 | |
1052 | --- No. S-3-13-17 | |
1052 | +-- No. S-3-12-17 | |
1053 | 1053 | /*+NoIndexScan(ti1 ti1_pkey)*/ |
1054 | 1054 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1055 | 1055 | |
1056 | --- No. S-3-13-18 | |
1056 | +-- No. S-3-12-18 | |
1057 | 1057 | /*+NoIndexScan(ti1 ti1_pkey ti1_btree)*/ |
1058 | 1058 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1059 | 1059 | |
1060 | --- No. S-3-13-19 | |
1060 | +-- No. S-3-12-19 | |
1061 | 1061 | /*+NoBitmapScan(ti1)*/ |
1062 | 1062 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1063 | 1063 | |
1064 | --- No. S-3-13-20 | |
1064 | +-- No. S-3-12-20 | |
1065 | 1065 | /*+NoBitmapScan(ti1 ti1_pkey)*/ |
1066 | 1066 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1067 | 1067 | |
1068 | --- No. S-3-13-21 | |
1068 | +-- No. S-3-12-21 | |
1069 | 1069 | /*+NoBitmapScan(ti1 ti1_pkey ti1_btree)*/ |
1070 | 1070 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1071 | 1071 | |
1072 | --- No. S-3-13-22 | |
1072 | +-- No. S-3-12-22 | |
1073 | 1073 | /*+NoTidScan(ti1)*/ |
1074 | 1074 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1075 | 1075 | |
1076 | --- No. S-3-13-23 | |
1076 | +-- No. S-3-12-23 | |
1077 | 1077 | /*+NoTidScan(ti1 ti1_pkey)*/ |
1078 | 1078 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1079 | 1079 | |
1080 | --- No. S-3-13-24 | |
1080 | +-- No. S-3-12-24 | |
1081 | 1081 | /*+NoTidScan(ti1 ti1_pkey ti1_btree)*/ |
1082 | 1082 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c1 = 1 AND ctid = '(1,1)'; |
1083 | 1083 | |
1084 | --- No. S-3-13-25 | |
1084 | +-- No. S-3-12-25 | |
1085 | 1085 | /*+IndexOnlyScan(ti1)*/ |
1086 | 1086 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 >= 1; |
1087 | 1087 | |
1088 | --- No. S-3-13-26 | |
1088 | +-- No. S-3-12-26 | |
1089 | 1089 | /*+IndexOnlyScan(ti1 ti1_pkey)*/ |
1090 | 1090 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 >= 1; |
1091 | 1091 | |
1092 | --- No. S-3-13-27 | |
1092 | +-- No. S-3-12-27 | |
1093 | 1093 | /*+IndexOnlyScan(ti1 ti1_pkey ti1_btree)*/ |
1094 | 1094 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 >= 1; |
1095 | 1095 | |
1096 | --- No. S-3-13-28 | |
1096 | +-- No. S-3-12-28 | |
1097 | 1097 | /*+NoIndexOnlyScan(ti1)*/ |
1098 | 1098 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 = 1; |
1099 | 1099 | |
1100 | --- No. S-3-13-29 | |
1100 | +-- No. S-3-12-29 | |
1101 | 1101 | /*+NoIndexOnlyScan(ti1 ti1_pkey)*/ |
1102 | 1102 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 = 1; |
1103 | 1103 | |
1104 | --- No. S-3-13-30 | |
1104 | +-- No. S-3-12-30 | |
1105 | 1105 | /*+NoIndexOnlyScan(ti1 ti1_pkey ti1_btree)*/ |
1106 | 1106 | EXPLAIN (COSTS false) SELECT c1 FROM s1.ti1 WHERE c1 = 1; |
1107 | 1107 | |
1108 | 1108 | |
1109 | 1109 | ---- |
1110 | ----- No. S-3-14 regular expression | |
1110 | +---- No. S-3-13 message output | |
1111 | 1111 | ---- |
1112 | 1112 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1113 | 1113 | |
1114 | --- No. S-3-14-1 | |
1114 | +-- No. S-3-13-1 | |
1115 | 1115 | /*+IndexScanRegexp(ti1 ti1_.*_key)*/ |
1116 | 1116 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1117 | 1117 | |
1118 | --- No. S-3-14-2 | |
1118 | +-- No. S-3-13-2 | |
1119 | 1119 | /*+IndexScanRegexp(ti1 ti1_i.)*/ |
1120 | 1120 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1121 | 1121 | |
1122 | --- No. S-3-14-3 | |
1122 | +-- No. S-3-13-3 | |
1123 | 1123 | /*+IndexScanRegexp(ti1 no.*_exist)*/ |
1124 | 1124 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1125 | 1125 | |
1126 | --- No. S-3-14-4 | |
1126 | +-- No. S-3-13-4 | |
1127 | 1127 | /*+IndexScanRegexp(p1 .*pkey)*/ |
1128 | 1128 | EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1; |
1129 | 1129 | |
1130 | --- No. S-3-14-5 | |
1130 | +-- No. S-3-13-5 | |
1131 | 1131 | /*+IndexScanRegexp(p1 p1.*i)*/ |
1132 | 1132 | EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1; |
1133 | 1133 | |
1134 | --- No. S-3-14-6 | |
1134 | +-- No. S-3-13-6 | |
1135 | 1135 | /*+IndexScanRegexp(p1 no.*_exist)*/ |
1136 | 1136 | EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1; |
1137 | 1137 | |
1138 | 1138 | ---- |
1139 | ----- No. S-3-15 message output of candidate for index | |
1139 | +---- No. S-3-14 message output | |
1140 | 1140 | ---- |
1141 | 1141 | |
1142 | --- No. S-3-15-1 | |
1142 | +-- No. S-3-14-1 | |
1143 | 1143 | /*+IndexScan(ti1 ti1_i1)*/ |
1144 | 1144 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1145 | --- No. S-3-15-2 | |
1145 | +-- No. S-3-14-2 | |
1146 | 1146 | /*+IndexScan(ti1 not_exist)*/ |
1147 | 1147 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1148 | --- No. S-3-15-3 | |
1148 | +-- No. S-3-14-3 | |
1149 | 1149 | /*+IndexScan(ti1 ti1_i1 ti1_i2)*/ |
1150 | 1150 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1151 | --- No. S-3-15-4 | |
1151 | +-- No. S-3-14-4 | |
1152 | 1152 | /*+IndexScan(ti1 ti1_i1 not_exist)*/ |
1153 | 1153 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |
1154 | --- No. S-3-15-5 | |
1154 | +-- No. S-3-14-5 | |
1155 | 1155 | /*+IndexScan(ti1 not_exist1 not_exist2)*/ |
1156 | 1156 | EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; |