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',
21 'restriction_is_constant_false',
22 'build_child_join_sjinfo',
23 'get_matching_part_pairs',
24 'compute_partition_bounds',
25 'try_partitionwise_join'],
26 head => core_c_head()},
29 funcs => ['make_join_rel',
30 'populate_joinrel_with_paths'],
31 head => make_join_rel_head()});
33 open (my $in, '-|', "objdump -W `which postgres`") || die "failed to objdump";
36 if (/DW_AT_comp_dir .*: (.*\/)src\/backend\//)
44 die "source path not found" if (! defined $srcpath);
45 #printf("Source path = %s\n", $srcpath);
52 for my $fname (@sources)
54 my $f = $srcpath.$fname;
57 open ($in, '<', $f) || die "failed to open $f: $!";
63 ## Collect static prototypes
65 while ($source =~ /\n(static [^\(\)\{\}]*?(\w+)(\([^\{\);]+?\);))/gsm)
67 # print "Prototype found: $2\n";
71 ## Collect function bodies
73 while ($source =~ /(\n\/\*\n.+?\*\/\n(static )?(.+?)\n(.+?) *\(.*?\)\n\{.+?\n\}\n)/gsm)
76 $func_is_static{$4} = (defined $2);
77 $func_source{$4} = $fname;
79 # printf("Function found: %s$4\n", $func_is_static{$4} ? "static " : "");
87 for my $fname (keys %defs)
89 my %d = %{$defs{$fname}};
91 my @protonames = @{$d{'protos'}};
92 my @funcnames = @{$d{'funcs'}};
93 my $head = $d{'head'};
95 print "Generate $fname.\n";
96 open (my $out, '>', $fname) || die "could not open $fname: $!";
102 print " Prototype: $_\n";
104 die "Prototype for $_ not found" if (! defined $protos{$_});
105 print $out $protos{$_};
110 printf(" %s function: $_@%s\n",
111 $func_is_static{$_}?"static":"public", $func_source{$_});
113 die "Function body for $_ not found" if (! defined $funcs{$_});
114 print $out $funcs{$_};
120 # modify make_join_rel.c
121 patch_make_join_rel();
126 /*-------------------------------------------------------------------------
129 * Routines copied from PostgreSQL core distribution.
131 * The main purpose of this files is having access to static functions in core.
132 * Another purpose is tweaking functions behavior by replacing part of them by
133 * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
134 * this file *must* contain required functions without making any change.
136 * This file contains the following functions from corresponding files.
138 * src/backend/optimizer/path/allpaths.c
141 * standard_join_search(): This funcion is not static. The reason for
142 * including this function is make_rels_by_clause_joins. In order to
143 * avoid generating apparently unwanted join combination, we decided to
144 * change the behavior of make_join_rel, which is called under this
148 * set_plain_rel_pathlist()
149 * set_append_rel_pathlist()
150 * create_plain_partial_paths()
152 * src/backend/optimizer/path/joinrels.c
155 * join_search_one_level(): We have to modify this to call my definition of
156 * make_rels_by_clause_joins.
159 * make_rels_by_clause_joins()
160 * make_rels_by_clauseless_joins()
162 * has_join_restriction()
163 * restriction_is_constant_false()
164 * build_child_join_sjinfo()
165 * get_matching_part_pairs()
166 * compute_partition_bounds()
167 * try_partitionwise_join()
169 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
170 * Portions Copyright (c) 1994, Regents of the University of California
172 *-------------------------------------------------------------------------
177 sub make_join_rel_head
180 /*-------------------------------------------------------------------------
183 * Routines copied from PostgreSQL core distribution with some
186 * src/backend/optimizer/path/joinrels.c
188 * This file contains the following functions from corresponding files.
192 * populate_joinrel_with_paths()
194 * Portions Copyright (c) 2013-2021, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
195 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
196 * Portions Copyright (c) 1994, Regents of the University of California
198 *-------------------------------------------------------------------------
202 * adjust_rows: tweak estimated row numbers according to the hint.
205 adjust_rows(double rows, RowsHint *hint)
207 double result = 0.0; /* keep compiler quiet */
209 if (hint->value_type == RVT_ABSOLUTE)
211 else if (hint->value_type == RVT_ADD)
212 result = rows + hint->rows;
213 else if (hint->value_type == RVT_SUB)
214 result = rows - hint->rows;
215 else if (hint->value_type == RVT_MULTI)
216 result = rows * hint->rows;
218 Assert(false); /* unrecognized rows value type */
220 hint->base.state = HINT_STATE_USED;
223 (errmsg("Force estimate to be at least one row, to avoid possible divide-by-zero when interpolating costs : %s",
224 hint->base.hint_str)));
225 result = clamp_row_est(result);
226 elog(DEBUG1, "adjusted rows %d to %d", (int) rows, (int) result);
234 sub patch_make_join_rel
236 open(my $out, '|-', 'patch') || die "failed to open pipe: $!";
239 diff --git b/make_join_rel.c a/make_join_rel.c
240 index 0e7b99f..287e7f1 100644
241 --- b/make_join_rel.c
242 +++ a/make_join_rel.c
243 @@ -126,6 +126,84 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
244 joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
247 + /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
249 + RowsHint *rows_hint = NULL;
251 + RowsHint *justforme = NULL;
252 + RowsHint *domultiply = NULL;
254 + /* Search for applicable rows hint for this join node */
255 + for (i = 0; i < current_hint_state->num_hints[HINT_TYPE_ROWS]; i++)
257 + rows_hint = current_hint_state->rows_hints[i];
260 + * Skip this rows_hint if it is invalid from the first or it
261 + * doesn't target any join rels.
263 + if (!rows_hint->joinrelids ||
264 + rows_hint->base.state == HINT_STATE_ERROR)
267 + if (bms_equal(joinrelids, rows_hint->joinrelids))
270 + * This joinrel is just the target of this rows_hint, so tweak
271 + * rows estimation according to the hint.
273 + justforme = rows_hint;
275 + else if (!(bms_is_subset(rows_hint->joinrelids, rel1->relids) ||
276 + bms_is_subset(rows_hint->joinrelids, rel2->relids)) &&
277 + bms_is_subset(rows_hint->joinrelids, joinrelids) &&
278 + rows_hint->value_type == RVT_MULTI)
281 + * If the rows_hint's target relids is not a subset of both of
282 + * component rels and is a subset of this joinrel, ths hint's
283 + * targets spread over both component rels. This menas that
284 + * this hint has been never applied so far and this joinrel is
285 + * the first (and only) chance to fire in current join tree.
286 + * Only the multiplication hint has the cumulative nature so we
287 + * apply only RVT_MULTI in this way.
289 + domultiply = rows_hint;
296 + * If a hint just for me is found, no other adjust method is
297 + * useles, but this cannot be more than twice becuase this joinrel
298 + * is already adjusted by this hint.
300 + if (justforme->base.state == HINT_STATE_NOTUSED)
301 + joinrel->rows = adjust_rows(joinrel->rows, justforme);
308 + * If we have multiple routes up to this joinrel which are not
309 + * applicable this hint, this multiply hint will applied more
310 + * than twice. But there's no means to know of that,
311 + * re-estimate the row number of this joinrel always just
312 + * before applying the hint. This is a bit different from
313 + * normal planner behavior but it doesn't harm so much.
315 + set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
318 + joinrel->rows = adjust_rows(joinrel->rows, domultiply);
323 + /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
326 * If we've already proven this join is empty, we needn't consider any