diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index 85af773..731eb43 100644
*** a/src/bin/pg_upgrade/info.c
--- b/src/bin/pg_upgrade/info.c
*************** static void create_rel_filename_map(cons
*** 18,23 ****
--- 18,25 ----
  						const DbInfo *old_db, const DbInfo *new_db,
  						const RelInfo *old_rel, const RelInfo *new_rel,
  						FileNameMap *map);
+ static void report_unmatched_relation(const RelInfo *rel, const DbInfo *db,
+ 						  bool is_new_db);
  static void free_db_and_rel_infos(DbInfoArr *db_arr);
  static void get_db_infos(ClusterInfo *cluster);
  static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo);
*************** static void print_rel_infos(RelInfoArr *
*** 29,127 ****
  /*
   * gen_db_file_maps()
   *
!  * generates database mappings for "old_db" and "new_db". Returns a malloc'ed
!  * array of mappings. nmaps is a return parameter which refers to the number
!  * mappings.
   */
  FileNameMap *
  gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
! 				 int *nmaps, const char *old_pgdata, const char *new_pgdata)
  {
  	FileNameMap *maps;
  	int			old_relnum,
  				new_relnum;
  	int			num_maps = 0;
  
  	maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) *
  									 old_db->rel_arr.nrels);
  
  	/*
! 	 * The old database shouldn't have more relations than the new one. We
! 	 * force the new cluster to have a TOAST table if the old table had one.
  	 */
! 	if (old_db->rel_arr.nrels > new_db->rel_arr.nrels)
! 		pg_fatal("old and new databases \"%s\" have a mismatched number of relations\n",
! 				 old_db->db_name);
! 
! 	/* Drive the loop using new_relnum, which might be higher. */
! 	for (old_relnum = new_relnum = 0; new_relnum < new_db->rel_arr.nrels;
! 		 new_relnum++)
  	{
! 		RelInfo    *old_rel;
! 		RelInfo    *new_rel = &new_db->rel_arr.rels[new_relnum];
  
! 		/*
! 		 * It is possible that the new cluster has a TOAST table for a table
! 		 * that didn't need one in the old cluster, e.g. 9.0 to 9.1 changed
! 		 * the NUMERIC length computation.  Therefore, if we have a TOAST
! 		 * table in the new cluster that doesn't match, skip over it and
! 		 * continue processing.  It is possible this TOAST table used an OID
! 		 * that was reserved in the old cluster, but we have no way of testing
! 		 * that, and we would have already gotten an error at the new cluster
! 		 * schema creation stage.  Fortunately, since we only restore the OID
! 		 * counter after schema restore, and restore in OID order via pg_dump,
! 		 * a conflict would only happen if the new TOAST table had a very low
! 		 * OID.  However, TOAST tables created long after initial table
! 		 * creation can have any OID, particularly after OID wraparound.
! 		 */
! 		if (old_relnum == old_db->rel_arr.nrels)
  		{
! 			if (strcmp(new_rel->nspname, "pg_toast") == 0)
! 				continue;
! 			else
! 				pg_fatal("Extra non-TOAST relation found in database \"%s\": new OID %d\n",
! 						 old_db->db_name, new_rel->reloid);
  		}
  
! 		old_rel = &old_db->rel_arr.rels[old_relnum];
! 
! 		if (old_rel->reloid != new_rel->reloid)
  		{
! 			if (strcmp(new_rel->nspname, "pg_toast") == 0)
! 				continue;
! 			else
! 				pg_fatal("Mismatch of relation OID in database \"%s\": old OID %d, new OID %d\n",
! 						 old_db->db_name, old_rel->reloid, new_rel->reloid);
  		}
  
  		/*
! 		 * TOAST table names initially match the heap pg_class oid. In
! 		 * pre-8.4, TOAST table names change during CLUSTER; in pre-9.0, TOAST
! 		 * table names change during ALTER TABLE ALTER COLUMN SET TYPE. In >=
! 		 * 9.0, TOAST relation names always use heap table oids, hence we
! 		 * cannot check relation names when upgrading from pre-9.0. Clusters
! 		 * upgraded to 9.0 will get matching TOAST names. If index names don't
! 		 * match primary key constraint names, this will fail because pg_dump
! 		 * dumps constraint names and pg_upgrade checks index names.
  		 */
  		if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
! 			((GET_MAJOR_VERSION(old_cluster.major_version) >= 900 ||
! 			  strcmp(old_rel->nspname, "pg_toast") != 0) &&
! 			 strcmp(old_rel->relname, new_rel->relname) != 0))
! 			pg_fatal("Mismatch of relation names in database \"%s\": "
! 					 "old name \"%s.%s\", new name \"%s.%s\"\n",
! 					 old_db->db_name, old_rel->nspname, old_rel->relname,
! 					 new_rel->nspname, new_rel->relname);
  
  		create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db,
  								old_rel, new_rel, maps + num_maps);
  		num_maps++;
  		old_relnum++;
  	}
  
