diff --git a/src/tools/findoidjoins/bogus_oidjoins.txt b/src/tools/findoidjoins/bogus_oidjoins.txt
new file mode 100644
index 0000000000..66e14a0b3c
--- /dev/null
+++ b/src/tools/findoidjoins/bogus_oidjoins.txt
@@ -0,0 +1,67 @@
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_amop.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_amproc.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_attrdef.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_cast.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_class.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_constraint.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_foreign_server.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_language.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_opclass.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_operator.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_opfamily.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_policy.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_proc.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_rewrite.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_statistic_ext.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_trigger.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_ts_config.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_ts_dict.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_ts_template.oid
+- Join pg_catalog.pg_depend.objid => pg_catalog.pg_type.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_am.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_amop.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_amproc.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_cast.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_class.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_collation.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_constraint.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_conversion.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_extension.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_foreign_data_wrapper.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_language.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_namespace.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_opclass.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_operator.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_opfamily.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_proc.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_transform.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_trigger.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_ts_config.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_ts_dict.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_ts_parser.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_ts_template.oid
+- Join pg_catalog.pg_depend.refobjid => pg_catalog.pg_type.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_am.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_class.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_collation.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_constraint.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_conversion.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_extension.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_language.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_largeobject_metadata.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_namespace.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_operator.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_proc.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_ts_config.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_ts_dict.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_ts_parser.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_ts_template.oid
+- Join pg_catalog.pg_description.objoid => pg_catalog.pg_type.oid
+- Join pg_catalog.pg_class.relfilenode => pg_catalog.pg_class.oid
+- Join pg_catalog.pg_database.datlastsysoid => pg_catalog.pg_database.oid
++ Join pg_catalog.pg_shdepend.classid => pg_catalog.pg_class.oid
++ Join pg_catalog.pg_shdepend.dbid => pg_catalog.pg_database.oid
+- Join pg_catalog.pg_shdepend.refobjid => pg_catalog.pg_authid.oid
+- Join pg_catalog.pg_init_privs.objoid => pg_catalog.pg_class.oid
+- Join pg_catalog.pg_init_privs.objoid => pg_catalog.pg_namespace.oid
+- Join pg_catalog.pg_init_privs.objoid => pg_catalog.pg_proc.oid
diff --git a/src/tools/findoidjoins/catalog_oidjoins.pl b/src/tools/findoidjoins/catalog_oidjoins.pl
new file mode 100755
index 0000000000..48165c4abe
--- /dev/null
+++ b/src/tools/findoidjoins/catalog_oidjoins.pl
@@ -0,0 +1,90 @@
+#! /usr/bin/perl
+
+#######################################################################
+#
+# catalog_oidjoins.pl -- parses catalog references out of catalogs.sgml
+#
+# Copyright (c) 2021, PostgreSQL Global Development Group
+#
+# src/tools/findoidjoins/catalog_oidjoins.pl
+#######################################################################
+
+use strict;
+use warnings;
+
+use XML::Simple;
+use File::Slurp qw(slurp);
+use Cwd 'abs_path';
+use Data::Dumper;
+
+my $filename = shift || abs_path() . "/../../../doc/src/sgml/catalogs.sgml";
+
+die "Usage: $0 [path to catalogs.sgml]\n" unless -e $filename;
+
+# Need to define &mdash; entity
+my $doc = <<EOF
+<!DOCTYPE doc [
+    <!ENTITY mdash "&#8212;" >
+]>
+EOF
+. slurp($filename);
+
+my $xml = new XML::Simple;
+my $o = $xml->XMLin($doc);
+
+foreach my $id (grep { /^catalog-pg-/ } sort keys %{$o->{sect1}}) {
+  my $sect = $o->{sect1}->{$id};
+  next unless exists $sect->{title};
+  my $title = $sect->{title};
+  next unless exists $title->{structname};
+  my $from_rel = $title->{structname};
+  next unless exists $sect->{table};
+  my $table = $sect->{table};
+  if (ref($table) eq 'ARRAY') {
+    $table = $table->[0];
+  }
+  next unless exists $table->{tgroup};
+  my $tgroup = $table->{tgroup};
+  next unless exists $tgroup->{tbody};
+  my $tbody = $tgroup->{tbody};
+  next unless exists $tbody->{row};
+  my $row = $tbody->{row};
+  if (ref($row) eq 'HASH') {
+    $row = [$row];
+  }
+  foreach my $r (@{$row}) {
+    next unless exists $r->{entry};
+    my $entry = $r->{entry};
+    next unless exists $entry->{role} && $entry->{role} eq 'catalog_table_entry';
+    next unless exists $entry->{para};
+    my $para = $entry->{para};
+    next unless ref($para) eq 'ARRAY' && scalar @{$para} == 2;
+    my $p = $para->[0];
+    next unless exists $p->{role} && $p->{role} eq 'column_definition';
+    next unless exists $p->{link};
+    my $link = $p->{link};
+    next unless exists $link->{structname};
+    my $to_rel = $link->{structname};
+    next unless exists $p->{type};
+    my $type = $p->{type};
+    my $arrow = $type =~ m/\[\]$|^oidvector$/ ? "[]=>" : "=>";
+    my $structfield = $p->{structfield};
+    next unless ref($structfield) eq 'ARRAY' && scalar @{$structfield} == 2;
+    my ($from_col, $to_col) = @{$structfield};
+    if (ref($from_col) eq 'HASH'
+      && exists $from_col->{replaceable}
+      && $from_col->{replaceable} eq 'N'
+      && $from_rel eq 'pg_statistic'
+      && exists $from_col->{content}
+    ) {
+      my $content = $from_col->{content};
+      foreach my $n (1..5) {
+        print "Join pg_catalog.$from_rel.$content$n $arrow pg_catalog.$to_rel.$to_col\n";
+      }
+    } elsif (ref($from_col) eq '') {
+      print "Join pg_catalog.$from_rel.$from_col $arrow pg_catalog.$to_rel.$to_col\n";
+    } else {
+      print Dumper $from_col;
+    }
+  }
+}
diff --git a/src/tools/findoidjoins/diff_oidjoins.sh b/src/tools/findoidjoins/diff_oidjoins.sh
new file mode 100755
index 0000000000..aaf86daad5
--- /dev/null
+++ b/src/tools/findoidjoins/diff_oidjoins.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+sort - > tmp1
+./catalog_oidjoins.pl | sort > tmp2
+diff -C 0 tmp1 tmp2 | grep -E '^[+-] '
+rm tmp1 tmp2
diff --git a/src/tools/findoidjoins/test_oidjoins.sh b/src/tools/findoidjoins/test_oidjoins.sh
new file mode 100755
index 0000000000..1620ff1906
--- /dev/null
+++ b/src/tools/findoidjoins/test_oidjoins.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+echo "README:"
+tail -n+60 README | grep -E '^Join' | ./diff_oidjoins.sh \
+  | grep -v -f bogus_oidjoins.txt | tee diff1
+
+echo "findoidjoins:"
+./findoidjoins regression | ./diff_oidjoins.sh \
+  | grep -v -f bogus_oidjoins.txt | tee diff2
+
+echo "diff of diffs:"
+diff diff1 diff2
+
+rm diff1 diff2
