• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision2b4d65fa7d67a3e25febfec672b02015f60c3d76 (tree)
Time2020-10-29 20:31:47
AuthorKyotaro Horiguchi <horikyoga.ntt@gmai...>
CommiterKyotaro Horiguchi

Log Message

New tool to generate source files for copied functions

Previously core.c and make_join_rel.c are maintained by hand. This
tool generates the files from corresponding core source files.

Change Summary

Incremental Difference

--- /dev/null
+++ b/update_copied_funcs.pl
@@ -0,0 +1,329 @@
1+#! /usr/bin/perl
2+
3+use strict;
4+
5+my $srcpath;
6+my @sources = (
7+ 'src/backend/optimizer/path/allpaths.c',
8+ 'src/backend/optimizer/path/joinrels.c');
9+my %defs =
10+ ('core.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',
19+ 'join_is_legal',
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()},
27+ 'make_join_rel.c'
28+ => {protos => [],
29+ funcs => ['make_join_rel',
30+ 'populate_joinrel_with_paths'],
31+ head => make_join_rel_head()});
32+
33+open (my $in, '-|', "objdump -W `which postgres`") || die "failed to objdump";
34+while (<$in>)
35+{
36+ if (/DW_AT_comp_dir .*: (.*\/)src\/backend\//)
37+ {
38+ $srcpath = $1;
39+ last;
40+ }
41+}
42+close($in);
43+
44+die "source path not found" if (! defined $srcpath);
45+#printf("Source path = %s\n", $srcpath);
46+
47+my %protos;
48+my %funcs;
49+my %func_is_static;
50+my %func_source;
51+
52+for my $fname (@sources)
53+{
54+ my $f = $srcpath.$fname;
55+ my $source;
56+
57+ open ($in, '<', $f) || die "failed to open $f: $!";
58+ while (<$in>)
59+ {
60+ $source .= $_;
61+ }
62+
63+ ## Collect static prototypes
64+
65+ while ($source =~ /\n(static [^\(\)\{\}]*?(\w+)(\([^\{\);]+?\);))/gsm)
66+ {
67+ # print "Prototype found: $2\n";
68+ $protos{$2} = $1;
69+ }
70+
71+ ## Collect function bodies
72+
73+ while ($source =~ /(\n\/\*\n.+?\*\/\n(static )?(.+?)\n(.+?) *\(.*?\)\n\{.+?\n\}\n)/gsm)
74+ {
75+ $funcs{$4} = $1;
76+ $func_is_static{$4} = (defined $2);
77+ $func_source{$4} = $fname;
78+
79+ # printf("Function found: %s$4\n", $func_is_static{$4} ? "static " : "");
80+ }
81+
82+ close($in);
83+}
84+
85+
86+# Generate files
87+for my $fname (keys %defs)
88+{
89+ my %d = %{$defs{$fname}};
90+
91+ my @protonames = @{$d{'protos'}};
92+ my @funcnames = @{$d{'funcs'}};
93+ my $head = $d{'head'};
94+
95+ print "Generate $fname.\n";
96+ open (my $out, '>', $fname) || die "could not open $fname: $!";
97+
98+ print $out $head;
99+
100+ for (@protonames)
101+ {
102+ print " Prototype: $_\n";
103+ print $out "\n";
104+ die "Prototype for $_ not found" if (! defined $protos{$_});
105+ print $out $protos{$_};
106+ }
107+
108+ for (@funcnames)
109+ {
110+ printf(" %s function: $_@%s\n",
111+ $func_is_static{$_}?"static":"public", $func_source{$_});
112+ print $out "\n";
113+ die "Function body for $_ not found" if (! defined $funcs{$_});
114+ print $out $funcs{$_};
115+ }
116+
117+ close($out);
118+}
119+
120+# modify make_join_rel.c
121+patch_make_join_rel();
122+
123+sub core_c_head()
124+{
125+ return << "EOS";
126+/*-------------------------------------------------------------------------
127+ *
128+ * core.c
129+ * Routines copied from PostgreSQL core distribution.
130+ *
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.
135+ *
136+ * This file contains the following functions from corresponding files.
137+ *
138+ * src/backend/optimizer/path/allpaths.c
139+ *
140+ * public functions:
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
145+ * function.
146+ *
147+ * static functions:
148+ * set_plain_rel_pathlist()
149+ * set_append_rel_pathlist()
150+ * create_plain_partial_paths()
151+ *
152+ * src/backend/optimizer/path/joinrels.c
153+ *
154+ * public functions:
155+ * join_search_one_level(): We have to modify this to call my definition of
156+ * make_rels_by_clause_joins.
157+ *
158+ * static functions:
159+ * make_rels_by_clause_joins()
160+ * make_rels_by_clauseless_joins()
161+ * join_is_legal()
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()
168+ *
169+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
170+ * Portions Copyright (c) 1994, Regents of the University of California
171+ *
172+ *-------------------------------------------------------------------------
173+ */
174+EOS
175+}
176+
177+sub make_join_rel_head
178+{
179+ return << "EOS";
180+/*-------------------------------------------------------------------------
181+ *
182+ * make_join_rel.c
183+ * Routines copied from PostgreSQL core distribution with some
184+ * modifications.
185+ *
186+ * src/backend/optimizer/path/joinrels.c
187+ *
188+ * This file contains the following functions from corresponding files.
189+ *
190+ * static functions:
191+ * make_join_rel()
192+ * populate_joinrel_with_paths()
193+ *
194+ * Portions Copyright (c) 2013-2020, 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
197+ *
198+ *-------------------------------------------------------------------------
199+ */
200+
201+/*
202+ * adjust_rows: tweak estimated row numbers according to the hint.
203+ */
204+static double
205+adjust_rows(double rows, RowsHint *hint)
206+{
207+ double result = 0.0; /* keep compiler quiet */
208+
209+ if (hint->value_type == RVT_ABSOLUTE)
210+ result = hint->rows;
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;
217+ else
218+ Assert(false); /* unrecognized rows value type */
219+
220+ hint->base.state = HINT_STATE_USED;
221+ if (result < 1.0)
222+ ereport(WARNING,
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);
227+
228+ return result;
229+}
230+EOS
231+}
232+
233+
234+sub patch_make_join_rel
235+{
236+ open(my $out, '|-', 'patch') || die "failed to open pipe: $!";
237+
238+ print $out <<"EOS";
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,
245+ &restrictlist);
246+
247++ /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
248++ {
249++ RowsHint *rows_hint = NULL;
250++ int i;
251++ RowsHint *justforme = NULL;
252++ RowsHint *domultiply = NULL;
253++
254++ /* Search for applicable rows hint for this join node */
255++ for (i = 0; i < current_hint_state->num_hints[HINT_TYPE_ROWS]; i++)
256++ {
257++ rows_hint = current_hint_state->rows_hints[i];
258++
259++ /*
260++ * Skip this rows_hint if it is invalid from the first or it
261++ * doesn't target any join rels.
262++ */
263++ if (!rows_hint->joinrelids ||
264++ rows_hint->base.state == HINT_STATE_ERROR)
265++ continue;
266++
267++ if (bms_equal(joinrelids, rows_hint->joinrelids))
268++ {
269++ /*
270++ * This joinrel is just the target of this rows_hint, so tweak
271++ * rows estimation according to the hint.
272++ */
273++ justforme = rows_hint;
274++ }
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)
279++ {
280++ /*
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.
288++ */
289++ domultiply = rows_hint;
290++ }
291++ }
292++
293++ if (justforme)
294++ {
295++ /*
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.
299++ */
300++ if (justforme->base.state == HINT_STATE_NOTUSED)
301++ joinrel->rows = adjust_rows(joinrel->rows, justforme);
302++ }
303++ else
304++ {
305++ if (domultiply)
306++ {
307++ /*
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.
314++ */
315++ set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
316++ restrictlist);
317++
318++ joinrel->rows = adjust_rows(joinrel->rows, domultiply);
319++ }
320++
321++ }
322++ }
323++ /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
324++
325+ /*
326+ * If we've already proven this join is empty, we needn't consider any
327+ * more paths for it.
328+EOS
329+}
Show on old repository browser