20130701_1_remove_reltoastidxid_v15_context.patch

application/octet-stream

Filename: 20130701_1_remove_reltoastidxid_v15_context.patch
Type: application/octet-stream
Part: 1
Message: Re: Support for REINDEX CONCURRENTLY

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
Series: patch v15
File+
contrib/pg_upgrade/info.c 11 0
doc/src/sgml/catalogs.sgml 0 9
doc/src/sgml/diskusage.sgml 13 7
doc/src/sgml/monitoring.sgml 2 0
src/backend/access/heap/tuptoaster.c 206 0
src/backend/catalog/heap.c 0 0
src/backend/catalog/index.c 4 0
src/backend/catalog/system_views.sql 5 0
src/backend/commands/cluster.c 22 0
src/backend/commands/tablecmds.c 15 0
src/backend/rewrite/rewriteDefine.c 2 0
src/backend/utils/adt/dbsize.c 17 0
src/bin/pg_dump/pg_dump.c 22 0
src/include/access/tuptoaster.h 9 0
src/include/catalog/pg_class.h 21 0
src/test/regress/expected/oidjoins.out 0 0
src/test/regress/expected/rules.out 8 0
src/test/regress/sql/oidjoins.sql 0 0
src/test/regress/sql/rules.sql 1 0
src/tools/findoidjoins/README 0 0
*** a/contrib/pg_upgrade/info.c
--- b/contrib/pg_upgrade/info.c
***************
*** 321,332 **** get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
  							  "INSERT INTO info_rels "
  							  "SELECT reltoastrelid "
  							  "FROM info_rels i JOIN pg_catalog.pg_class c "
! 							  "		ON i.reloid = c.oid"));
  	PQclear(executeQueryOrDie(conn,
  							  "INSERT INTO info_rels "
! 							  "SELECT reltoastidxid "
! 							  "FROM info_rels i JOIN pg_catalog.pg_class c "
! 							  "		ON i.reloid = c.oid"));
  
  	snprintf(query, sizeof(query),
  			 "SELECT c.oid, n.nspname, c.relname, "
--- 321,339 ----
  							  "INSERT INTO info_rels "
  							  "SELECT reltoastrelid "
  							  "FROM info_rels i JOIN pg_catalog.pg_class c "
! 							  "		ON i.reloid = c.oid "
! 							  "		AND c.reltoastrelid != %u", InvalidOid));
  	PQclear(executeQueryOrDie(conn,
  							  "INSERT INTO info_rels "
! 							  "SELECT indexrelid "
! 							  "FROM pg_index "
! 							  "WHERE indisvalid "
! 							  "    AND indrelid IN (SELECT reltoastrelid "
! 							  "        FROM info_rels i "
! 							  "            JOIN pg_catalog.pg_class c "
! 							  "            ON i.reloid = c.oid "
! 							  "            AND c.reltoastrelid != %u)",
! 							  InvalidOid));
  
  	snprintf(query, sizeof(query),
  			 "SELECT c.oid, n.nspname, c.relname, "
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1745,1759 ****
       </row>
  
       <row>
-       <entry><structfield>reltoastidxid</structfield></entry>
-       <entry><type>oid</type></entry>
-       <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
-       <entry>
-        For a TOAST table, the OID of its index.  0 if not a TOAST table.
-       </entry>
-      </row>
- 
-      <row>
        <entry><structfield>relhasindex</structfield></entry>
        <entry><type>bool</type></entry>
        <entry></entry>
--- 1745,1750 ----
*** a/doc/src/sgml/diskusage.sgml
--- b/doc/src/sgml/diskusage.sgml
***************
*** 20,31 ****
     stored. If the table has any columns with potentially-wide values,
     there also might be a <acronym>TOAST</> file associated with the table,
     which is used to store values too wide to fit comfortably in the main
!    table (see <xref linkend="storage-toast">).  There will be one index on the
!    <acronym>TOAST</> table, if present. There also might be indexes associated
!    with the base table.  Each table and index is stored in a separate disk
!    file &mdash; possibly more than one file, if the file would exceed one
!    gigabyte.  Naming conventions for these files are described in <xref
!    linkend="storage-file-layout">.
    </para>
  
    <para>
--- 20,31 ----
     stored. If the table has any columns with potentially-wide values,
     there also might be a <acronym>TOAST</> file associated with the table,
     which is used to store values too wide to fit comfortably in the main
!    table (see <xref linkend="storage-toast">).  There will be one valid index
!    on the <acronym>TOAST</> table, if present. There also might be indexes
!    associated with the base table.  Each table and index is stored in a
!    separate disk file &mdash; possibly more than one file, if the file would
!    exceed one gigabyte.  Naming conventions for these files are described
!    in <xref linkend="storage-file-layout">.
    </para>
  
    <para>
***************
*** 44,50 ****
  <programlisting>
  SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer';
  
!  pg_relation_filepath | relpages 
  ----------------------+----------
   base/16384/16806     |       60
  (1 row)
--- 44,50 ----
  <programlisting>
  SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer';
  
!  pg_relation_filepath | relpages
  ----------------------+----------
   base/16384/16806     |       60
  (1 row)
***************
*** 65,76 **** FROM pg_class,
        FROM pg_class
        WHERE relname = 'customer') AS ss
  WHERE oid = ss.reltoastrelid OR
!       oid = (SELECT reltoastidxid
!              FROM pg_class
!              WHERE oid = ss.reltoastrelid)
  ORDER BY relname;
  
!        relname        | relpages 
  ----------------------+----------
   pg_toast_16806       |        0
   pg_toast_16806_index |        1
--- 65,76 ----
        FROM pg_class
        WHERE relname = 'customer') AS ss
  WHERE oid = ss.reltoastrelid OR
!       oid = (SELECT indexrelid
!              FROM pg_index
!              WHERE indrelid = ss.reltoastrelid)
  ORDER BY relname;
  
!        relname        | relpages
  ----------------------+----------
   pg_toast_16806       |        0
   pg_toast_16806_index |        1
***************
*** 87,93 **** WHERE c.relname = 'customer' AND
        c2.oid = i.indexrelid
  ORDER BY c2.relname;
  
!        relname        | relpages 
  ----------------------+----------
   customer_id_indexdex |       26
  </programlisting>
--- 87,93 ----
        c2.oid = i.indexrelid
  ORDER BY c2.relname;
  
!        relname        | relpages
  ----------------------+----------
   customer_id_indexdex |       26
  </programlisting>
***************
*** 101,107 **** SELECT relname, relpages
  FROM pg_class
  ORDER BY relpages DESC;
  
!        relname        | relpages 
  ----------------------+----------
   bigtable             |     3290
   customer             |     3144
--- 101,107 ----
  FROM pg_class
  ORDER BY relpages DESC;
  
!        relname        | relpages
  ----------------------+----------
   bigtable             |     3290
   customer             |     3144
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 1163,1174 **** postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
      <row>
       <entry><structfield>tidx_blks_read</></entry>
       <entry><type>bigint</></entry>
!      <entry>Number of disk blocks read from this table's TOAST table index (if any)</entry>
      </row>
      <row>
       <entry><structfield>tidx_blks_hit</></entry>
       <entry><type>bigint</></entry>
!      <entry>Number of buffer hits in this table's TOAST table index (if any)</entry>
      </row>
     </tbody>
     </tgroup>
