• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revisionbeb6a85e08730367cf6efc788557d74d18173bea (tree)
Time2020-10-29 21:20:30
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,330 @@
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+ 'create_plain_partial_paths',
14+ 'set_append_rel_pathlist',
15+ 'add_paths_to_append_rel',
16+ 'generate_mergeappend_paths',
17+ 'get_cheapest_parameterized_child_path',
18+ 'accumulate_append_subpath',
19+ 'standard_join_search',
20+ 'join_search_one_level',
21+ 'make_rels_by_clause_joins',
22+ 'make_rels_by_clauseless_joins',
23+ 'join_is_legal',
24+ 'has_join_restriction',
25+ 'mark_dummy_rel',
26+ 'restriction_is_constant_false'],
27+ head => core_c_head()},
28+ 'make_join_rel.c'
29+ => {protos => [],
30+ funcs => ['make_join_rel',
31+ 'populate_joinrel_with_paths'],
32+ head => make_join_rel_head()});
33+
34+open (my $in, '-|', "objdump -W `which postgres`") || die "failed to objdump";
35+while (<$in>)
36+{
37+ if (/DW_AT_comp_dir .*: (.*\/)src\/backend\//)
38+ {
39+ $srcpath = $1;
40+ last;
41+ }
42+}
43+close($in);
44+
45+die "source path not found" if (! defined $srcpath);
46+printf("Source path = %s\n", $srcpath);
47+
48+my %protos;
49+my %funcs;
50+my %func_is_static;
51+my %func_source;
52+
53+for my $fname (@sources)
54+{
55+ my $f = $srcpath.$fname;
56+ my $source;
57+
58+ open ($in, '<', $f) || die "failed to open $f: $!";
59+ while (<$in>)
60+ {
61+ $source .= $_;
62+ }
63+
64+ ## Collect static prototypes
65+
66+ while ($source =~ /\n(static [^\(\)\{\}]*?(\w+)(\([^\{\);]+?\);))/gsm)
67+ {
68+ # print "Prototype found: $2\n";
69+ $protos{$2} = $1;
70+ }
71+
72+ ## Collect function bodies
73+
74+ while ($source =~ /(\n\/\*\n.+?\*\/\n(static )?(.+?)\n(.+?) *\(.*?\)\n\{.+?\n\}\n)/gsm)
75+ {
76+ $funcs{$4} = $1;
77+ $func_is_static{$4} = (defined $2);
78+ $func_source{$4} = $fname;
79+
80+ # printf("Function found: %s$4\n", $func_is_static{$4} ? "static " : "");
81+ }
82+
83+ close($in);
84+}
85+
86+
87+# Generate files
88+for my $fname (keys %defs)
89+{
90+ my %d = %{$defs{$fname}};
91+
92+ my @protonames = @{$d{'protos'}};
93+ my @funcnames = @{$d{'funcs'}};
94+ my $head = $d{'head'};
95+
96+ print "Generate $fname.\n";
97+ open (my $out, '>', $fname) || die "could not open $fname: $!";
98+
99+ print $out $head;
100+
101+ for (@protonames)
102+ {
103+ print " Prototype: $_\n";
104+ print $out "\n";
105+ die "Prototype for $_ not found" if (! defined $protos{$_});
106+ print $out $protos{$_};
107+ }
108+
109+ for (@funcnames)
110+ {
111+ printf(" %s function: $_@%s\n",
112+ $func_is_static{$_}?"static":"public", $func_source{$_});
113+ print $out "\n";
114+ die "Function body for $_ not found" if (! defined $funcs{$_});
115+ print $out $funcs{$_};
116+ }
117+
118+ close($out);
119+}
120+
121+# modify make_join_rel.c
122+patch_make_join_rel();
123+
124+sub core_c_head()
125+{
126+ return << "EOS";
127+/*-------------------------------------------------------------------------
128+ *
129+ * core.c
130+ * Routines copied from PostgreSQL core distribution.
131+ *
132+ * The main purpose of this files is having access to static functions in core.
133+ * Another purpose is tweaking functions behavior by replacing part of them by
134+ * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
135+ * this file *must* contain required functions without making any change.
136+ *
137+ * This file contains the following functions from corresponding files.
138+ *
139+ * src/backend/optimizer/path/allpaths.c
140+ *
141+ * public functions:
142+ * standard_join_search(): This funcion is not static. The reason for
143+ * including this function is make_rels_by_clause_joins. In order to
144+ * avoid generating apparently unwanted join combination, we decided to
145+ * change the behavior of make_join_rel, which is called under this
146+ * function.
147+ *
148+ * static functions:
149+ * set_plain_rel_pathlist()
150+ * create_plain_partial_paths()
151+ * set_append_rel_pathlist()
152+ * add_paths_to_append_rel()
153+ * generate_mergeappend_paths()
154+ * get_cheapest_parameterized_child_path()
155+ *
156+ * src/backend/optimizer/path/joinrels.c
157+ *
158+ * public functions:
159+ * join_search_one_level(): We have to modify this to call my definition of
160+ * make_rels_by_clause_joins.
161+ *
162+ * static functions:
163+ * make_rels_by_clause_joins()
164+ * make_rels_by_clauseless_joins()
165+ * join_is_legal()
166+ * has_join_restriction()
167+ * mark_dummy_rel()
168+ * restriction_is_constant_false()
169+ *
170+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
171+ * Portions Copyright (c) 1994, Regents of the University of California
172+ *
173+ *-------------------------------------------------------------------------
174+ */
175+EOS
176+}
177+
178+sub make_join_rel_head
179+{
180+ return << "EOS";
181+/*-------------------------------------------------------------------------
182+ *
183+ * make_join_rel.c
184+ * Routines copied from PostgreSQL core distribution with some
185+ * modifications.
186+ *
187+ * src/backend/optimizer/path/joinrels.c
188+ *
189+ * This file contains the following functions from corresponding files.
190+ *
191+ * static functions:
192+ * make_join_rel()
193+ * populate_joinrel_with_paths()
194+ *
195+ * Portions Copyright (c) 2013-2020, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
196+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
197+ * Portions Copyright (c) 1994, Regents of the University of California
198+ *
199+ *-------------------------------------------------------------------------
200+ */
201+
202+/*
203+ * adjust_rows: tweak estimated row numbers according to the hint.
204+ */
205+static double
206+adjust_rows(double rows, RowsHint *hint)
207+{
208+ double result = 0.0; /* keep compiler quiet */
209+
210+ if (hint->value_type == RVT_ABSOLUTE)
211+ result = hint->rows;
212+ else if (hint->value_type == RVT_ADD)
213+ result = rows + hint->rows;
214+ else if (hint->value_type == RVT_SUB)
215+ result = rows - hint->rows;
216+ else if (hint->value_type == RVT_MULTI)
217+ result = rows * hint->rows;
218+ else
219+ Assert(false); /* unrecognized rows value type */
220+
221+ hint->base.state = HINT_STATE_USED;
222+ if (result < 1.0)
223+ ereport(WARNING,
224+ (errmsg("Force estimate to be at least one row, to avoid possible divide-by-zero when interpolating costs : %s",
225+ hint->base.hint_str)));
226+ result = clamp_row_est(result);
227+ elog(DEBUG1, "adjusted rows %d to %d", (int) rows, (int) result);
228+
229+ return result;
230+}
231+EOS
232+}
233+
234+
235+sub patch_make_join_rel
236+{
237+ open(my $out, '|-', 'patch') || die "failed to open pipe: $!";
238+
239+ print $out <<"EOS";
240+diff --git b/make_join_rel.c a/make_join_rel.c
241+index 0e7b99f..287e7f1 100644
242+--- b/make_join_rel.c
243++++ a/make_join_rel.c
244+@@ -126,6 +126,84 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
245+ joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
246+ &restrictlist);
247+
248++ /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
249++ {
250++ RowsHint *rows_hint = NULL;
251++ int i;
252++ RowsHint *justforme = NULL;
253++ RowsHint *domultiply = NULL;
254++
255++ /* Search for applicable rows hint for this join node */
256++ for (i = 0; i < current_hint_state->num_hints[HINT_TYPE_ROWS]; i++)
257++ {
258++ rows_hint = current_hint_state->rows_hints[i];
259++
260++ /*
261++ * Skip this rows_hint if it is invalid from the first or it
262++ * doesn't target any join rels.
263++ */
264++ if (!rows_hint->joinrelids ||
265++ rows_hint->base.state == HINT_STATE_ERROR)
266++ continue;
267++
268++ if (bms_equal(joinrelids, rows_hint->joinrelids))
269++ {
270++ /*
271++ * This joinrel is just the target of this rows_hint, so tweak
272++ * rows estimation according to the hint.
273++ */
274++ justforme = rows_hint;
275++ }
276++ else if (!(bms_is_subset(rows_hint->joinrelids, rel1->relids) ||
277++ bms_is_subset(rows_hint->joinrelids, rel2->relids)) &&
278++ bms_is_subset(rows_hint->joinrelids, joinrelids) &&
279++ rows_hint->value_type == RVT_MULTI)
280++ {
281++ /*
282++ * If the rows_hint's target relids is not a subset of both of
283++ * component rels and is a subset of this joinrel, ths hint's
284++ * targets spread over both component rels. This menas that
285++ * this hint has been never applied so far and this joinrel is
286++ * the first (and only) chance to fire in current join tree.
287++ * Only the multiplication hint has the cumulative nature so we
288++ * apply only RVT_MULTI in this way.
289++ */
290++ domultiply = rows_hint;
291++ }
292++ }
293++
294++ if (justforme)
295++ {
296++ /*
297++ * If a hint just for me is found, no other adjust method is
298++ * useles, but this cannot be more than twice becuase this joinrel
299++ * is already adjusted by this hint.
300++ */
301++ if (justforme->base.state == HINT_STATE_NOTUSED)
302++ joinrel->rows = adjust_rows(joinrel->rows, justforme);
303++ }
304++ else
305++ {
306++ if (domultiply)
307++ {
308++ /*
309++ * If we have multiple routes up to this joinrel which are not
310++ * applicable this hint, this multiply hint will applied more
311++ * than twice. But there's no means to know of that,
312++ * re-estimate the row number of this joinrel always just
313++ * before applying the hint. This is a bit different from
314++ * normal planner behavior but it doesn't harm so much.
315++ */
316++ set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
317++ restrictlist);
318++
319++ joinrel->rows = adjust_rows(joinrel->rows, domultiply);
320++ }
321++
322++ }
323++ }
324++ /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
325++
326+ /*
327+ * If we've already proven this join is empty, we needn't consider any
328+ * more paths for it.
329+EOS
330+}
Show on old repository browser