gpcontrib/pg_hint_plan/update_copied_funcs.pl (282 lines of code) (raw):

#! /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 }