--- 1163,1174 ----
      <row>
       <entry><structfield>tidx_blks_read</></entry>
       <entry><type>bigint</></entry>
!      <entry>Number of disk blocks read from this table's TOAST table indexes (if any)</entry>
      </row>
      <row>
       <entry><structfield>tidx_blks_hit</></entry>
       <entry><type>bigint</></entry>
!      <entry>Number of buffer hits in this table's TOAST table indexes (if any)</entry>
      </row>
     </tbody>
     </tgroup>
*** a/src/backend/access/heap/tuptoaster.c
--- b/src/backend/access/heap/tuptoaster.c
***************
*** 76,86 **** do { \
  static void toast_delete_datum(Relation rel, Datum value);
  static Datum toast_save_datum(Relation rel, Datum value,
  				 struct varlena * oldexternal, int options);
! static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
  static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
  static struct varlena *toast_fetch_datum(struct varlena * attr);
  static struct varlena *toast_fetch_datum_slice(struct varlena * attr,
  						int32 sliceoffset, int32 length);
  
  
  /* ----------
--- 76,93 ----
  static void toast_delete_datum(Relation rel, Datum value);
  static Datum toast_save_datum(Relation rel, Datum value,
  				 struct varlena * oldexternal, int options);
! static bool toastrel_valueid_exists(Relation toastrel,
! 						Oid valueid, LOCKMODE lockmode);
  static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
  static struct varlena *toast_fetch_datum(struct varlena * attr);
  static struct varlena *toast_fetch_datum_slice(struct varlena * attr,
  						int32 sliceoffset, int32 length);
+ static int toast_open_indexes(Relation toastrel,
+ 							  LOCKMODE lock,
+ 							  Relation **toastidxs,
+ 							  int *num_indexes);
+ static void toast_close_indexes(Relation *toastidxs, int num_indexes,
+ 								LOCKMODE lock);
  
  
  /* ----------
***************
*** 1222,1227 **** toast_compress_datum(Datum value)
--- 1229,1269 ----
  
  
  /* ----------
+  * toast_get_valid_index
+  *
+  *	Get the valid index of given toast relation. A toast relation can only
+  *	have one valid index at the same time. The lock taken on the index
+  *	relations is released at the end of this function call.
+  */
+ Oid
+ toast_get_valid_index(Oid toastoid, LOCKMODE lock)
+ {
+ 	int			num_indexes;
+ 	int			validIndex;
+ 	Oid			validIndexOid;
+ 	Relation   *toastidxs;
+ 	Relation	toastrel;
+ 
+ 	/* Get the index list of relation */
+ 	toastrel = heap_open(toastoid, lock);
+ 
+ 	/* Look for the valid index of relation */
+ 	validIndex = toast_open_indexes(toastrel,
+ 									lock,
+ 									&toastidxs,
+ 									&num_indexes);
+ 	validIndexOid = RelationGetRelid(toastidxs[validIndex]);
+ 
+ 	/* Close all the index relations */
+ 	toast_close_indexes(toastidxs, num_indexes, lock);
+ 
+ 	/* Close toast relation */
+ 	heap_close(toastrel, lock);
+ 	return validIndexOid;
+ }
+ 
+ 
+ /* ----------
   * toast_save_datum -
   *
   *	Save one single datum into the secondary relation and return
***************
*** 1238,1244 **** toast_save_datum(Relation rel, Datum value,
  				 struct varlena * oldexternal, int options)
  {
  	Relation	toastrel;
! 	Relation	toastidx;
  	HeapTuple	toasttup;
  	TupleDesc	toasttupDesc;
  	Datum		t_values[3];
--- 1280,1286 ----
  				 struct varlena * oldexternal, int options)
  {
  	Relation	toastrel;
! 	Relation   *toastidxs;
  	HeapTuple	toasttup;
  	TupleDesc	toasttupDesc;
  	Datum		t_values[3];
***************
*** 1257,1271 **** toast_save_datum(Relation rel, Datum value,
  	char	   *data_p;
  	int32		data_todo;
  	Pointer		dval = DatumGetPointer(value);
  
  	/*
  	 * Open the toast relation and its index.  We can use the index to check
  	 * uniqueness of the OID we assign to the toasted item, even though it has
! 	 * additional columns besides OID.
  	 */
  	toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
  	toasttupDesc = toastrel->rd_att;
! 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
  
  	/*
  	 * Get the data pointer and length, and compute va_rawsize and va_extsize.
--- 1299,1321 ----
  	char	   *data_p;
  	int32		data_todo;
  	Pointer		dval = DatumGetPointer(value);
+ 	int			num_indexes;
+ 	int			validIndex;
  
  	/*
  	 * Open the toast relation and its index.  We can use the index to check
  	 * uniqueness of the OID we assign to the toasted item, even though it has
! 	 * additional columns besides OID. A toast table can have multiple identical
! 	 * indexes associated to it.
  	 */
  	toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
  	toasttupDesc = toastrel->rd_att;
! 
! 	/* Fetch valid index used for process */
! 	validIndex = toast_open_indexes(toastrel,
! 									RowExclusiveLock,
! 									&toastidxs,
! 									&num_indexes);
  
  	/*
  	 * Get the data pointer and length, and compute va_rawsize and va_extsize.
***************
*** 1330,1336 **** toast_save_datum(Relation rel, Datum value,
  		/* normal case: just choose an unused OID */
  		toast_pointer.va_valueid =
  			GetNewOidWithIndex(toastrel,
! 							   RelationGetRelid(toastidx),
  							   (AttrNumber) 1);
  	}
  	else
--- 1380,1386 ----
  		/* normal case: just choose an unused OID */
  		toast_pointer.va_valueid =
  			GetNewOidWithIndex(toastrel,
! 							   RelationGetRelid(toastidxs[validIndex]),
  							   (AttrNumber) 1);
  	}
  	else