! 	/* Did we fail to exhaust the old array? */
! 	if (old_relnum != old_db->rel_arr.nrels)
! 		pg_fatal("old and new databases \"%s\" have a mismatched number of relations\n",
  				 old_db->db_name);
  
  	*nmaps = num_maps;
--- 31,155 ----
  /*
   * gen_db_file_maps()
   *
!  * generates a database mapping from "old_db" to "new_db".
!  *
!  * Returns a malloc'ed array of mappings.  The length of the array
!  * is returned into *nmaps.
   */
  FileNameMap *
  gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
! 				 int *nmaps,
! 				 const char *old_pgdata, const char *new_pgdata)
  {
  	FileNameMap *maps;
  	int			old_relnum,
  				new_relnum;
  	int			num_maps = 0;
+ 	bool		all_matched = true;
  
+ 	/* There will certainly not be more mappings than there are old rels */
  	maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) *
  									 old_db->rel_arr.nrels);
  
  	/*
! 	 * Each of the RelInfo arrays should be sorted by OID.  Scan through them
! 	 * and match them up.  If we fail to match everything, we'll abort, but
! 	 * first print as much info as we can about mismatches.
  	 */
! 	old_relnum = new_relnum = 0;
! 	while (old_relnum < old_db->rel_arr.nrels ||
! 		   new_relnum < new_db->rel_arr.nrels)
  	{
! 		RelInfo    *old_rel = (old_relnum < old_db->rel_arr.nrels) ?
! 		&old_db->rel_arr.rels[old_relnum] : NULL;
! 		RelInfo    *new_rel = (new_relnum < new_db->rel_arr.nrels) ?
! 		&new_db->rel_arr.rels[new_relnum] : NULL;
  
! 		/* handle running off one array before the other */
! 		if (!new_rel)
  		{
! 			/*
! 			 * old_rel is unmatched.  This should never happen, because we
! 			 * force new rels to have TOAST tables if the old one did.
! 			 */
! 			report_unmatched_relation(old_rel, old_db, false);
! 			all_matched = false;
! 			old_relnum++;
! 			continue;
! 		}
! 		if (!old_rel)
! 		{
! 			/*
! 			 * new_rel is unmatched.  This shouldn't really happen either, but
! 			 * if it's a TOAST table, we can ignore it and continue
! 			 * processing, assuming that the new server made a TOAST table
! 			 * that wasn't needed.
! 			 */
! 			if (strcmp(new_rel->nspname, "pg_toast") != 0)
! 			{
! 				report_unmatched_relation(new_rel, new_db, true);
! 				all_matched = false;
! 			}
! 			new_relnum++;
! 			continue;
  		}
  
! 		/* check for mismatched OID */
! 		if (old_rel->reloid < new_rel->reloid)
  		{
! 			/* old_rel is unmatched, see comment above */
! 			report_unmatched_relation(old_rel, old_db, false);
! 			all_matched = false;
! 			old_relnum++;
! 			continue;
! 		}
! 		else if (old_rel->reloid > new_rel->reloid)
! 		{
! 			/* new_rel is unmatched, see comment above */
! 			if (strcmp(new_rel->nspname, "pg_toast") != 0)
! 			{
! 				report_unmatched_relation(new_rel, new_db, true);
! 				all_matched = false;
! 			}
! 			new_relnum++;
! 			continue;
  		}
  
  		/*
! 		 * Verify that rels of same OID have same name.  The namespace name
! 		 * should always match, but the relname might not match for TOAST
! 		 * tables (and, therefore, their indexes).
! 		 *
! 		 * TOAST table names initially match the heap pg_class oid, but
! 		 * pre-9.0 they can change during certain commands such as CLUSTER, so
! 		 * don't insist on a match if old cluster is < 9.0.
  		 */
  		if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
! 			(strcmp(old_rel->relname, new_rel->relname) != 0 &&
! 			 (GET_MAJOR_VERSION(old_cluster.major_version) >= 900 ||
! 			  strcmp(old_rel->nspname, "pg_toast") != 0)))
! 		{
! 			pg_log(PG_WARNING, "Relation names for OID %u in database \"%s\" do not match: "
! 				   "old name \"%s.%s\", new name \"%s.%s\"\n",
! 				   old_rel->reloid, old_db->db_name,
! 				   old_rel->nspname, old_rel->relname,
! 				   new_rel->nspname, new_rel->relname);
! 			all_matched = false;
! 			old_relnum++;
! 			new_relnum++;
! 			continue;
! 		}
  
