OSDN Git Service

New tool to generate source files for copied functions
authorKyotaro Horiguchi <horikyoga.ntt@gmail.com>
Thu, 29 Oct 2020 04:45:37 +0000 (13:45 +0900)
committerKyotaro Horiguchi <horikyoga.ntt@gmail.com>
Thu, 29 Oct 2020 11:30:12 +0000 (20:30 +0900)
Previously core.c and make_join_rel.c are maintained by hand. This
tool generates the files from corresponding core source files.

update_copied_funcs.pl [new file with mode: 0755]

diff --git a/update_copied_funcs.pl b/update_copied_funcs.pl
new file mode 100755 (executable)
index 0000000..a5c7289
--- /dev/null
@@ -0,0 +1,325 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $srcpath;
+my @sources = (
+       'src/backend/optimizer/path/allpaths.c',
+       'src/backend/optimizer/path/joinrels.c');
+my %defs =
+  ('core.c'
+   => {protos => ['populate_joinrel_with_paths'],
+          funcs => ['set_plain_rel_pathlist',
+                                'set_append_rel_pathlist',
+                                'standard_join_search',
+                                'create_plain_partial_paths',
+                                'join_search_one_level',
+                                'make_rels_by_clause_joins',
+                                'make_rels_by_clauseless_joins',
+                                'join_is_legal',
+                                'has_join_restriction',
+                                'restriction_is_constant_false',
+                                'build_child_join_sjinfo',
+                                'try_partitionwise_join'],
+          head => core_c_head()},
+   'make_join_rel.c'
+   => {protos => [],
+          funcs => ['make_join_rel',
+                                'populate_joinrel_with_paths'],
+          head => make_join_rel_head()});
+       
+open (my $in, '-|', "objdump -W `which postgres`") || die "failed to objdump";
+while (<$in>)
+{
+       if (/DW_AT_comp_dir .*: (.*\/)src\/backend\//)
+       {
+               $srcpath = $1;
+               last;
+       }
+}
+close($in);
+
+die "source path not found" if (! defined $srcpath);
+#printf("Source path = %s\n", $srcpath);
+
+my %protos;
+my %funcs;
+my %func_is_static;
+my %func_source;
+
+for my $fname (@sources)
+{
+       my $f = $srcpath.$fname;
+       my $source;
+
+       open ($in, '<', $f) || die "failed to open $f: $!";
+       while (<$in>)
+       {
+               $source .= $_;
+       }
+
+       ## Collect static prototypes
+
+       while ($source =~ /\n(static [^\(\)\{\}]*?(\w+)(\([^\{\);]+?\);))/gsm)
+       {
+               #       print "Prototype found: $2\n";
+               $protos{$2} = $1;
+       }
+
+       ## Collect function bodies
+
+       while ($source =~ /(\n\/\*\n.+?\*\/\n(static )?(.+?)\n(.+?) *\(.*?\)\n\{.+?\n\}\n)/gsm)
+       {
+               $funcs{$4} = $1;
+               $func_is_static{$4} = (defined $2);
+               $func_source{$4} = $fname;
+
+                 #     printf("Function found: %s$4\n", $func_is_static{$4} ? "static " : "");
+       }
+
+       close($in);
+}
+
+
+# Generate files
+for my $fname (keys %defs)
+{
+       my %d = %{$defs{$fname}};
+
+       my @protonames = @{$d{'protos'}};
+       my @funcnames = @{$d{'funcs'}};
+       my $head = $d{'head'};
+
+       print "Generate $fname.\n";
+       open (my $out, '>', $fname) || die "could not open $fname: $!";
+
+       print $out $head;
+
+       for (@protonames)
+       {
+               print " Prototype: $_\n";
+               print $out "\n";
+               die "Prototype for $_ not found" if (! defined $protos{$_});
+               print $out $protos{$_};
+       }
+
+       for (@funcnames)
+       {
+               printf(" %s function: $_@%s\n",
+                          $func_is_static{$_}?"static":"public", $func_source{$_});
+               print $out "\n";
+               die "Function body for $_ not found" if (! defined $funcs{$_});
+               print $out $funcs{$_};
+       }
+
+       close($out);
+}
+
+# modify make_join_rel.c
+patch_make_join_rel();
+
+sub core_c_head()
+{
+       return << "EOS";
+/*-------------------------------------------------------------------------
+ *
+ * core.c
+ *       Routines copied from PostgreSQL core distribution.
+ *
+ * The main purpose of this files is having access to static functions in core.
+ * Another purpose is tweaking functions behavior by replacing part of them by
+ * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
+ * this file *must* contain required functions without making any change.
+ *
+ * This file contains the following functions from corresponding files.
+ *
+ * src/backend/optimizer/path/allpaths.c
+ *
+ *  public functions:
+ *     standard_join_search(): This funcion is not static. The reason for
+ *        including this function is make_rels_by_clause_joins. In order to
+ *        avoid generating apparently unwanted join combination, we decided to
+ *        change the behavior of make_join_rel, which is called under this
+ *        function.
+ *
+ *     static functions:
+ *        set_plain_rel_pathlist()
+ *        set_append_rel_pathlist()
+ *        create_plain_partial_paths()
+ *
+ * src/backend/optimizer/path/joinrels.c
+ *
+ *     public functions:
+ *     join_search_one_level(): We have to modify this to call my definition of
+ *                 make_rels_by_clause_joins.
+ *
+ *     static functions:
+ *     make_rels_by_clause_joins()
+ *     make_rels_by_clauseless_joins()
+ *     join_is_legal()
+ *     has_join_restriction()
+ *     restriction_is_constant_false()
+ *     build_child_join_sjinfo()
+ *     try_partitionwise_join()
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+EOS
+}
+
+sub make_join_rel_head
+{
+       return << "EOS";
+/*-------------------------------------------------------------------------
+ *
+ * make_join_rel.c
+ *       Routines copied from PostgreSQL core distribution with some
+ *       modifications.
+ *
+ * src/backend/optimizer/path/joinrels.c
+ *
+ * This file contains the following functions from corresponding files.
+ *
+ *     static functions:
+ *     make_join_rel()
+ *     populate_joinrel_with_paths()
+ *
+ * Portions Copyright (c) 2013-2020, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * adjust_rows: tweak estimated row numbers according to the hint.
+ */
+static double
+adjust_rows(double rows, RowsHint *hint)
+{
+       double          result = 0.0;   /* keep compiler quiet */
+
+       if (hint->value_type == RVT_ABSOLUTE)
+               result = hint->rows;
+       else if (hint->value_type == RVT_ADD)
+               result = rows + hint->rows;
+       else if (hint->value_type == RVT_SUB)
+               result =  rows - hint->rows;
+       else if (hint->value_type == RVT_MULTI)
+               result = rows * hint->rows;
+       else
+               Assert(false);  /* unrecognized rows value type */
+
+       hint->base.state = HINT_STATE_USED;
+       if (result < 1.0)
+               ereport(WARNING,
+                               (errmsg("Force estimate to be at least one row, to avoid possible divide-by-zero when interpolating costs : %s",
+                                       hint->base.hint_str)));
+       result = clamp_row_est(result);
+       elog(DEBUG1, "adjusted rows %d to %d", (int) rows, (int) result);
+
+       return result;
+}
+EOS
+}
+   
+
+sub patch_make_join_rel
+{
+       open(my $out, '|-', 'patch') || die "failed to open pipe: $!";
+
+       print $out <<"EOS";
+diff --git b/make_join_rel.c a/make_join_rel.c
+index 0e7b99f..287e7f1 100644
+--- b/make_join_rel.c
++++ a/make_join_rel.c
+@@ -126,6 +126,84 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
+       joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
+                                                        &restrictlist);
++      /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
++      {
++              RowsHint   *rows_hint = NULL;
++              int                     i;
++              RowsHint   *justforme = NULL;
++              RowsHint   *domultiply = NULL;
++
++              /* Search for applicable rows hint for this join node */
++              for (i = 0; i < current_hint_state->num_hints[HINT_TYPE_ROWS]; i++)
++              {
++                      rows_hint = current_hint_state->rows_hints[i];
++
++                      /*
++                       * Skip this rows_hint if it is invalid from the first or it
++                       * doesn't target any join rels.
++                       */
++                      if (!rows_hint->joinrelids ||
++                              rows_hint->base.state == HINT_STATE_ERROR)
++                              continue;
++
++                      if (bms_equal(joinrelids, rows_hint->joinrelids))
++                      {
++                              /*
++                               * This joinrel is just the target of this rows_hint, so tweak
++                               * rows estimation according to the hint.
++                               */
++                              justforme = rows_hint;
++                      }
++                      else if (!(bms_is_subset(rows_hint->joinrelids, rel1->relids) ||
++                                         bms_is_subset(rows_hint->joinrelids, rel2->relids)) &&
++                                       bms_is_subset(rows_hint->joinrelids, joinrelids) &&
++                                       rows_hint->value_type == RVT_MULTI)
++                      {
++                              /*
++                               * If the rows_hint's target relids is not a subset of both of
++                               * component rels and is a subset of this joinrel, ths hint's
++                               * targets spread over both component rels. This menas that
++                               * this hint has been never applied so far and this joinrel is
++                               * the first (and only) chance to fire in current join tree.
++                               * Only the multiplication hint has the cumulative nature so we
++                               * apply only RVT_MULTI in this way.
++                               */
++                              domultiply = rows_hint;
++                      }
++              }
++
++              if (justforme)
++              {
++                      /*
++                       * If a hint just for me is found, no other adjust method is
++                       * useles, but this cannot be more than twice becuase this joinrel
++                       * is already adjusted by this hint.
++                       */
++                      if (justforme->base.state == HINT_STATE_NOTUSED)
++                              joinrel->rows = adjust_rows(joinrel->rows, justforme);
++              }
++              else
++              {
++                      if (domultiply)
++                      {
++                              /*
++                               * If we have multiple routes up to this joinrel which are not
++                               * applicable this hint, this multiply hint will applied more
++                               * than twice. But there's no means to know of that,
++                               * re-estimate the row number of this joinrel always just
++                               * before applying the hint. This is a bit different from
++                               * normal planner behavior but it doesn't harm so much.
++                               */
++                              set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
++                                                                                 restrictlist);
++                              
++                              joinrel->rows = adjust_rows(joinrel->rows, domultiply);
++                      }
++                      
++              }
++      }
++      /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
++
+       /*
+        * If we've already proven this join is empty, we needn't consider any
+        * more paths for it.
+EOS
+}