***************
*** 1367,1373 **** toast_save_datum(Relation rel, Datum value,
  				 * be reclaimed by VACUUM.
  				 */
  				if (toastrel_valueid_exists(toastrel,
! 											toast_pointer.va_valueid))
  				{
  					/* Match, so short-circuit the data storage loop below */
  					data_todo = 0;
--- 1417,1424 ----
  				 * be reclaimed by VACUUM.
  				 */
  				if (toastrel_valueid_exists(toastrel,
! 											toast_pointer.va_valueid,
! 											RowExclusiveLock))
  				{
  					/* Match, so short-circuit the data storage loop below */
  					data_todo = 0;
***************
*** 1384,1391 **** toast_save_datum(Relation rel, Datum value,
  			{
  				toast_pointer.va_valueid =
  					GetNewOidWithIndex(toastrel,
! 									   RelationGetRelid(toastidx),
! 									   (AttrNumber) 1);
  			} while (toastid_valueid_exists(rel->rd_toastoid,
  											toast_pointer.va_valueid));
  		}
--- 1435,1442 ----
  			{
  				toast_pointer.va_valueid =
  					GetNewOidWithIndex(toastrel,
! 							   RelationGetRelid(toastidxs[validIndex]),
! 							   (AttrNumber) 1);
  			} while (toastid_valueid_exists(rel->rd_toastoid,
  											toast_pointer.va_valueid));
  		}
***************
*** 1405,1410 **** toast_save_datum(Relation rel, Datum value,
--- 1456,1463 ----
  	 */
  	while (data_todo > 0)
  	{
+ 		int i;
+ 
  		/*
  		 * Calculate the size of this chunk
  		 */
***************
*** 1423,1438 **** toast_save_datum(Relation rel, Datum value,
  		/*
  		 * Create the index entry.	We cheat a little here by not using
  		 * FormIndexDatum: this relies on the knowledge that the index columns
! 		 * are the same as the initial columns of the table.
  		 *
  		 * Note also that there had better not be any user-created index on
  		 * the TOAST table, since we don't bother to update anything else.
  		 */
! 		index_insert(toastidx, t_values, t_isnull,
! 					 &(toasttup->t_self),
! 					 toastrel,
! 					 toastidx->rd_index->indisunique ?
! 					 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
  
  		/*
  		 * Free memory
--- 1476,1497 ----
  		/*
  		 * Create the index entry.	We cheat a little here by not using
  		 * FormIndexDatum: this relies on the knowledge that the index columns
! 		 * are the same as the initial columns of the table for all the
! 		 * indexes.
  		 *
  		 * Note also that there had better not be any user-created index on
  		 * the TOAST table, since we don't bother to update anything else.
  		 */
! 		for (i = 0; i < num_indexes; i++)
! 		{
! 			/* Only index relations marked as ready can updated */
! 			if (toastidxs[i]->rd_index->indisready)
! 				index_insert(toastidxs[i], t_values, t_isnull,
! 							 &(toasttup->t_self),
! 							 toastrel,
! 							 toastidxs[i]->rd_index->indisunique ?
! 							 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
! 		}
  
  		/*
  		 * Free memory
***************
*** 1447,1455 **** toast_save_datum(Relation rel, Datum value,
  	}
  
  	/*
! 	 * Done - close toast relation
  	 */
! 	index_close(toastidx, RowExclusiveLock);
  	heap_close(toastrel, RowExclusiveLock);
  
  	/*
--- 1506,1514 ----
  	}
  
  	/*
! 	 * Done - close toast relations
  	 */
! 	toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
  	heap_close(toastrel, RowExclusiveLock);
  
  	/*
***************
*** 1475,1484 **** toast_delete_datum(Relation rel, Datum value)
  	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
  	struct varatt_external toast_pointer;
  	Relation	toastrel;
! 	Relation	toastidx;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
  	HeapTuple	toasttup;
  
  	if (!VARATT_IS_EXTERNAL(attr))
  		return;
--- 1534,1545 ----
  	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
  	struct varatt_external toast_pointer;
  	Relation	toastrel;
! 	Relation   *toastidxs;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
  	HeapTuple	toasttup;
+ 	int			num_indexes;
+ 	int			validIndex;
  
  	if (!VARATT_IS_EXTERNAL(attr))
  		return;
***************
*** 1487,1496 **** toast_delete_datum(Relation rel, Datum value)
  	VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
  
  	/*
! 	 * Open the toast relation and its index
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
! 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
  
  	/*
  	 * Setup a scan key to find chunks with matching va_valueid
--- 1548,1562 ----
  	VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
  
  	/*
! 	 * Open the toast relation and its indexes
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
! 
! 	/* Fetch valid relation used for process */
! 	validIndex = toast_open_indexes(toastrel,
! 									RowExclusiveLock,
! 									&toastidxs,
! 									&num_indexes);
  
  	/*
  	 * Setup a scan key to find chunks with matching va_valueid
***************
*** 1505,1511 **** toast_delete_datum(Relation rel, Datum value)
  	 * sequence or not, but since we've already locked the index we might as
  	 * well use systable_beginscan_ordered.)
  	 */
! 	toastscan = systable_beginscan_ordered(toastrel, toastidx,
  										   SnapshotToast, 1, &toastkey);
  	while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
--- 1571,1577 ----
  	 * sequence or not, but since we've already locked the index we might as
  	 * well use systable_beginscan_ordered.)
  	 */
! 	toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
  										   SnapshotToast, 1, &toastkey);
  	while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
***************
*** 1519,1525 **** toast_delete_datum(Relation rel, Datum value)
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	index_close(toastidx, RowExclusiveLock);
  	heap_close(toastrel, RowExclusiveLock);
  }
  
--- 1585,1591 ----
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
  	heap_close(toastrel, RowExclusiveLock);
  }
  
***************
*** 1531,1541 **** toast_delete_datum(Relation rel, Datum value)
   * ----------
   */
  static bool
! toastrel_valueid_exists(Relation toastrel, Oid valueid)
  {
  	bool		result = false;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
  
  	/*
  	 * Setup a scan key to find chunks with matching va_valueid
--- 1597,1616 ----
   * ----------
   */
  static bool
! toastrel_valueid_exists(Relation toastrel, Oid valueid, LOCKMODE lockmode)
  {
  	bool		result = false;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
+ 	int			num_indexes;
+ 	int			validIndex;
+ 	Relation   *toastidxs;
+ 
+ 	/* Fetch a valid index relation */
+ 	validIndex = toast_open_indexes(toastrel,
+ 									lockmode,
+ 									&toastidxs,
+ 									&num_indexes);
  
  	/*
  	 * Setup a scan key to find chunks with matching va_valueid
***************
*** 1548,1561 **** toastrel_valueid_exists(Relation toastrel, Oid valueid)
  	/*
  	 * Is there any such chunk?
  	 */
! 	toastscan = systable_beginscan(toastrel, toastrel->rd_rel->reltoastidxid,
! 								   true, SnapshotToast, 1, &toastkey);
  
  	if (systable_getnext(toastscan) != NULL)
  		result = true;
  
  	systable_endscan(toastscan);
  
  	return result;
  }
  
--- 1623,1640 ----
  	/*
  	 * Is there any such chunk?
  	 */
! 	toastscan = systable_beginscan(toastrel,
! 						   RelationGetRelid(toastidxs[validIndex]),
! 						   true, SnapshotToast, 1, &toastkey);
  
  	if (systable_getnext(toastscan) != NULL)
  		result = true;
  
  	systable_endscan(toastscan);
  
+ 	/* Clean up */
+ 	toast_close_indexes(toastidxs, num_indexes, lockmode);
+ 
  	return result;
  }
  
***************
*** 1573,1579 **** toastid_valueid_exists(Oid toastrelid, Oid valueid)
  
  	toastrel = heap_open(toastrelid, AccessShareLock);
  
! 	result = toastrel_valueid_exists(toastrel, valueid);
  
  	heap_close(toastrel, AccessShareLock);
  
--- 1652,1658 ----
  
  	toastrel = heap_open(toastrelid, AccessShareLock);
  
! 	result = toastrel_valueid_exists(toastrel, valueid, RowExclusiveLock);
  
  	heap_close(toastrel, AccessShareLock);
  
***************
*** 1592,1598 **** static struct varlena *
  toast_fetch_datum(struct varlena * attr)
  {
  	Relation	toastrel;
! 	Relation	toastidx;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
  	HeapTuple	ttup;
--- 1671,1677 ----
  toast_fetch_datum(struct varlena * attr)
  {
  	Relation	toastrel;
! 	Relation   *toastidxs;
  	ScanKeyData toastkey;
  	SysScanDesc toastscan;
  	HeapTuple	ttup;
***************
*** 1607,1612 **** toast_fetch_datum(struct varlena * attr)
--- 1686,1693 ----
  	bool		isnull;
  	char	   *chunkdata;
  	int32		chunksize;
+ 	int			num_indexes;
+ 	int			validIndex;
  
  	/* Must copy to access aligned fields */
  	VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
***************
*** 1622,1632 **** toast_fetch_datum(struct varlena * attr)
  		SET_VARSIZE(result, ressize + VARHDRSZ);
  
  	/*
! 	 * Open the toast relation and its index
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
  	toasttupDesc = toastrel->rd_att;
! 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
  
  	/*
  	 * Setup a scan key to fetch from the index by va_valueid
--- 1703,1718 ----
  		SET_VARSIZE(result, ressize + VARHDRSZ);
  
  	/*
! 	 * Open the toast relation and its indexes
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
  	toasttupDesc = toastrel->rd_att;
! 
! 	/* Fetch relation used for process */
! 	validIndex = toast_open_indexes(toastrel,
! 									AccessShareLock,
! 									&toastidxs,
! 									&num_indexes);
  
  	/*
  	 * Setup a scan key to fetch from the index by va_valueid
***************
*** 1645,1651 **** toast_fetch_datum(struct varlena * attr)
  	 */
  	nextidx = 0;
  
! 	toastscan = systable_beginscan_ordered(toastrel, toastidx,
  										   SnapshotToast, 1, &toastkey);
  	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
--- 1731,1737 ----
  	 */
  	nextidx = 0;
  
! 	toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
  										   SnapshotToast, 1, &toastkey);
  	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
***************
*** 1734,1740 **** toast_fetch_datum(struct varlena * attr)
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	index_close(toastidx, AccessShareLock);
  	heap_close(toastrel, AccessShareLock);
  
  	return result;
--- 1820,1826 ----
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
  	heap_close(toastrel, AccessShareLock);
  
  	return result;
***************
*** 1751,1757 **** static struct varlena *
  toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
  {
  	Relation	toastrel;
! 	Relation	toastidx;
  	ScanKeyData toastkey[3];
  	int			nscankeys;
  	SysScanDesc toastscan;
--- 1837,1843 ----
  toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
  {
  	Relation	toastrel;
! 	Relation   *toastidxs;
  	ScanKeyData toastkey[3];
  	int			nscankeys;
  	SysScanDesc toastscan;
***************
*** 1774,1779 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
--- 1860,1867 ----
  	int32		chunksize;
  	int32		chcpystrt;
  	int32		chcpyend;
+ 	int			num_indexes;
+ 	int			validIndex;
  
  	Assert(VARATT_IS_EXTERNAL(attr));
  
***************
*** 1816,1826 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
  	endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
  
  	/*
! 	 * Open the toast relation and its index
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
  	toasttupDesc = toastrel->rd_att;
! 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
  
  	/*
  	 * Setup a scan key to fetch from the index. This is either two keys or
--- 1904,1919 ----
  	endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
  
  	/*
! 	 * Open the toast relation and its indexes
  	 */
  	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
  	toasttupDesc = toastrel->rd_att;
! 
! 	/* Look for the valid index of toast relation */
! 	validIndex = toast_open_indexes(toastrel,
! 									AccessShareLock,
! 									&toastidxs,
! 									&num_indexes);
  
  	/*
  	 * Setup a scan key to fetch from the index. This is either two keys or
***************
*** 1861,1867 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
  	 * The index is on (valueid, chunkidx) so they will come in order
  	 */
  	nextidx = startchunk;
! 	toastscan = systable_beginscan_ordered(toastrel, toastidx,
  										 SnapshotToast, nscankeys, toastkey);
  	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
--- 1954,1960 ----
  	 * The index is on (valueid, chunkidx) so they will come in order
  	 */
  	nextidx = startchunk;
! 	toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
  										 SnapshotToast, nscankeys, toastkey);
  	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
  	{
***************
*** 1958,1965 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	index_close(toastidx, AccessShareLock);
  	heap_close(toastrel, AccessShareLock);
  
  	return result;
  }
--- 2051,2136 ----
  	 * End scan and close relations
  	 */
  	systable_endscan_ordered(toastscan);
! 	toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
  	heap_close(toastrel, AccessShareLock);
  
  	return result;
  }
+ 
+ /* ----------
+  * toast_open_indexes
+  *
+  *	Get an array of index relations associated to the given toast relation
+  *	and return as well the position of the valid index used by the toast
+  *	relation in this array. It is the responsibility of the caller of this
+  *	function to close the index relations as well as free them.
+  */
+ static int
+ toast_open_indexes(Relation toastrel,
+ 				   LOCKMODE lock,
+ 				   Relation **toastidxs,
+ 				   int *num_indexes)
+ {
+ 	int			i = 0;
+ 	int			res = 0;
+ 	bool		found = false;
+ 	List	   *indexlist;
+ 	ListCell   *lc;
+ 
+ 	/* Get index list of relation */
+ 	indexlist = RelationGetIndexList(toastrel);
+ 	Assert(indexlist != NIL);
+ 
+ 	*num_indexes = list_length(indexlist);
+ 
+ 	/* Open all the index relations */
+ 	*toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation));
+ 	foreach(lc, indexlist)
+ 		*toastidxs[i++] = index_open(lfirst_oid(lc), lock);
+ 
+ 	/* Fetch the first valid index in list */
+ 	for (i = 0; i < *num_indexes; i++)
+ 	{
+ 		Relation toastidx = *toastidxs[i];
+ 		if (toastidx->rd_index->indisvalid)
+ 		{
+ 			res = i;
+ 			found = true;
+ 			break;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Free index list, not necessary as relations are opened and a valid index
+ 	 * has been found.
+ 	 */
+ 	list_free(indexlist);
+ 
+ 	/*
+ 	 * The toast relation should have one valid index, so something is
+ 	 * going wrong if there is nothing.
+ 	 */
+ 	if (!found)
+ 		elog(ERROR, "no valid index found for toast relation with Oid %d",
+ 			 RelationGetRelid(toastrel));
+ 
+ 	return res;
+ }
+ 
+ /* ----------
+  * toast_close_indexes
+  *
+  *	Close an array of indexes for a toast relation and free it. This should
+  *	be called for a set of index relations opened previously with
+  *	toast_open_indexes.
+  */
+ static void
+ toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
+ {
+ 	int i;
+ 
+ 	/* Close relations and clean up things */
+ 	for (i = 0; i < num_indexes; i++)
+ 		index_close(toastidxs[i], lock);
+ 	pfree(toastidxs);
+ }
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 781,787 **** InsertPgClassTuple(Relation pg_class_desc,
  	values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples);
  	values[Anum_pg_class_relallvisible - 1] = Int32GetDatum(rd_rel->relallvisible);
  	values[Anum_pg_class_reltoastrelid - 1] = ObjectIdGetDatum(rd_rel->reltoastrelid);
- 	values[Anum_pg_class_reltoastidxid - 1] = ObjectIdGetDatum(rd_rel->reltoastidxid);
  	values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex);
  	values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared);
  	values[Anum_pg_class_relpersistence - 1] = CharGetDatum(rd_rel->relpersistence);
--- 781,786 ----
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 103,109 **** static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
  					bool isvalid);
  static void index_update_stats(Relation rel,
  				   bool hasindex, bool isprimary,
! 				   Oid reltoastidxid, double reltuples);
  static void IndexCheckExclusion(Relation heapRelation,
  					Relation indexRelation,
  					IndexInfo *indexInfo);
--- 103,109 ----
  					bool isvalid);
  static void index_update_stats(Relation rel,
  				   bool hasindex, bool isprimary,
! 				   double reltuples);
  static void IndexCheckExclusion(Relation heapRelation,
  					Relation indexRelation,
  					IndexInfo *indexInfo);
