 src/backend/catalog/genbki.pl     | 142 +++++++++++++++++++++++++++++++++++---
 src/include/catalog/genbki.h      |  10 +++
 src/include/catalog/pg_amop.h     |  10 +--
 src/include/catalog/pg_amproc.h   |   6 +-
 src/include/catalog/pg_opclass.h  |   8 +--
 src/include/catalog/pg_operator.h |   6 +-
 src/include/catalog/pg_opfamily.h |   2 +-
 7 files changed, 158 insertions(+), 26 deletions(-)

diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
new file mode 100644
index 973ffc2..a363cdd
*** a/src/backend/catalog/genbki.pl
--- b/src/backend/catalog/genbki.pl
*************** my $catalog_data = Catalog::ParseData(@d
*** 114,119 ****
--- 114,170 ----
  # tables here. However, if we need default values for a catalog, we
  # need to wait until the full tuples have been built.
  
+ # vars to hold lookups built later
+ my %regprocoids;
+ my @types;
+ 
+ # index access method OID lookup
+ my %regamoids;
+ foreach my $row (@{ $catalog_data->{pg_am} })
+ {
+ 	$regamoids{$row->{amname}} = $row->{oid};
+ }
+ 
+ # operator OID lookup
+ my %regoperoids;
+ foreach my $row (@{ $catalog_data->{pg_operator} })
+ {
+ 	# There is no unique name, so we need to invent one that contains
+ 	# the relevant type names.
+ 	# Note: assumes that we're only interested in binary operators.
+ 	if (defined $row->{oprleft} and defined $row->{oprright})
+ 	{
+ 		my $key = "$row->{oprname}($row->{oprleft},$row->{oprright})";
+ 		$regoperoids{$key} = $row->{oid};
+ 	}
+ }
+ 
+ # opfamily OID lookup
+ my %regopfoids;
+ foreach my $row (@{ $catalog_data->{pg_opfamily} })
+ {
+ 	# There is no unique name, so we need to combine access method
+ 	# and opfamily name.
+ 	$regopfoids{$row->{opfmethod} . '/' . $row->{opfname}} = $row->{oid};
+ }
+ 
+ # type OID lookup
+ # Note: We can't just use the same type lookup that pg_attribute uses,
+ # because pg_proc has type names and comes before pg_type in the input list.
+ my %regtypeoids;
+ foreach my $row (@{ $catalog_data->{pg_type} })
+ {
+ 	$regtypeoids{$row->{typname}} = $row->{oid};
+ }
+ 
+ # We use OID aliases to indicate when to do OID lookups, so column names
+ # have to be turned back into 'oid' before writing the CREATE command.
+ my %RENAME_REGOID = (
+ 	regam => 'oid',
+ 	regoper => 'oid',
+ 	regopf => 'oid',
+ 	regtype => 'oid');
+ 
  # Generate postgres.bki, postgres.description, and postgres.shdescription
  
  # version marker for .bki file
*************** print $bki "# PostgreSQL $major_version\
*** 122,129 ****
  # vars to hold data needed for schemapg.h
  my %schemapg_entries;
  my @tables_needing_macros;
- my %regprocoids;
- my @types;
  
  # Produce output, one catalog at a time.
  foreach my $catname (@{ $catalogs->{names} })
--- 173,178 ----
*************** foreach my $catname (@{ $catalogs->{name
*** 145,153 ****
  	foreach my $column (@$schema)
  	{
  		my $attname = $column->{name};
- 		my $atttype = $column->{type};
  		push @attnames, $attname;
  
  		if (!$first)
  		{
  			print $bki " ,\n";
--- 194,205 ----
  	foreach my $column (@$schema)
  	{
  		my $attname = $column->{name};
  		push @attnames, $attname;
  
+ 		my $atttype = $column->{type};
+ 		$atttype = $RENAME_REGOID{$atttype}
+ 		  if exists $RENAME_REGOID{$atttype};
+ 
  		if (!$first)
  		{
  			print $bki " ,\n";
*************** foreach my $catname (@{ $catalogs->{name
*** 201,221 ****
  				$bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
  				$bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
  
! 				# Replace regproc columns' values with OIDs.
! 				# If we don't have a unique value to substitute,
! 				# just do nothing (regprocin will complain).
! 				if ($atttype eq 'regproc')
  				{
  					my $procoid = $regprocoids{ $bki_values{$attname} };
  					$bki_values{$attname} = $procoid
  					  if defined($procoid) && $procoid ne 'MULTIPLE';
  				}
  			}
  
- 			# Save pg_proc oids for use in later regproc substitutions.
- 			# This relies on the order we process the files in!
  			if ($catname eq 'pg_proc')
  			{
  				if (defined($regprocoids{ $bki_values{proname} }))
  				{
  					$regprocoids{ $bki_values{proname} } = 'MULTIPLE';
--- 253,301 ----
  				$bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
  				$bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
  
! 				# Replace reg* columns' values with OIDs.
! 				if ($atttype eq 'regam')
! 				{
! 					my $amoid = $regamoids{ $bki_values{$attname} };
! 					$bki_values{$attname} = $amoid
! 					  if defined($amoid);
! 				}
! 				elsif ($atttype eq 'regopf')
! 				{
! 					my $opfoid = $regopfoids{ $bki_values{$attname} };
! 					$bki_values{$attname} = $opfoid
! 					  if defined($opfoid);
! 				}
! 				elsif ($atttype eq 'regoper')
! 				{
! 					my $operoid = $regoperoids{ $bki_values{$attname} };
! 					$bki_values{$attname} = $operoid
! 					  if defined($operoid);
! 				}
! 				elsif ($atttype eq 'regproc')
  				{
  					my $procoid = $regprocoids{ $bki_values{$attname} };
+ 
+ 					# If we don't have a unique value to substitute,
+ 					# just do nothing (regprocin will complain).
  					$bki_values{$attname} = $procoid
  					  if defined($procoid) && $procoid ne 'MULTIPLE';
  				}
+ 				elsif ($atttype eq 'regtype')
+ 				{
+ 					my $typeoid = $regtypeoids{ $bki_values{$attname} };
+ 					$bki_values{$attname} = $typeoid
+ 					  if defined($typeoid);
+ 				}
  			}
  
  			if ($catname eq 'pg_proc')
  			{
+ 				# Save pg_proc oids for use in later regproc substitutions.
+ 				# We build this lookup after the forming the full tuple,
+ 				# since otherwise the script will break if the abbreviation
+ 				# for 'proname' is changed. This is fine since pg_proc
+ 				# comes first in the input list.
  				if (defined($regprocoids{ $bki_values{proname} }))
  				{
  					$regprocoids{ $bki_values{proname} } = 'MULTIPLE';
*************** foreach my $catname (@{ $catalogs->{name
*** 224,232 ****
  				{
  					$regprocoids{ $bki_values{proname} } = $bki_values{oid};
  				}
  			}
  
! 			# Save pg_type info for pg_attribute processing below
  			if ($catname eq 'pg_type')
  			{
  				my %type = %bki_values;
--- 304,354 ----
  				{
  					$regprocoids{ $bki_values{proname} } = $bki_values{oid};
  				}
+ 
+ 				# We can't do type lookups in a general way for pg_proc,
+ 				# so do special handling here.
+ 
+ 				# prorettype
+ 				# Note: We could handle this automatically by using the
+ 				# 'regtype' alias, but then we would have to teach
+ 				# emit_pgattr_row() to change it back to oid. Since we
+ 				# have to treat pg_proc differently anyway, just do the
+ 				# type lookup manually here.
+ 				my $rettypeoid = $regtypeoids{ $bki_values{prorettype}};
+ 				$bki_values{prorettype} = $rettypeoid
+ 				  if defined($rettypeoid);
+ 
+ 				# proargtypes
+ 				if ($bki_values{proargtypes})
+ 				{
+ 					my @argtypenames = split /\s+/, $bki_values{proargtypes};
+ 					my @argtypeoids;
+ 					foreach my $argtypename (@argtypenames)
+ 					{
+ 						my $argtypeoid  = $regtypeoids{$argtypename};
+ 						push @argtypeoids, $argtypeoid;
+ 					}
+ 					$bki_values{proargtypes} = join(' ', @argtypeoids);
+ 				}
+ 
+ 				# proallargtypes
+ 				if ($bki_values{proallargtypes} ne '_null_')
+ 				{
+ 					$bki_values{proallargtypes} =~ s/[{}]//g;
+ 					my @argtypenames = split /,/, $bki_values{proallargtypes};
+ 					my @argtypeoids;
+ 					foreach my $argtypename (@argtypenames)
+ 					{
+ 						my $argtypeoid  = $regtypeoids{$argtypename};
+ 						push @argtypeoids, $argtypeoid;
+ 					}
+ 					$bki_values{proallargtypes} =
+ 						'{' . join(',', @argtypeoids) . '}';
+ 				}
  			}
  
! 			# Save pg_type info for pg_attribute processing below.
! 			# We need to build this lookup after the forming the full tuple.
  			if ($catname eq 'pg_type')
  			{
  				my %type = %bki_values;
diff --git a/src/include/catalog/genbki.h b/src/include/catalog/genbki.h
new file mode 100644
index c3ffa29..39e9b59
*** a/src/include/catalog/genbki.h
--- b/src/include/catalog/genbki.h
***************
*** 37,42 ****
--- 37,52 ----
  /* Specifies an abbreviated label for a column name */
  #define BKI_ABBREV(abb)
  
+ /* ----------------
+  *	Some columns of type Oid have human-readable entries that are
+  *	resolved when creating postgres.bki.
+  * ----------------
+  */
+ #define regam Oid
+ #define regoper Oid
+ #define regopf Oid
+ #define regtype Oid
+ 
  /*
   * This is never defined; it's here only for documentation.
   *
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
new file mode 100644
index 5724e3f..3d9e9e7
*** a/src/include/catalog/pg_amop.h
--- b/src/include/catalog/pg_amop.h
***************
*** 56,68 ****
  CATALOG(pg_amop,2602)
  {
  	/* the index opfamily this entry is for */
! 	Oid			amopfamily BKI_ABBREV(opf);
  
  	/* operator's left input data type */
! 	Oid			amoplefttype BKI_ABBREV(lt);
  
  	/* operator's right input data type */
! 	Oid			amoprighttype BKI_ABBREV(rt);
  
  	/* operator strategy number */
  	int16		amopstrategy BKI_ABBREV(str);
--- 56,68 ----
  CATALOG(pg_amop,2602)
  {
  	/* the index opfamily this entry is for */
! 	regopf		amopfamily BKI_ABBREV(opf);
  
  	/* operator's left input data type */
! 	regtype		amoplefttype BKI_ABBREV(lt);
  
  	/* operator's right input data type */
! 	regtype		amoprighttype BKI_ABBREV(rt);
  
  	/* operator strategy number */
  	int16		amopstrategy BKI_ABBREV(str);
*************** CATALOG(pg_amop,2602)
*** 71,80 ****
  	char		amoppurpose BKI_ABBREV(pur)  BKI_DEFAULT(s);
  
  	/* the operator's pg_operator OID */
! 	Oid			amopopr BKI_ABBREV(oper);
  
  	/* the index access method this entry is for */
! 	Oid			amopmethod BKI_ABBREV(am);
  
  	/* ordering opfamily OID, or 0 if search op */
  	Oid			amopsortfamily BKI_DEFAULT(0);
--- 71,80 ----
  	char		amoppurpose BKI_ABBREV(pur)  BKI_DEFAULT(s);
  
  	/* the operator's pg_operator OID */
! 	regoper		amopopr BKI_ABBREV(oper);
  
  	/* the index access method this entry is for */
! 	regam		amopmethod BKI_ABBREV(am);
  
  	/* ordering opfamily OID, or 0 if search op */
  	Oid			amopsortfamily BKI_DEFAULT(0);
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
new file mode 100644
index afdfeb1..17ed0e2
*** a/src/include/catalog/pg_amproc.h
--- b/src/include/catalog/pg_amproc.h
***************
*** 45,57 ****
  CATALOG(pg_amproc,2603)
  {
  	/* the index opfamily this entry is for */
! 	Oid			amprocfamily BKI_ABBREV(opf);
  
  	/* procedure's left input data type */
! 	Oid			amproclefttype BKI_ABBREV(lt);
  
  	/* procedure's right input data type */
! 	Oid			amprocrighttype BKI_ABBREV(rt);
  
  	/* support procedure index */
  	int16		amprocnum BKI_ABBREV(num);
--- 45,57 ----
  CATALOG(pg_amproc,2603)
  {
  	/* the index opfamily this entry is for */
! 	regopf		amprocfamily BKI_ABBREV(opf);
  
  	/* procedure's left input data type */
! 	regtype		amproclefttype BKI_ABBREV(lt);
  
  	/* procedure's right input data type */
! 	regtype		amprocrighttype BKI_ABBREV(rt);
  
  	/* support procedure index */
  	int16		amprocnum BKI_ABBREV(num);
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
new file mode 100644
index e0e4f62..472511c
*** a/src/include/catalog/pg_opclass.h
--- b/src/include/catalog/pg_opclass.h
***************
*** 50,56 ****
  
  CATALOG(pg_opclass,2616)
  {
! 	Oid			opcmethod;		/* index access method opclass is for */
  	NameData	opcname;		/* name of this opclass */
  
  	/* namespace of this opclass */
--- 50,56 ----
  
  CATALOG(pg_opclass,2616)
  {
! 	regam		opcmethod;		/* index access method opclass is for */
  	NameData	opcname;		/* name of this opclass */
  
  	/* namespace of this opclass */
*************** CATALOG(pg_opclass,2616)
*** 59,72 ****
  	/* opclass owner */
  	Oid			opcowner BKI_DEFAULT(PGUID);
  
! 	Oid			opcfamily;		/* containing operator family */
! 	Oid			opcintype;		/* type of data indexed by opclass */
  
  	/* T if opclass is default for opcintype */
  	bool		opcdefault BKI_DEFAULT(t);
  
  	/* type of data in index, or InvalidOid */
! 	Oid			opckeytype BKI_DEFAULT(0);
  } FormData_pg_opclass;
  
  /* ----------------
--- 59,72 ----
  	/* opclass owner */
  	Oid			opcowner BKI_DEFAULT(PGUID);
  
! 	regopf		opcfamily;		/* containing operator family */
! 	regtype		opcintype;		/* type of data indexed by opclass */
  
  	/* T if opclass is default for opcintype */
  	bool		opcdefault BKI_DEFAULT(t);
  
  	/* type of data in index, or InvalidOid */
! 	regtype		opckeytype BKI_DEFAULT(0);
  } FormData_pg_opclass;
  
  /* ----------------
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
new file mode 100644
index fe6b660..8ca7621
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
*************** CATALOG(pg_operator,2617)
*** 50,62 ****
  	bool		oprcanhash BKI_DEFAULT(f);
  
  	/* left arg type, or 0 if 'l' oprkind */
! 	Oid			oprleft;
  
  	/* right arg type, or 0 if 'r' oprkind */
! 	Oid			oprright;
  
  	/* result datatype */
! 	Oid			oprresult;
  
  	/* OID of commutator oper, or 0 if none */
  	Oid			oprcom BKI_DEFAULT(0);
--- 50,62 ----
  	bool		oprcanhash BKI_DEFAULT(f);
  
  	/* left arg type, or 0 if 'l' oprkind */
! 	regtype		oprleft BKI_DEFAULT(0);
  
  	/* right arg type, or 0 if 'r' oprkind */
! 	regtype		oprright BKI_DEFAULT(0);
  
  	/* result datatype */
! 	regtype		oprresult;
  
  	/* OID of commutator oper, or 0 if none */
  	Oid			oprcom BKI_DEFAULT(0);
diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h
new file mode 100644
index b683770..2f410b4
*** a/src/include/catalog/pg_opfamily.h
--- b/src/include/catalog/pg_opfamily.h
***************
*** 30,36 ****
  
  CATALOG(pg_opfamily,2753)
  {
! 	Oid			opfmethod;		/* index access method opfamily is for */
  	NameData	opfname;		/* name of this opfamily */
  
  	/* namespace of this opfamily */
--- 30,36 ----
  
  CATALOG(pg_opfamily,2753)
  {
! 	regam		opfmethod;		/* index access method opfamily is for */
  	NameData	opfname;		/* name of this opfamily */
  
  	/* namespace of this opfamily */
