7 'src/backend/optimizer/path/allpaths.c',
8 'src/backend/optimizer/path/joinrels.c');
11 => {protos => ['populate_joinrel_with_paths'],
12 funcs => ['set_plain_rel_pathlist',
13 'set_append_rel_pathlist',
14 'standard_join_search',
15 'create_plain_partial_paths',
16 'join_search_one_level',
17 'make_rels_by_clause_joins',
18 'make_rels_by_clauseless_joins',
20 'has_join_restriction',
22 'restriction_is_constant_false',
23 'try_partitionwise_join'],
24 head => core_c_head()},
27 funcs => ['make_join_rel',
28 'populate_joinrel_with_paths'],
29 head => make_join_rel_head()});
31 open (my $in, '-|', "objdump -W `which postgres`") || die "failed to objdump";
34 if (/DW_AT_comp_dir .*: (.*\/)src\/backend\//)
42 die "source path not found" if (! defined $srcpath);
43 printf("Source path = %s\n", $srcpath);
50 for my $fname (@sources)
52 my $f = $srcpath.$fname;
55 open ($in, '<', $f) || die "failed to open $f: $!";
61 ## Collect static prototypes
63 while ($source =~ /\n(static [^\(\)\{\}]*?(\w+)(\([^\{\);]+?\);))/gsm)
65 # print "Prototype found: $2\n";
69 ## Collect function bodies
71 while ($source =~ /(\n\/\*\n.+?\*\/\n(static )?(.+?)\n(.+?) *\(.*?\)\n\{.+?\n\}\n)/gsm)
74 $func_is_static{$4} = (defined $2);
75 $func_source{$4} = $fname;
77 # printf("Function found: %s$4\n", $func_is_static{$4} ? "static " : "");
85 for my $fname (keys %defs)
87 my %d = %{$defs{$fname}};
89 my @protonames = @{$d{'protos'}};
90 my @funcnames = @{$d{'funcs'}};
91 my $head = $d{'head'};
93 print "Generate $fname.\n";
94 open (my $out, '>', $fname) || die "could not open $fname: $!";
100 print " Prototype: $_\n";
102 die "Prototype for $_ not found" if (! defined $protos{$_});
103 print $out $protos{$_};
108 printf(" %s function: $_@%s\n",
109 $func_is_static{$_}?"static":"public", $func_source{$_});
111 die "Function body for $_ not found" if (! defined $funcs{$_});
112 print $out $funcs{$_};
118 # modify make_join_rel.c
119 patch_make_join_rel();
124 /*-------------------------------------------------------------------------
127 * Routines copied from PostgreSQL core distribution.
129 * The main purpose of this files is having access to static functions in core.
130 * Another purpose is tweaking functions behavior by replacing part of them by
131 * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
132 * this file *must* contain required functions without making any change.
134 * This file contains the following functions from corresponding files.
136 * src/backend/optimizer/path/allpaths.c
139 * standard_join_search(): This funcion is not static. The reason for
140 * including this function is make_rels_by_clause_joins. In order to
141 * avoid generating apparently unwanted join combination, we decided to
142 * change the behavior of make_join_rel, which is called under this
146 * set_plain_rel_pathlist()
147 * set_append_rel_pathlist()
148 * create_plain_partial_paths()
150 * src/backend/optimizer/path/joinrels.c
153 * join_search_one_level(): We have to modify this to call my definition of
154 * make_rels_by_clause_joins.
157 * make_rels_by_clause_joins()
158 * make_rels_by_clauseless_joins()
160 * has_join_restriction()
162 * restriction_is_constant_false()
163 * try_partitionwise_join()
165 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
166 * Portions Copyright (c) 1994, Regents of the University of California
168 *-------------------------------------------------------------------------
173 sub make_join_rel_head
176 /*-------------------------------------------------------------------------
179 * Routines copied from PostgreSQL core distribution with some
182 * src/backend/optimizer/path/joinrels.c
184 * This file contains the following functions from corresponding files.
188 * populate_joinrel_with_paths()
190 * Portions Copyright (c) 2013-2020, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
191 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
192 * Portions Copyright (c) 1994, Regents of the University of California
194 *-------------------------------------------------------------------------
198 * adjust_rows: tweak estimated row numbers according to the hint.
201 adjust_rows(double rows, RowsHint *hint)
203 double result = 0.0; /* keep compiler quiet */
205 if (hint->value_type == RVT_ABSOLUTE)
207 else if (hint->value_type == RVT_ADD)
208 result = rows + hint->rows;
209 else if (hint->value_type == RVT_SUB)
210 result = rows - hint->rows;
211 else if (hint->value_type == RVT_MULTI)
212 result = rows * hint->rows;
214 Assert(false); /* unrecognized rows value type */
216 hint->base.state = HINT_STATE_USED;
219 (errmsg("Force estimate to be at least one row, to avoid possible divide-by-zero when interpolating costs : %s",
220 hint->base.hint_str)));
221 result = clamp_row_est(result);
222 elog(DEBUG1, "adjusted rows %d to %d", (int) rows, (int) result);
230 sub patch_make_join_rel
232 open(my $out, '|-', 'patch') || die "failed to open pipe: $!";
235 diff --git b/make_join_rel.c a/make_join_rel.c
236 index 0e7b99f..287e7f1 100644
237 --- b/make_join_rel.c
238 +++ a/make_join_rel.c
239 @@ -126,6 +126,84 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
240 joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
243 + /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
245 + RowsHint *rows_hint = NULL;
247 + RowsHint *justforme = NULL;
248 + RowsHint *domultiply = NULL;
250 + /* Search for applicable rows hint for this join node */
251 + for (i = 0; i < current_hint_state->num_hints[HINT_TYPE_ROWS]; i++)
253 + rows_hint = current_hint_state->rows_hints[i];
256 + * Skip this rows_hint if it is invalid from the first or it
257 + * doesn't target any join rels.
259 + if (!rows_hint->joinrelids ||
260 + rows_hint->base.state == HINT_STATE_ERROR)
263 + if (bms_equal(joinrelids, rows_hint->joinrelids))
266 + * This joinrel is just the target of this rows_hint, so tweak
267 + * rows estimation according to the hint.
269 + justforme = rows_hint;
271 + else if (!(bms_is_subset(rows_hint->joinrelids, rel1->relids) ||
272 + bms_is_subset(rows_hint->joinrelids, rel2->relids)) &&
273 + bms_is_subset(rows_hint->joinrelids, joinrelids) &&
274 + rows_hint->value_type == RVT_MULTI)
277 + * If the rows_hint's target relids is not a subset of both of
278 + * component rels and is a subset of this joinrel, ths hint's
279 + * targets spread over both component rels. This means that
280 + * this hint has been never applied so far and this joinrel is
281 + * the first (and only) chance to fire in current join tree.
282 + * Only the multiplication hint has the cumulative nature so we
283 + * apply only RVT_MULTI in this way.
285 + domultiply = rows_hint;
292 + * If a hint just for me is found, no other adjust method is
293 + * useless, but this cannot be more than twice becuase this joinrel
294 + * is already adjusted by this hint.
296 + if (justforme->base.state == HINT_STATE_NOTUSED)
297 + joinrel->rows = adjust_rows(joinrel->rows, justforme);
304 + * If we have multiple routes up to this joinrel which are not
305 + * applicable this hint, this multiply hint will applied more
306 + * than twice. But there's no means to know of that,
307 + * re-estimate the row number of this joinrel always just
308 + * before applying the hint. This is a bit different from
309 + * normal planner behavior but it doesn't harm so much.
311 + set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
314 + joinrel->rows = adjust_rows(joinrel->rows, domultiply);
319 + /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
322 * If we've already proven this join is empty, we needn't consider any