***************
*** 1072,1078 **** index_create(Relation heapRelation,
  		index_update_stats(heapRelation,
  						   true,
  						   isprimary,
- 						   InvalidOid,
  						   -1.0);
  		/* Make the above update visible */
  		CommandCounterIncrement();
--- 1072,1077 ----
***************
*** 1254,1260 **** index_constraint_create(Relation heapRelation,
  		index_update_stats(heapRelation,
  						   true,
  						   true,
- 						   InvalidOid,
  						   -1.0);
  
  	/*
--- 1253,1258 ----
***************
*** 1764,1771 **** FormIndexDatum(IndexInfo *indexInfo,
   *
   * hasindex: set relhasindex to this value
   * isprimary: if true, set relhaspkey true; else no change
-  * reltoastidxid: if not InvalidOid, set reltoastidxid to this value;
-  *		else no change
   * reltuples: if >= 0, set reltuples to this value; else no change
   *
   * If reltuples >= 0, relpages and relallvisible are also updated (using
--- 1762,1767 ----
***************
*** 1781,1788 **** FormIndexDatum(IndexInfo *indexInfo,
   */
  static void
  index_update_stats(Relation rel,
! 				   bool hasindex, bool isprimary,
! 				   Oid reltoastidxid, double reltuples)
  {
  	Oid			relid = RelationGetRelid(rel);
  	Relation	pg_class;
--- 1777,1785 ----
   */
  static void
  index_update_stats(Relation rel,
! 				   bool hasindex,
! 				   bool isprimary,
! 				   double reltuples)
  {
  	Oid			relid = RelationGetRelid(rel);
  	Relation	pg_class;
***************
*** 1876,1890 **** index_update_stats(Relation rel,
  			dirty = true;
  		}
  	}
- 	if (OidIsValid(reltoastidxid))
- 	{
- 		Assert(rd_rel->relkind == RELKIND_TOASTVALUE);
- 		if (rd_rel->reltoastidxid != reltoastidxid)
- 		{
- 			rd_rel->reltoastidxid = reltoastidxid;
- 			dirty = true;
- 		}
- 	}
  
  	if (reltuples >= 0)
  	{
--- 1873,1878 ----
***************
*** 2072,2085 **** index_build(Relation heapRelation,
  	index_update_stats(heapRelation,
  					   true,
  					   isprimary,
- 					   (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ?
- 					   RelationGetRelid(indexRelation) : InvalidOid,
  					   stats->heap_tuples);
  
  	index_update_stats(indexRelation,
  					   false,
  					   false,
- 					   InvalidOid,
  					   stats->index_tuples);
  
  	/* Make the updated catalog row versions visible */
--- 2060,2070 ----
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 473,488 **** CREATE VIEW pg_statio_all_tables AS
              pg_stat_get_blocks_fetched(T.oid) -
                      pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
              pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
!             pg_stat_get_blocks_fetched(X.oid) -
!                     pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read,
!             pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit
      FROM pg_class C LEFT JOIN
              pg_index I ON C.oid = I.indrelid LEFT JOIN
              pg_class T ON C.reltoastrelid = T.oid LEFT JOIN
!             pg_class X ON T.reltoastidxid = X.oid
              LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
      WHERE C.relkind IN ('r', 't', 'm')
!     GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid;
  
  CREATE VIEW pg_statio_sys_tables AS
      SELECT * FROM pg_statio_all_tables
--- 473,488 ----
              pg_stat_get_blocks_fetched(T.oid) -
                      pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
              pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
!             sum(pg_stat_get_blocks_fetched(X.indexrelid) -
!                     pg_stat_get_blocks_hit(X.indexrelid))::bigint AS tidx_blks_read,
!             sum(pg_stat_get_blocks_hit(X.indexrelid))::bigint AS tidx_blks_hit
      FROM pg_class C LEFT JOIN
              pg_index I ON C.oid = I.indrelid LEFT JOIN
              pg_class T ON C.reltoastrelid = T.oid LEFT JOIN
!             pg_index X ON T.oid = X.indrelid
              LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
      WHERE C.relkind IN ('r', 't', 'm')
!     GROUP BY C.oid, N.nspname, C.relname, T.oid, X.indexrelid;
  
  CREATE VIEW pg_statio_sys_tables AS
      SELECT * FROM pg_statio_all_tables
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "access/relscan.h"
  #include "access/rewriteheap.h"
  #include "access/transam.h"
+ #include "access/tuptoaster.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
  #include "catalog/dependency.h"
***************
*** 1172,1179 **** swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
  			swaptemp = relform1->reltoastrelid;
  			relform1->reltoastrelid = relform2->reltoastrelid;
  			relform2->reltoastrelid = swaptemp;
- 
- 			/* we should NOT swap reltoastidxid */
  		}
  	}
  	else
--- 1173,1178 ----
***************
*** 1393,1410 **** swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
  
  	/*
  	 * If we're swapping two toast tables by content, do the same for their
! 	 * indexes.
  	 */
  	if (swap_toast_by_content &&
! 		relform1->reltoastidxid && relform2->reltoastidxid)
! 		swap_relation_files(relform1->reltoastidxid,
! 							relform2->reltoastidxid,
  							target_is_pg_class,
  							swap_toast_by_content,
  							is_internal,
  							InvalidTransactionId,
  							InvalidMultiXactId,
  							mapped_tables);
  
  	/* Clean up. */
  	heap_freetuple(reltup1);
--- 1392,1421 ----
  
  	/*
  	 * If we're swapping two toast tables by content, do the same for their
! 	 * valid index. The swap can actually be safely done only if the relations
! 	 * have indexes.
  	 */
  	if (swap_toast_by_content &&
! 		relform1->relkind == RELKIND_TOASTVALUE &&
! 		relform2->relkind == RELKIND_TOASTVALUE)
! 	{
! 		Oid			toastIndex1, toastIndex2;
! 
! 		/* Get valid index for each relation */
! 		toastIndex1 = toast_get_valid_index(r1,
! 											AccessExclusiveLock);
! 		toastIndex2 = toast_get_valid_index(r2,
! 											AccessExclusiveLock);
! 
! 		swap_relation_files(toastIndex1,
! 							toastIndex2,
  							target_is_pg_class,
  							swap_toast_by_content,
  							is_internal,
  							InvalidTransactionId,
  							InvalidMultiXactId,
  							mapped_tables);
+ 	}
  
  	/* Clean up. */
  	heap_freetuple(reltup1);
***************
*** 1528,1541 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
  		newrel = heap_open(OIDOldHeap, NoLock);
  		if (OidIsValid(newrel->rd_rel->reltoastrelid))
  		{
- 			Relation	toastrel;
  			Oid			toastidx;
  			char		NewToastName[NAMEDATALEN];
  
! 			toastrel = relation_open(newrel->rd_rel->reltoastrelid,
! 									 AccessShareLock);
! 			toastidx = toastrel->rd_rel->reltoastidxid;
! 			relation_close(toastrel, AccessShareLock);
  
  			/* rename the toast table ... */
  			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
--- 1539,1550 ----
  		newrel = heap_open(OIDOldHeap, NoLock);
  		if (OidIsValid(newrel->rd_rel->reltoastrelid))
  		{
  			Oid			toastidx;
  			char		NewToastName[NAMEDATALEN];
  
! 			/* Get the associated valid index to be renamed */
! 			toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
! 											 AccessShareLock);
  
  			/* rename the toast table ... */
  			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
***************
*** 1543,1551 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
  			RenameRelationInternal(newrel->rd_rel->reltoastrelid,
  								   NewToastName, true);
  
! 			/* ... and its index too */
  			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
  					 OIDOldHeap);
  			RenameRelationInternal(toastidx,
  								   NewToastName, true);
  		}
--- 1552,1561 ----
  			RenameRelationInternal(newrel->rd_rel->reltoastrelid,
  								   NewToastName, true);
  
! 			/* ... and its valid index too. */
  			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
  					 OIDOldHeap);
+ 
  			RenameRelationInternal(toastidx,
  								   NewToastName, true);
  		}
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 8869,8875 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
  	Relation	rel;
  	Oid			oldTableSpace;
  	Oid			reltoastrelid;
- 	Oid			reltoastidxid;
  	Oid			newrelfilenode;
  	RelFileNode newrnode;
  	SMgrRelation dstrel;
--- 8869,8874 ----
***************
*** 8877,8882 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
--- 8876,8883 ----
  	HeapTuple	tuple;
  	Form_pg_class rd_rel;
  	ForkNumber	forkNum;
+ 	List	   *reltoastidxids = NIL;
+ 	ListCell   *lc;
  
  	/*
  	 * Need lock here in case we are recursing to toast table or index
***************
*** 8923,8929 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
  				 errmsg("cannot move temporary tables of other sessions")));
  
  	reltoastrelid = rel->rd_rel->reltoastrelid;
! 	reltoastidxid = rel->rd_rel->reltoastidxid;
  
  	/* Get a modifiable copy of the relation's pg_class row */
  	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
--- 8924,8936 ----
  				 errmsg("cannot move temporary tables of other sessions")));
  
  	reltoastrelid = rel->rd_rel->reltoastrelid;