+ 		/* OK, create a mapping entry */
  		create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db,
  								old_rel, new_rel, maps + num_maps);
  		num_maps++;
  		old_relnum++;
+ 		new_relnum++;
  	}
  
! 	if (!all_matched)
! 		pg_fatal("Failed to match up old and new tables in database \"%s\"\n",
  				 old_db->db_name);
  
  	*nmaps = num_maps;
*************** create_rel_filename_map(const char *old_
*** 187,192 ****
--- 215,285 ----
  }
  
  
+ /*
+  * Complain about a relation we couldn't match to the other database,
+  * identifying it as best we can.
+  */
+ static void
+ report_unmatched_relation(const RelInfo *rel, const DbInfo *db, bool is_new_db)
+ {
+ 	Oid			reloid = rel->reloid;	/* we might change rel below */
+ 	char		reldesc[1000];
+ 	int			i;
+ 
+ 	snprintf(reldesc, sizeof(reldesc), "\"%s.%s\"",
+ 			 rel->nspname, rel->relname);
+ 	if (rel->indtable)
+ 	{
+ 		for (i = 0; i < db->rel_arr.nrels; i++)
+ 		{
+ 			const RelInfo *hrel = &db->rel_arr.rels[i];
+ 
+ 			if (hrel->reloid == rel->indtable)
+ 			{
+ 				snprintf(reldesc + strlen(reldesc),
+ 						 sizeof(reldesc) - strlen(reldesc),
+ 						 ", index on \"%s.%s\"",
+ 						 hrel->nspname, hrel->relname);
+ 				/* Shift attention to index's table for toast check */
+ 				rel = hrel;
+ 				break;
+ 			}
+ 		}
+ 		if (i >= db->rel_arr.nrels)
+ 			snprintf(reldesc + strlen(reldesc),
+ 					 sizeof(reldesc) - strlen(reldesc),
+ 					 ", index on OID %u", rel->indtable);
+ 	}
+ 	if (rel->toastheap)
+ 	{
+ 		for (i = 0; i < db->rel_arr.nrels; i++)
+ 		{
+ 			const RelInfo *brel = &db->rel_arr.rels[i];
+ 
+ 			if (brel->reloid == rel->toastheap)
+ 			{
+ 				snprintf(reldesc + strlen(reldesc),
+ 						 sizeof(reldesc) - strlen(reldesc),
+ 						 ", toast table for \"%s.%s\"",
+ 						 brel->nspname, brel->relname);
+ 				break;
+ 			}
+ 		}
+ 		if (i >= db->rel_arr.nrels)
+ 			snprintf(reldesc + strlen(reldesc),
+ 					 sizeof(reldesc) - strlen(reldesc),
+ 					 ", toast table for OID %u", rel->toastheap);
+ 	}
+ 
+ 	if (is_new_db)
+ 		pg_log(PG_WARNING, "No match found in old cluster for new relation with OID %u in database \"%s\": %s\n",
+ 			   reloid, db->db_name, reldesc);
+ 	else
+ 		pg_log(PG_WARNING, "No match found in new cluster for old relation with OID %u in database \"%s\": %s\n",
+ 			   reloid, db->db_name, reldesc);
+ }
+ 
+ 
  void
  print_maps(FileNameMap *maps, int n_maps, const char *db_name)
  {
