• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

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