! 	/* Fetch the list of indexes on toast relation if necessary */
! 	if (OidIsValid(reltoastrelid))
! 	{
! 		Relation toastRel = relation_open(reltoastrelid, lockmode);
! 		reltoastidxids = RelationGetIndexList(toastRel);
! 		relation_close(toastRel, lockmode);
! 	}
  
  	/* Get a modifiable copy of the relation's pg_class row */
  	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
***************
*** 9001,9011 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
  	/* Make sure the reltablespace change is visible */
  	CommandCounterIncrement();
  
! 	/* Move associated toast relation and/or index, too */
  	if (OidIsValid(reltoastrelid))
  		ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
! 	if (OidIsValid(reltoastidxid))
! 		ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
  }
  
  /*
--- 9008,9021 ----
  	/* Make sure the reltablespace change is visible */
  	CommandCounterIncrement();
  
! 	/* Move associated toast relation and/or indexes, too */
  	if (OidIsValid(reltoastrelid))
  		ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
! 	foreach(lc, reltoastidxids)
! 		ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
! 
! 	/* Clean up */
! 	list_free(reltoastidxids);
  }
  
  /*
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 575,582 **** DefineQueryRewrite(char *rulename,
  
  		/*
  		 * Fix pg_class entry to look like a normal view's, including setting
! 		 * the correct relkind and removal of reltoastrelid/reltoastidxid of
! 		 * the toast table we potentially removed above.
  		 */
  		classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid));
  		if (!HeapTupleIsValid(classTup))
--- 575,582 ----
  
  		/*
  		 * Fix pg_class entry to look like a normal view's, including setting
! 		 * the correct relkind and removal of reltoastrelid of the toast table
! 		 * we potentially removed above.
  		 */
  		classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid));
  		if (!HeapTupleIsValid(classTup))
***************
*** 588,594 **** DefineQueryRewrite(char *rulename,
  		classForm->reltuples = 0;
  		classForm->relallvisible = 0;
  		classForm->reltoastrelid = InvalidOid;
- 		classForm->reltoastidxid = InvalidOid;
  		classForm->relhasindex = false;
  		classForm->relkind = RELKIND_VIEW;
  		classForm->relhasoids = false;
--- 588,593 ----
*** a/src/backend/utils/adt/dbsize.c
--- b/src/backend/utils/adt/dbsize.c
***************
*** 332,338 **** pg_relation_size(PG_FUNCTION_ARGS)
  }
  
  /*
!  * Calculate total on-disk size of a TOAST relation, including its index.
   * Must not be applied to non-TOAST relations.
   */
  static int64
--- 332,338 ----
  }
  
  /*
!  * Calculate total on-disk size of a TOAST relation, including its indexes.
   * Must not be applied to non-TOAST relations.
   */
  static int64
***************
*** 340,347 **** calculate_toast_table_size(Oid toastrelid)
  {
  	int64		size = 0;
  	Relation	toastRel;
- 	Relation	toastIdxRel;
  	ForkNumber	forkNum;
  
  	toastRel = relation_open(toastrelid, AccessShareLock);
  
--- 340,348 ----
  {
  	int64		size = 0;
  	Relation	toastRel;
  	ForkNumber	forkNum;
+ 	ListCell   *lc;
+ 	List	   *indexlist;
  
  	toastRel = relation_open(toastrelid, AccessShareLock);
  
***************
*** 351,362 **** calculate_toast_table_size(Oid toastrelid)
  										toastRel->rd_backend, forkNum);
  
  	/* toast index size, including FSM and VM size */
! 	toastIdxRel = relation_open(toastRel->rd_rel->reltoastidxid, AccessShareLock);
! 	for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
! 		size += calculate_relation_size(&(toastIdxRel->rd_node),
! 										toastIdxRel->rd_backend, forkNum);
  
! 	relation_close(toastIdxRel, AccessShareLock);
  	relation_close(toastRel, AccessShareLock);
  
  	return size;
--- 352,372 ----
  										toastRel->rd_backend, forkNum);
  
  	/* toast index size, including FSM and VM size */
! 	indexlist = RelationGetIndexList(toastRel);
  
! 	/* Size is calculated using all the indexes available */
! 	foreach(lc, indexlist)
! 	{
! 		Relation	toastIdxRel;
! 		toastIdxRel = relation_open(lfirst_oid(lc),
! 									AccessShareLock);
! 		for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
! 			size += calculate_relation_size(&(toastIdxRel->rd_node),
! 											toastIdxRel->rd_backend, forkNum);
! 
! 		relation_close(toastIdxRel, AccessShareLock);
! 	}
! 	list_free(indexlist);
  	relation_close(toastRel, AccessShareLock);
  
  	return size;
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 2778,2787 **** binary_upgrade_set_pg_class_oids(Archive *fout,
  	PQExpBuffer upgrade_query = createPQExpBuffer();
  	PGresult   *upgrade_res;
  	Oid			pg_class_reltoastrelid;
- 	Oid			pg_class_reltoastidxid;
  
  	appendPQExpBuffer(upgrade_query,
! 					  "SELECT c.reltoastrelid, t.reltoastidxid "
  					  "FROM pg_catalog.pg_class c LEFT JOIN "
  					  "pg_catalog.pg_class t ON (c.reltoastrelid = t.oid) "
  					  "WHERE c.oid = '%u'::pg_catalog.oid;",
--- 2778,2786 ----
  	PQExpBuffer upgrade_query = createPQExpBuffer();
  	PGresult   *upgrade_res;
  	Oid			pg_class_reltoastrelid;
  
  	appendPQExpBuffer(upgrade_query,
! 					  "SELECT c.reltoastrelid "
  					  "FROM pg_catalog.pg_class c LEFT JOIN "
  					  "pg_catalog.pg_class t ON (c.reltoastrelid = t.oid) "
  					  "WHERE c.oid = '%u'::pg_catalog.oid;",
***************
*** 2790,2796 **** binary_upgrade_set_pg_class_oids(Archive *fout,
  	upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
  
  	pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid")));
- 	pg_class_reltoastidxid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastidxid")));
  
  	appendPQExpBuffer(upgrade_buffer,
  				   "\n-- For binary upgrade, must preserve pg_class oids\n");
--- 2789,2794 ----
***************
*** 2803,2808 **** binary_upgrade_set_pg_class_oids(Archive *fout,
--- 2801,2810 ----
  		/* only tables have toast tables, not indexes */
  		if (OidIsValid(pg_class_reltoastrelid))
  		{
+ 			PQExpBuffer index_query = createPQExpBuffer();
+ 			PGresult   *index_res;
+ 			Oid			indexrelid;
+ 
  			/*
  			 * One complexity is that the table definition might not require
  			 * the creation of a TOAST table, and the TOAST table might have
***************
*** 2816,2825 **** binary_upgrade_set_pg_class_oids(Archive *fout,
  							  "SELECT binary_upgrade.set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
  							  pg_class_reltoastrelid);
  
! 			/* every toast table has an index */
  			appendPQExpBuffer(upgrade_buffer,
  							  "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
! 							  pg_class_reltoastidxid);
  		}
  	}
  	else
--- 2818,2840 ----
  							  "SELECT binary_upgrade.set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
  							  pg_class_reltoastrelid);
  
! 			/* Every toast table has one valid index, so fetch it first... */
! 			appendPQExpBuffer(index_query,
! 							  "SELECT c.indexrelid "
! 							  "FROM pg_catalog.pg_index c "
! 							  "WHERE c.indrelid = %u;",
! 							  pg_class_reltoastrelid);
! 			index_res = ExecuteSqlQueryForSingleRow(fout, index_query->data);
! 			indexrelid = atooid(PQgetvalue(index_res, 0,
! 									   PQfnumber(index_res, "indexrelid")));
! 
! 			/* Then set it */
  			appendPQExpBuffer(upgrade_buffer,
  							  "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
! 							  indexrelid);
! 
! 			PQclear(index_res);
! 			destroyPQExpBuffer(index_query);
  		}
  	}
  	else
***************
*** 13126,13132 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
  		 * attislocal correctly, plus fix up any inherited CHECK constraints.
  		 * Analogously, we set up typed tables using ALTER TABLE / OF here.
  		 */
! 		if (binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || 
  							   tbinfo->relkind == RELKIND_FOREIGN_TABLE) )
  		{
  			for (j = 0; j < tbinfo->numatts; j++)
--- 13141,13147 ----
  		 * attislocal correctly, plus fix up any inherited CHECK constraints.
  		 * Analogously, we set up typed tables using ALTER TABLE / OF here.
  		 */
! 		if (binary_upgrade && (tbinfo->relkind == RELKIND_RELATION ||
  							   tbinfo->relkind == RELKIND_FOREIGN_TABLE) )
  		{
  			for (j = 0; j < tbinfo->numatts; j++)
***************
*** 13151,13157 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
  					else
  						appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ",
  										  fmtId(tbinfo->dobj.name));
! 						
  					appendPQExpBuffer(q, "DROP COLUMN %s;\n",
  									  fmtId(tbinfo->attnames[j]));
  				}
--- 13166,13172 ----
  					else
  						appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ",
  										  fmtId(tbinfo->dobj.name));
! 
  					appendPQExpBuffer(q, "DROP COLUMN %s;\n",
  									  fmtId(tbinfo->attnames[j]));
  				}
*** a/src/include/access/tuptoaster.h
--- b/src/include/access/tuptoaster.h
***************
*** 15,20 ****
--- 15,21 ----
  
  #include "access/htup_details.h"
  #include "utils/relcache.h"
+ #include "storage/lock.h"
  
  /*
   * This enables de-toasting of index entries.  Needed until VACUUM is
***************
*** 188,191 **** extern Size toast_raw_datum_size(Datum value);
--- 189,200 ----
   */
  extern Size toast_datum_size(Datum value);
  
+ /* ----------
+  * toast_get_valid_index -
+  *
+  *	Return valid index associated to a toast relation
+  * ----------
+  */
+ extern Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock);
+ 
  #endif   /* TUPTOASTER_H */
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 48,54 **** CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
  	int32		relallvisible;	/* # of all-visible blocks (not always
  								 * up-to-date) */
  	Oid			reltoastrelid;	/* OID of toast table; 0 if none */
- 	Oid			reltoastidxid;	/* if toast table, OID of chunk_id index */
  	bool		relhasindex;	/* T if has (or has had) any indexes */
  	bool		relisshared;	/* T if shared across databases */
  	char		relpersistence; /* see RELPERSISTENCE_xxx constants below */
--- 48,53 ----
***************
*** 94,100 **** typedef FormData_pg_class *Form_pg_class;
   * ----------------
   */
  
! #define Natts_pg_class					29
  #define Anum_pg_class_relname			1
  #define Anum_pg_class_relnamespace		2
  #define Anum_pg_class_reltype			3
--- 93,99 ----
   * ----------------
   */
  
! #define Natts_pg_class					28
  #define Anum_pg_class_relname			1
  #define Anum_pg_class_relnamespace		2
  #define Anum_pg_class_reltype			3
***************
*** 107,129 **** typedef FormData_pg_class *Form_pg_class;
  #define Anum_pg_class_reltuples			10
  #define Anum_pg_class_relallvisible		11
  #define Anum_pg_class_reltoastrelid		12
! #define Anum_pg_class_reltoastidxid		13
! #define Anum_pg_class_relhasindex		14
! #define Anum_pg_class_relisshared		15
! #define Anum_pg_class_relpersistence	16
! #define Anum_pg_class_relkind			17
! #define Anum_pg_class_relnatts			18
! #define Anum_pg_class_relchecks			19
! #define Anum_pg_class_relhasoids		20
! #define Anum_pg_class_relhaspkey		21
! #define Anum_pg_class_relhasrules		22
! #define Anum_pg_class_relhastriggers	23
! #define Anum_pg_class_relhassubclass	24
! #define Anum_pg_class_relispopulated	25
! #define Anum_pg_class_relfrozenxid		26
! #define Anum_pg_class_relminmxid		27
! #define Anum_pg_class_relacl			28
! #define Anum_pg_class_reloptions		29
  
  /* ----------------
   *		initial contents of pg_class
--- 106,127 ----
  #define Anum_pg_class_reltuples			10
  #define Anum_pg_class_relallvisible		11
  #define Anum_pg_class_reltoastrelid		12
! #define Anum_pg_class_relhasindex		13
! #define Anum_pg_class_relisshared		14
! #define Anum_pg_class_relpersistence	15
! #define Anum_pg_class_relkind			16
! #define Anum_pg_class_relnatts			17
! #define Anum_pg_class_relchecks			18
! #define Anum_pg_class_relhasoids		19
! #define Anum_pg_class_relhaspkey		20
! #define Anum_pg_class_relhasrules		21
! #define Anum_pg_class_relhastriggers	22
! #define Anum_pg_class_relhassubclass	23
! #define Anum_pg_class_relispopulated	24
! #define Anum_pg_class_relfrozenxid		25
! #define Anum_pg_class_relminmxid		26
! #define Anum_pg_class_relacl			27
! #define Anum_pg_class_reloptions		28
  
  /* ----------------
   *		initial contents of pg_class
***************
*** 138,150 **** typedef FormData_pg_class *Form_pg_class;
   * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
   * similarly, "1" in relminmxid stands for FirstMultiXactId
   */
! DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 29 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
  
  
--- 136,148 ----
   * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
   * similarly, "1" in relminmxid stands for FirstMultiXactId
   */
! DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
  
  
*** a/src/test/regress/expected/oidjoins.out
--- b/src/test/regress/expected/oidjoins.out
***************
*** 353,366 **** WHERE	reltoastrelid != 0 AND
  ------+---------------
  (0 rows)
  
- SELECT	ctid, reltoastidxid
- FROM	pg_catalog.pg_class fk
- WHERE	reltoastidxid != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastidxid);
-  ctid | reltoastidxid 
- ------+---------------
- (0 rows)
- 
  SELECT	ctid, collnamespace
  FROM	pg_catalog.pg_collation fk
  WHERE	collnamespace != 0 AND
--- 353,358 ----
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1852,1866 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
                                   |     (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit,                                                                                                                                       +
                                   |     (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read,                                                                                                                    +
                                   |     pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit,                                                                                                                                                           +
!                                  |     (pg_stat_get_blocks_fetched(x.oid) - pg_stat_get_blocks_hit(x.oid)) AS tidx_blks_read,                                                                                                                     +
!                                  |     pg_stat_get_blocks_hit(x.oid) AS tidx_blks_hit                                                                                                                                                             +
                                   |    FROM ((((pg_class c                                                                                                                                                                                         +
                                   |    LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))                                                                                                                                                             +
                                   |    LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid)))                                                                                                                                                        +
!                                  |    LEFT JOIN pg_class x ON ((t.reltoastidxid = x.oid)))                                                                                                                                                        +
                                   |    LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))                                                                                                                                                     +
                                   |   WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"]))                                                                                                                                       +
!                                  |   GROUP BY c.oid, n.nspname, c.relname, t.oid, x.oid;
   pg_statio_sys_indexes           |  SELECT pg_statio_all_indexes.relid,                                                                                                                                                                           +
                                   |     pg_statio_all_indexes.indexrelid,                                                                                                                                                                          +
                                   |     pg_statio_all_indexes.schemaname,                                                                                                                                                                          +
--- 1852,1866 ----
                                   |     (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit,                                                                                                                                       +
                                   |     (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read,                                                                                                                    +
                                   |     pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit,                                                                                                                                                           +
!                                  |     (sum((pg_stat_get_blocks_fetched(x.indexrelid) - pg_stat_get_blocks_hit(x.indexrelid))))::bigint AS tidx_blks_read,                                                                                        +
!                                  |     (sum(pg_stat_get_blocks_hit(x.indexrelid)))::bigint AS tidx_blks_hit                                                                                                                                       +
                                   |    FROM ((((pg_class c                                                                                                                                                                                         +
                                   |    LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))                                                                                                                                                             +
                                   |    LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid)))                                                                                                                                                        +
!                                  |    LEFT JOIN pg_index x ON ((t.oid = x.indrelid)))                                                                                                                                                             +
                                   |    LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))                                                                                                                                                     +
                                   |   WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"]))                                                                                                                                       +
!                                  |   GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid;
   pg_statio_sys_indexes           |  SELECT pg_statio_all_indexes.relid,                                                                                                                                                                           +
                                   |     pg_statio_all_indexes.indexrelid,                                                                                                                                                                          +
                                   |     pg_statio_all_indexes.schemaname,                                                                                                                                                                          +
***************
*** 2347,2357 **** select xmin, * from fooview;  -- fail, views don't have such a column
  ERROR:  column "xmin" does not exist
  LINE 1: select xmin, * from fooview;
                 ^
! select reltoastrelid, reltoastidxid, relkind, relfrozenxid
    from pg_class where oid = 'fooview'::regclass;
!  reltoastrelid | reltoastidxid | relkind | relfrozenxid 
! ---------------+---------------+---------+--------------
!              0 |             0 | v       |            0
  (1 row)
  
  drop view fooview;
--- 2347,2357 ----
  ERROR:  column "xmin" does not exist
  LINE 1: select xmin, * from fooview;
                 ^
! select reltoastrelid, relkind, relfrozenxid
    from pg_class where oid = 'fooview'::regclass;
!  reltoastrelid | relkind | relfrozenxid 
! ---------------+---------+--------------
!              0 | v       |            0
  (1 row)
  
  drop view fooview;
*** a/src/test/regress/sql/oidjoins.sql
--- b/src/test/regress/sql/oidjoins.sql
***************
*** 177,186 **** SELECT	ctid, reltoastrelid
  FROM	pg_catalog.pg_class fk
  WHERE	reltoastrelid != 0 AND
  	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastrelid);
- SELECT	ctid, reltoastidxid
- FROM	pg_catalog.pg_class fk
- WHERE	reltoastidxid != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastidxid);
  SELECT	ctid, collnamespace
  FROM	pg_catalog.pg_collation fk
  WHERE	collnamespace != 0 AND
--- 177,182 ----
*** a/src/test/regress/sql/rules.sql
--- b/src/test/regress/sql/rules.sql
***************
*** 872,878 **** create rule "_RETURN" as on select to fooview do instead
  select * from fooview;
  select xmin, * from fooview;  -- fail, views don't have such a column
  
! select reltoastrelid, reltoastidxid, relkind, relfrozenxid
    from pg_class where oid = 'fooview'::regclass;
  
  drop view fooview;
--- 872,878 ----
  select * from fooview;
  select xmin, * from fooview;  -- fail, views don't have such a column
  
! select reltoastrelid, relkind, relfrozenxid
    from pg_class where oid = 'fooview'::regclass;
  
  drop view fooview;
*** a/src/tools/findoidjoins/README
--- b/src/tools/findoidjoins/README
***************
*** 86,92 **** Join pg_catalog.pg_class.relowner => pg_catalog.pg_authid.oid
  Join pg_catalog.pg_class.relam => pg_catalog.pg_am.oid
  Join pg_catalog.pg_class.reltablespace => pg_catalog.pg_tablespace.oid
  Join pg_catalog.pg_class.reltoastrelid => pg_catalog.pg_class.oid
- Join pg_catalog.pg_class.reltoastidxid => pg_catalog.pg_class.oid
  Join pg_catalog.pg_collation.collnamespace => pg_catalog.pg_namespace.oid
  Join pg_catalog.pg_collation.collowner => pg_catalog.pg_authid.oid
  Join pg_catalog.pg_constraint.connamespace => pg_catalog.pg_namespace.oid
--- 86,91 ----