diff --git a/contrib/pg_upgrade/info.c b/contrib/pg_upgrade/info.c
index a5aa40f..763c703 100644
--- a/contrib/pg_upgrade/info.c
+++ b/contrib/pg_upgrade/info.c
@@ -310,12 +310,17 @@ 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"));
+							  "		ON i.reloid = c.oid "
+							  "		AND c.reltoastrelid != %u", InvalidOid));
 	PQclear(executeQueryOrDie(conn,
 							  "INSERT INTO info_rels "
-							  "SELECT reltoastidxid "
-							  "FROM info_rels i JOIN pg_catalog.pg_class c "
-							  "		ON i.reloid = c.oid"));
+							  "SELECT indexrelid "
+							  "FROM pg_index "
+							  "WHERE indrelid IN (SELECT reltoastrelid "
+							  "          FROM pg_class "
+							  "          WHERE oid >= %u "
+							  "             AND reltoastrelid != %u)",
+							  FirstNormalObjectId, InvalidOid));
 
 	snprintf(query, sizeof(query),
 			 "SELECT c.oid, n.nspname, c.relname, "
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6c0ef5b..8ba390c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1745,15 +1745,6 @@
      </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>
diff --git a/doc/src/sgml/diskusage.sgml b/doc/src/sgml/diskusage.sgml
index de1d0b4..e12d1c1 100644
--- a/doc/src/sgml/diskusage.sgml
+++ b/doc/src/sgml/diskusage.sgml
@@ -44,7 +44,7 @@
 <programlisting>
 SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer';
 
- pg_relation_filepath | relpages 
+ pg_relation_filepath | relpages
 ----------------------+----------
  base/16384/16806     |       60
 (1 row)
@@ -65,12 +65,12 @@ 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)
+      oid = (SELECT indexrelid
+             FROM pg_index
+             WHERE indrelid = ss.reltoastrelid)
 ORDER BY relname;
 
-       relname        | relpages 
+       relname        | relpages
 ----------------------+----------
  pg_toast_16806       |        0
  pg_toast_16806_index |        1
@@ -87,7 +87,7 @@ WHERE c.relname = 'customer' AND
       c2.oid = i.indexrelid
 ORDER BY c2.relname;
 
-       relname        | relpages 
+       relname        | relpages
 ----------------------+----------
  customer_id_indexdex |       26
 </programlisting>
@@ -101,7 +101,7 @@ SELECT relname, relpages
 FROM pg_class
 ORDER BY relpages DESC;
 
-       relname        | relpages 
+       relname        | relpages
 ----------------------+----------
  bigtable             |     3290
  customer             |     3144
diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml
index db820d6..e77b058 100644
--- a/doc/src/sgml/mvcc.sgml
+++ b/doc/src/sgml/mvcc.sgml
@@ -863,8 +863,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
         <para>
          Acquired by <command>VACUUM</command> (without <option>FULL</option>),
-         <command>ANALYZE</>, <command>CREATE INDEX CONCURRENTLY</>, and
-         some forms of <command>ALTER TABLE</command>.
+         <command>ANALYZE</>, <command>CREATE INDEX CONCURRENTLY</>,
+         <command>REINDEX CONCURRENTLY</> and some forms of
+         <command>ALTER TABLE</command>.
         </para>
        </listitem>
       </varlistentry>
diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml
index 7222665..a8b5fc9 100644
--- a/doc/src/sgml/ref/reindex.sgml
+++ b/doc/src/sgml/ref/reindex.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-REINDEX { INDEX | TABLE | DATABASE | SYSTEM } <replaceable class="PARAMETER">name</replaceable> [ FORCE ]
+REINDEX { INDEX | TABLE | DATABASE | SYSTEM } [ CONCURRENTLY ] <replaceable class="PARAMETER">name</replaceable> [ FORCE ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -68,9 +68,21 @@ REINDEX { INDEX | TABLE | DATABASE | SYSTEM } <replaceable class="PARAMETER">nam
       An index build with the <literal>CONCURRENTLY</> option failed, leaving
       an <quote>invalid</> index. Such indexes are useless but it can be
       convenient to use <command>REINDEX</> to rebuild them. Note that
-      <command>REINDEX</> will not perform a concurrent build. To build the
-      index without interfering with production you should drop the index and
-      reissue the <command>CREATE INDEX CONCURRENTLY</> command.
+      <command>REINDEX</> will perform a concurrent build if <literal>
+      CONCURRENTLY</> is specified. To build the index without interfering
+      with production you should drop the index and reissue either the
+      <command>CREATE INDEX CONCURRENTLY</> or <command>REINDEX CONCURRENTLY</>
+      command. Indexes of toast relations can be rebuilt with <command>REINDEX
+      CONCURRENTLY</>.
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      Concurrent indexes based on a <literal>PRIMARY KEY</> or an <literal>
+      EXCLUDE</>  constraint need to be dropped with <literal>ALTER TABLE
+      DROP CONSTRAINT</>. This is also the case of <literal>UNIQUE</> indexes
+      using constraints. Other indexes can be dropped using <literal>DROP INDEX</>.
      </para>
     </listitem>
 
@@ -139,6 +151,21 @@ REINDEX { INDEX | TABLE | DATABASE | SYSTEM } <replaceable class="PARAMETER">nam
    </varlistentry>
 
    <varlistentry>
+    <term><literal>CONCURRENTLY</literal></term>
+    <listitem>
+     <para>
+      When this option is used, <productname>PostgreSQL</> will rebuild the
+      index without taking any locks that prevent concurrent inserts,
+      updates, or deletes on the table; whereas a standard reindex build
+      locks out writes (but not reads) on the table until it's done.
+      There are several caveats to be aware of when using this option
+      &mdash; see <xref linkend="SQL-REINDEX-CONCURRENTLY"
+      endterm="SQL-REINDEX-CONCURRENTLY-title">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>FORCE</literal></term>
     <listitem>
      <para>
@@ -231,6 +258,119 @@ REINDEX { INDEX | TABLE | DATABASE | SYSTEM } <replaceable class="PARAMETER">nam
    to be reindexed by separate commands.  This is still possible, but
    redundant.
   </para>
+
+
+  <refsect2 id="SQL-REINDEX-CONCURRENTLY">
+   <title id="SQL-REINDEX-CONCURRENTLY-title">Rebuilding Indexes Concurrently</title>
+
+   <indexterm zone="SQL-REINDEX-CONCURRENTLY">
+   <primary>index</primary>
+   <secondary>rebuilding concurrently</secondary>
+   </indexterm>
+
+   <para>
+    Rebuilding an index can interfere with regular operation of a database.
+    Normally <productname>PostgreSQL</> locks the table whose index is rebuilt
+    against writes and performs the entire index build with a single scan of the
+    table. Other transactions can still read the table, but if they try to
+    insert, update, or delete rows in the table they will block until the
+    index rebuild is finished. This could have a severe effect if the system is
+    a live production database.  Very large tables can take many hours to be
+    indexed, and even for smaller tables, an index rebuild can lock out writers
+    for periods that are unacceptably long for a production system.
+   </para>
+
+   <para>
+    <productname>PostgreSQL</> supports rebuilding indexes without locking
+    out writes.  This method is invoked by specifying the
+    <literal>CONCURRENTLY</> option of <command>REINDEX</>.
+    When this option is used, <productname>PostgreSQL</> must perform two
+    scans of the table for each index that needs to be rebuild and in
+    addition it must wait for all existing transactions that could potentially
+    use the index to terminate. This method requires more total work than a
+    standard index rebuild and takes significantly longer to complete as it
+    needs to wait for unfinished transactions that might modify the index.
+    However, since it allows normal operations to continue while the index
+    is rebuilt, this method is useful for rebuilding indexes in a production
+    environment.  Of course, the extra CPU, memory and I/O load imposed by
+    the index rebuild might slow other operations.
+   </para>
+
+   <para>
+    In a concurrent index build, a new index whose storage will replace the one
+    to be rebuild is actually entered into the system catalogs in one transaction,
+    then two table scans occur in two more transactions.  Once this is performed,
+    the old and fresh indexes are swapped in. During this phase the concurrent
+    index is marked as valid, is then swapped and marked as invalid. An exclusive
+    lock is taken at this phase. Finally two additional transactions are used to
+    mark the concurrent index as not ready and then drop it.
+   </para>
+
+   <para>
+    If a problem arises while rebuilding the indexes, such as a
+    uniqueness violation in a unique index, the <command>REINDEX</>
+    command will fail but leave behind an <quote>invalid</> new index on top
+    of the existing one. This index will be ignored for querying purposes
+    because it might be incomplete; however it will still consume update
+    overhead. The <application>psql</> <command>\d</> command will report
+    such an index as <literal>INVALID</>:
+
+<programlisting>
+postgres=# \d tab
+       Table "public.tab"
+ Column |  Type   | Modifiers
+--------+---------+-----------
+ col    | integer |
+Indexes:
+    "idx" btree (col)
+    "idx_cct" btree (col) INVALID
+</programlisting>
+
+    The recommended recovery method in such cases is to drop the concurrent
+    index and try again to perform <command>REINDEX CONCURRENTLY</>.
+    The concurrent index created during the processing has a name finishing by
+    the suffix cct. This works as well with indexes of toast relations.
+   </para>
+
+   <para>
+    Regular index builds permit other regular index builds on the
+    same table to occur in parallel, but only one concurrent index build
+    can occur on a table at a time.  In both cases, no other types of schema
+    modification on the table are allowed meanwhile.  Another difference
+    is that a regular <command>REINDEX TABLE</> or <command>REINDEX INDEX</>
+    command can be performed within a transaction block, but
+    <command>REINDEX CONCURRENTLY</> cannot. <command>REINDEX DATABASE</> is
+    by default not allowed to run inside a transaction block, so in this case
+    <command>CONCURRENTLY</> is not supported.
+   </para>
+
+   <para>
+    Invalid indexes of toast relations can be dropped if a failure occurred
+    during <command>REINDEX CONCURRENTLY</>. Live indexes of toast relations
+    cannot be dropped.
+   </para>
+
+   <para>
+    <command>REINDEX DATABASE</command> used with <command>CONCURRENTLY
+    </command> rebuilds concurrently only the non-system relations. System
+    relations are rebuilt with a non-concurrent context. Toast indexes are
+    rebuilt concurrently if the relation they depend on is a non-system
+    relation.
+   </para>
+
+   <para>
+    <command>REINDEX</command> uses <literal>ACCESS EXCLUSIVE</literal> lock
+    on all the relations involved during operation. When <command>CONCURRENTLY</command>
+    is specified, the operation is done with <literal>SHARE UPDATE EXCLUSIVE</literal>
+    except during relation swap where <literal>ACCESS EXCLUSIVE</literal> lock
+    is taken.
+   </para>
+
+   <para>
+    <command>REINDEX SYSTEM</command> does not support <command>CONCURRENTLY
+    </command>.
+   </para>
+  </refsect2>
  </refsect1>
 
  <refsect1>
@@ -262,7 +402,18 @@ $ <userinput>psql broken_db</userinput>
 ...
 broken_db=&gt; REINDEX DATABASE broken_db;
 broken_db=&gt; \q
-</programlisting></para>
+</programlisting>
+  </para>
+
+  <para>
+   Rebuild a table while authorizing read and write operations on involved
+   relations when performed:
+
+<programlisting>
+REINDEX TABLE CONCURRENTLY my_broken_table;
+</programlisting>
+  </para>
+
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index fc37ceb..e1af68d 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -76,11 +76,13 @@ 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 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 Relation toast_index_fetch_valid(Relation *toastidxs, int num_indexes);
 
 
 /* ----------
@@ -1237,8 +1239,8 @@ static Datum
 toast_save_datum(Relation rel, Datum value,
 				 struct varlena * oldexternal, int options)
 {
-	Relation	toastrel;
-	Relation	toastidx;
+	Relation	toastrel, validtoastidx;
+	Relation   *toastidxs;
 	HeapTuple	toasttup;
 	TupleDesc	toasttupDesc;
 	Datum		t_values[3];
@@ -1257,15 +1259,29 @@ toast_save_datum(Relation rel, Datum value,
 	char	   *data_p;
 	int32		data_todo;
 	Pointer		dval = DatumGetPointer(value);
+	ListCell   *lc;
+	int			i = 0;
+	int			num_indexes;
 
 	/*
 	 * 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.
+	 * 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;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
+	RelationGetIndexListIfValid(toastrel);
+	num_indexes = list_length(toastrel->rd_indexlist);
+
+	toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation));
+
+	/* Open all the indexes of toast relation with similar lock */
+	foreach(lc, toastrel->rd_indexlist)
+		toastidxs[i++] = index_open(lfirst_oid(lc), RowExclusiveLock);
+
+	/* Fetch relation used for process */
+	validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes);
 
 	/*
 	 * Get the data pointer and length, and compute va_rawsize and va_extsize.
@@ -1330,7 +1346,7 @@ toast_save_datum(Relation rel, Datum value,
 		/* normal case: just choose an unused OID */
 		toast_pointer.va_valueid =
 			GetNewOidWithIndex(toastrel,
-							   RelationGetRelid(toastidx),
+							   RelationGetRelid(validtoastidx),
 							   (AttrNumber) 1);
 	}
 	else
@@ -1367,7 +1383,8 @@ toast_save_datum(Relation rel, Datum value,
 				 * be reclaimed by VACUUM.
 				 */
 				if (toastrel_valueid_exists(toastrel,
-											toast_pointer.va_valueid))
+											toast_pointer.va_valueid,
+											RowExclusiveLock))
 				{
 					/* Match, so short-circuit the data storage loop below */
 					data_todo = 0;
@@ -1384,7 +1401,7 @@ toast_save_datum(Relation rel, Datum value,
 			{
 				toast_pointer.va_valueid =
 					GetNewOidWithIndex(toastrel,
-									   RelationGetRelid(toastidx),
+									   RelationGetRelid(validtoastidx),
 									   (AttrNumber) 1);
 			} while (toastid_valueid_exists(rel->rd_toastoid,
 											toast_pointer.va_valueid));
@@ -1423,16 +1440,18 @@ 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.
+		 * 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.
 		 */
-		index_insert(toastidx, t_values, t_isnull,
-					 &(toasttup->t_self),
-					 toastrel,
-					 toastidx->rd_index->indisunique ?
-					 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
+		for (i = 0; i < num_indexes; i++)
+			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
@@ -1449,8 +1468,10 @@ toast_save_datum(Relation rel, Datum value,
 	/*
 	 * Done - close toast relation
 	 */
-	index_close(toastidx, RowExclusiveLock);
+	for (i = 0; i < num_indexes; i++)
+		index_close(toastidxs[i], RowExclusiveLock);
 	heap_close(toastrel, RowExclusiveLock);
+	pfree(toastidxs);
 
 	/*
 	 * Create the TOAST pointer value that we'll return
@@ -1474,11 +1495,14 @@ toast_delete_datum(Relation rel, Datum value)
 {
 	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
 	struct varatt_external toast_pointer;
-	Relation	toastrel;
-	Relation	toastidx;
+	Relation	toastrel, validtoastidx;
+	Relation   *toastidxs;
 	ScanKeyData toastkey;
 	SysScanDesc toastscan;
 	HeapTuple	toasttup;
+	ListCell   *lc;
+	int			num_indexes;
+	int			i = 0;
 
 	if (!VARATT_IS_EXTERNAL(attr))
 		return;
@@ -1487,10 +1511,22 @@ toast_delete_datum(Relation rel, Datum value)
 	VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
 
 	/*
-	 * Open the toast relation and its index
+	 * Open the toast relation and its indexes
 	 */
 	toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
+	RelationGetIndexListIfValid(toastrel);
+	num_indexes = list_length(toastrel->rd_indexlist);
+	toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation));
+
+	/*
+	 * We actually use only the first valid index but taking a lock on all is
+	 * necessary.
+	 */
+	foreach(lc, toastrel->rd_indexlist)
+		toastidxs[i++] = index_open(lfirst_oid(lc), RowExclusiveLock);
+
+	/* Fetch relation used for process */
+	validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes);
 
 	/*
 	 * Setup a scan key to find chunks with matching va_valueid
@@ -1505,7 +1541,7 @@ 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,
+	toastscan = systable_beginscan_ordered(toastrel, validtoastidx,
 										   SnapshotToast, 1, &toastkey);
 	while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1519,8 +1555,10 @@ toast_delete_datum(Relation rel, Datum value)
 	 * End scan and close relations
 	 */
 	systable_endscan_ordered(toastscan);
-	index_close(toastidx, RowExclusiveLock);
+	for (i = 0; i < num_indexes; i++)
+		index_close(toastidxs[i], RowExclusiveLock);
 	heap_close(toastrel, RowExclusiveLock);
+	pfree(toastidxs);
 }
 
 
@@ -1531,11 +1569,28 @@ toast_delete_datum(Relation rel, Datum value)
  * ----------
  */
 static bool
-toastrel_valueid_exists(Relation toastrel, Oid valueid)
+toastrel_valueid_exists(Relation toastrel, Oid valueid, LOCKMODE lockmode)
 {
 	bool		result = false;
 	ScanKeyData toastkey;
 	SysScanDesc toastscan;
+	int			i = 0;
+	int			num_indexes;
+	Relation   *toastidxs;
+	Relation	validtoastidx;
+	ListCell   *lc;
+
+	/* Ensure that the list of indexes of toast relation is computed */
+	RelationGetIndexListIfValid(toastrel);
+	num_indexes = list_length(toastrel->rd_indexlist);
+
+	/* Open each index relation necessary */
+	toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation));
+	foreach(lc, toastrel->rd_indexlist)
+		toastidxs[i++] = index_open(lfirst_oid(lc), lockmode);
+
+	/* Fetch a valid index relation */
+	validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes);
 
 	/*
 	 * Setup a scan key to find chunks with matching va_valueid
@@ -1548,7 +1603,8 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid)
 	/*
 	 * Is there any such chunk?
 	 */
-	toastscan = systable_beginscan(toastrel, toastrel->rd_rel->reltoastidxid,
+	toastscan = systable_beginscan(toastrel,
+								   RelationGetRelid(validtoastidx),
 								   true, SnapshotToast, 1, &toastkey);
 
 	if (systable_getnext(toastscan) != NULL)
@@ -1556,6 +1612,11 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid)
 
 	systable_endscan(toastscan);
 
+	/* Clean up */
+	for (i = 0; i < num_indexes; i++)
+		index_close(toastidxs[i], lockmode);
+	pfree(toastidxs);
+
 	return result;
 }
 
@@ -1573,7 +1634,7 @@ toastid_valueid_exists(Oid toastrelid, Oid valueid)
 
 	toastrel = heap_open(toastrelid, AccessShareLock);
 
-	result = toastrel_valueid_exists(toastrel, valueid);
+	result = toastrel_valueid_exists(toastrel, valueid, AccessShareLock);
 
 	heap_close(toastrel, AccessShareLock);
 
@@ -1591,8 +1652,8 @@ toastid_valueid_exists(Oid toastrelid, Oid valueid)
 static struct varlena *
 toast_fetch_datum(struct varlena * attr)
 {
-	Relation	toastrel;
-	Relation	toastidx;
+	Relation	toastrel, validtoastidx;
+	Relation   *toastidxs;
 	ScanKeyData toastkey;
 	SysScanDesc toastscan;
 	HeapTuple	ttup;
@@ -1607,6 +1668,9 @@ toast_fetch_datum(struct varlena * attr)
 	bool		isnull;
 	char	   *chunkdata;
 	int32		chunksize;
+	ListCell   *lc;
+	int			num_indexes;
+	int			i = 0;
 
 	/* Must copy to access aligned fields */
 	VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
@@ -1622,11 +1686,21 @@ toast_fetch_datum(struct varlena * attr)
 		SET_VARSIZE(result, ressize + VARHDRSZ);
 
 	/*
-	 * Open the toast relation and its index
+	 * Open the toast relation and its indexes
 	 */
 	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
+	RelationGetIndexListIfValid(toastrel);
+	num_indexes = list_length(toastrel->rd_indexlist);
+
+	toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation));
+
+	/* Open all the indexes of toast relation with similar lock */
+	foreach(lc, toastrel->rd_indexlist)
+		toastidxs[i++] = index_open(lfirst_oid(lc), AccessShareLock);
+
+	/* Fetch relation used for process */
+	validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes);
 
 	/*
 	 * Setup a scan key to fetch from the index by va_valueid
@@ -1645,7 +1719,7 @@ toast_fetch_datum(struct varlena * attr)
 	 */
 	nextidx = 0;
 
-	toastscan = systable_beginscan_ordered(toastrel, toastidx,
+	toastscan = systable_beginscan_ordered(toastrel, validtoastidx,
 										   SnapshotToast, 1, &toastkey);
 	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1734,8 +1808,10 @@ toast_fetch_datum(struct varlena * attr)
 	 * End scan and close relations
 	 */
 	systable_endscan_ordered(toastscan);
-	index_close(toastidx, AccessShareLock);
+	for (i = 0; i < num_indexes; i++)
+		index_close(toastidxs[i], AccessShareLock);
 	heap_close(toastrel, AccessShareLock);
+	pfree(toastidxs);
 
 	return result;
 }
@@ -1750,8 +1826,8 @@ toast_fetch_datum(struct varlena * attr)
 static struct varlena *
 toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
 {
-	Relation	toastrel;
-	Relation	toastidx;
+	Relation	toastrel, validtoastidx;
+	Relation   *toastidxs;
 	ScanKeyData toastkey[3];
 	int			nscankeys;
 	SysScanDesc toastscan;
@@ -1774,6 +1850,9 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
 	int32		chunksize;
 	int32		chcpystrt;
 	int32		chcpyend;
+	int			num_indexes;
+	int			i = 0;
+	ListCell   *lc;
 
 	Assert(VARATT_IS_EXTERNAL(attr));
 
@@ -1816,11 +1895,18 @@ 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
+	 * Open the toast relation and its indexes
 	 */
 	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
+	RelationGetIndexListIfValid(toastrel);
+	num_indexes = list_length(toastrel->rd_indexlist);
+
+	toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation));
+
+	foreach(lc, toastrel->rd_indexlist)
+		toastidxs[i++] = index_open(lfirst_oid(lc), AccessShareLock);
+	validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes);
 
 	/*
 	 * Setup a scan key to fetch from the index. This is either two keys or
@@ -1861,7 +1947,7 @@ 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,
+	toastscan = systable_beginscan_ordered(toastrel, validtoastidx,
 										 SnapshotToast, nscankeys, toastkey);
 	while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1958,8 +2044,36 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
 	 * End scan and close relations
 	 */
 	systable_endscan_ordered(toastscan);
-	index_close(toastidx, AccessShareLock);
+	for (i = 0; i < num_indexes; i++)
+		index_close(toastidxs[i], AccessShareLock);
 	heap_close(toastrel, AccessShareLock);
+	pfree(toastidxs);
 
 	return result;
 }
+
+/* ----------
+ * toast_index_fetch_valid
+ *
+ *	Get a valid index in list of indexes for a toast relation. Those relations
+ *	need to be already open prior calling this routine.
+ */
+static Relation
+toast_index_fetch_valid(Relation *toastidxs, int num_indexes)
+{
+	int			i;
+	Relation	res = NULL;
+
+	/* Fetch the first valid index in list */
+	for (i = 0; i < num_indexes; i++)
+	{
+		if (toastidxs[i]->rd_index->indisvalid)
+		{
+			res = toastidxs[i];
+			break;
+		}
+	}
+
+	Assert(res);
+	return res;
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0b4c659..8114d77 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -768,7 +768,6 @@ 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);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7966558..73686f6 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -43,9 +43,11 @@
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "catalog/storage.h"
+#include "commands/defrem.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -103,7 +105,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 					bool isvalid);
 static void index_update_stats(Relation rel,
 				   bool hasindex, bool isprimary,
-				   Oid reltoastidxid, double reltuples);
+				   double reltuples);
 static void IndexCheckExclusion(Relation heapRelation,
 					Relation indexRelation,
 					IndexInfo *indexInfo);
@@ -672,6 +674,10 @@ UpdateIndexRelation(Oid indexoid,
  *		will be marked "invalid" and the caller must take additional steps
  *		to fix it up.
  * is_internal: if true, post creation hook for new index
+ * is_reindex: if true, create an index that is used as a duplicate of an
+ *		existing index created during a concurrent operation. This index can
+ *		also be a toast relation. Sufficient locks are normally taken on
+ *		the related relations once this is called during a concurrent operation.
  *
  * Returns the OID of the created index.
  */
@@ -695,7 +701,8 @@ index_create(Relation heapRelation,
 			 bool allow_system_table_mods,
 			 bool skip_build,
 			 bool concurrent,
-			 bool is_internal)
+			 bool is_internal,
+			 bool is_reindex)
 {
 	Oid			heapRelationId = RelationGetRelid(heapRelation);
 	Relation	pg_class;
@@ -738,19 +745,22 @@ index_create(Relation heapRelation,
 
 	/*
 	 * concurrent index build on a system catalog is unsafe because we tend to
-	 * release locks before committing in catalogs
+	 * release locks before committing in catalogs. If the index is created during
+	 * a REINDEX CONCURRENTLY operation, sufficient locks are already taken.
 	 */
 	if (concurrent &&
-		IsSystemRelation(heapRelation))
+		IsSystemRelation(heapRelation) &&
+		!is_reindex)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("concurrent index creation on system catalog tables is not supported")));
 
 	/*
-	 * This case is currently not supported, but there's no way to ask for it
-	 * in the grammar anyway, so it can't happen.
+	 * This case is currently only supported during a concurrent index
+	 * rebuild, but there is no way to ask for it in the grammar otherwise
+	 * anyway.
 	 */
-	if (concurrent && is_exclusion)
+	if (concurrent && is_exclusion && !is_reindex)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg_internal("concurrent index creation for exclusion constraints is not supported")));
@@ -1071,7 +1081,6 @@ index_create(Relation heapRelation,
 		index_update_stats(heapRelation,
 						   true,
 						   isprimary,
-						   InvalidOid,
 						   -1.0);
 		/* Make the above update visible */
 		CommandCounterIncrement();
@@ -1090,6 +1099,438 @@ index_create(Relation heapRelation,
 	return indexRelationId;
 }
 
+
+/*
+ * index_concurrent_create
+ *
+ * Create an index based on the given one that will be used for concurrent
+ * operations. The index is inserted into catalogs and needs to be built later
+ * on. This is called during concurrent index processing. The heap relation
+ * on which is based the index needs to be closed by the caller.
+ */
+Oid
+index_concurrent_create(Relation heapRelation, Oid indOid, char *concurrentName)
+{
+	Relation	indexRelation;
+	IndexInfo  *indexInfo;
+	Oid			concurrentOid = InvalidOid;
+	List	   *columnNames = NIL;
+	List	   *indexprs = NIL;
+	ListCell   *indexpr_item;
+	int			i;
+	HeapTuple	indexTuple, classTuple;
+	Datum		indclassDatum, colOptionDatum, optionDatum;
+	oidvector  *indclass;
+	int2vector *indcoloptions;
+	bool		isnull;
+	bool		initdeferred = false;
+	Oid			constraintOid = get_index_constraint(indOid);
+
+	indexRelation = index_open(indOid, RowExclusiveLock);
+
+	/* Concurrent index uses the same index information as former index */
+	indexInfo = BuildIndexInfo(indexRelation);
+
+	/*
+	 * Determine if index is initdeferred, this depends on its dependent
+	 * constraint.
+	 */
+	if (OidIsValid(constraintOid))
+	{
+		/* Look for the correct value */
+		HeapTuple			constraintTuple;
+		Form_pg_constraint	constraintForm;
+
+		constraintTuple = SearchSysCache1(CONSTROID,
+									 ObjectIdGetDatum(constraintOid));
+		if (!HeapTupleIsValid(constraintTuple))
+			elog(ERROR, "cache lookup failed for constraint %u",
+				 constraintOid);
+		constraintForm = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+		initdeferred = constraintForm->condeferred;
+
+		ReleaseSysCache(constraintTuple);
+	}
+
+	/* Get expressions associated to this index for compilation of column names */
+	indexprs = RelationGetIndexExpressions(indexRelation);
+	indexpr_item = list_head(indexprs);
+
+	/* Build the list of column names, necessary for index_create */
+	for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+	{
+		char	   *origname, *curname;
+		char		buf[NAMEDATALEN];
+		AttrNumber	attnum = indexInfo->ii_KeyAttrNumbers[i];
+		int			j;
+
+		/* Pick up column name depending on attribute type */
+		if (attnum > 0)
+		{
+			/*
+			 * This is a column attribute, so simply pick column name from
+			 * relation.
+			 */
+			Form_pg_attribute attform = heapRelation->rd_att->attrs[attnum - 1];;
+			origname = pstrdup(NameStr(attform->attname));
+		}
+		else if (attnum < 0)
+		{
+			/* Case of a system attribute */
+			Form_pg_attribute attform = SystemAttributeDefinition(attnum,
+										  heapRelation->rd_rel->relhasoids);
+			origname = pstrdup(NameStr(attform->attname));
+		}
+		else
+		{
+			Node *indnode;
+			/*
+			 * This is the case of an expression, so pick up the expression
+			 * name.
+			 */
+			Assert(indexpr_item != NULL);
+			indnode = (Node *) lfirst(indexpr_item);
+			indexpr_item = lnext(indexpr_item);
+			origname = deparse_expression(indnode,
+							deparse_context_for(RelationGetRelationName(heapRelation),
+												RelationGetRelid(heapRelation)),
+							false, false);
+		}
+
+		/*
+		 * Check if the name picked has any conflict with exising names and
+		 * change it.
+		 */
+		curname = origname;
+		for (j = 1;; j++)
+		{
+			ListCell   *lc2;
+			char		nbuf[32];
+			int			nlen;
+
+			foreach(lc2, columnNames)
+			{
+				if (strcmp(curname, (char *) lfirst(lc2)) == 0)
+					break;
+			}
+			if (lc2 == NULL)
+				break; /* found nonconflicting name */
+
+			sprintf(nbuf, "%d", j);
+
+			/* Ensure generated names are shorter than NAMEDATALEN */
+			nlen = pg_mbcliplen(origname, strlen(origname),
+								NAMEDATALEN - 1 - strlen(nbuf));
+			memcpy(buf, origname, nlen);
+			strcpy(buf + nlen, nbuf);
+			curname = buf;
+		}
+
+		/* Append name to existing list */
+		columnNames = lappend(columnNames, pstrdup(curname));
+	}
+
+	/* Get the array of class and column options IDs from index info */
+	indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid));
+	if (!HeapTupleIsValid(indexTuple))
+		elog(ERROR, "cache lookup failed for index %u", indOid);
+	indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
+									Anum_pg_index_indclass, &isnull);
+	Assert(!isnull);
+	indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+	colOptionDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
+									 Anum_pg_index_indoption, &isnull);
+	Assert(!isnull);
+	indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);
+
+	/* Fetch options of index if any */
+	classTuple = SearchSysCache1(RELOID, indOid);
+	if (!HeapTupleIsValid(classTuple))
+		elog(ERROR, "cache lookup failed for relation %u", indOid);
+	optionDatum = SysCacheGetAttr(RELOID, classTuple,
+								  Anum_pg_class_reloptions, &isnull);
+
+	/* Now create the concurrent index */
+	concurrentOid = index_create(heapRelation,
+								 (const char *) concurrentName,
+								 InvalidOid,
+								 InvalidOid,
+								 indexInfo,
+								 columnNames,
+								 indexRelation->rd_rel->relam,
+								 indexRelation->rd_rel->reltablespace,
+								 indexRelation->rd_indcollation,
+								 indclass->values,
+								 indcoloptions->values,
+								 optionDatum,
+								 indexRelation->rd_index->indisprimary,
+								 OidIsValid(constraintOid),	/* is constraint? */
+								 !indexRelation->rd_index->indimmediate,	/* is deferrable? */
+								 initdeferred,	/* is initially deferred? */
+								 true,	/* allow table to be a system catalog? */
+								 true,	/* skip build? */
+								 true,	/* concurrent? */
+								 false,	/* is_internal */
+								 true); /* reindex? */
+
+	/* Close the relations used and clean up */
+	index_close(indexRelation, RowExclusiveLock);
+	ReleaseSysCache(indexTuple);
+	ReleaseSysCache(classTuple);
+
+	return concurrentOid;
+}
+
+
+/*
+ * index_concurrent_build
+ *
+ * Build index for a concurrent operation. Low-level locks are taken when this
+ * operation is performed to prevent only schema changes.
+ */
+void
+index_concurrent_build(Oid heapOid,
+					   Oid indexOid,
+					   bool isprimary)
+{
+	Relation	rel,
+				indexRelation;
+	IndexInfo  *indexInfo;
+
+	/* Open and lock the parent heap relation */
+	rel = heap_open(heapOid, ShareUpdateExclusiveLock);
+
+	/* And the target index relation */
+	indexRelation = index_open(indexOid, RowExclusiveLock);
+
+	/*
+	 * We have to re-build the IndexInfo struct, since it was lost in
+	 * commit of transaction where this concurrent index was created
+	 * at the catalog level.
+	 */
+	indexInfo = BuildIndexInfo(indexRelation);
+	Assert(!indexInfo->ii_ReadyForInserts);
+	indexInfo->ii_Concurrent = true;
+	indexInfo->ii_BrokenHotChain = false;
+
+	/* Now build the index */
+	index_build(rel, indexRelation, indexInfo, isprimary, false);
+
+	/* Close both the relations, but keep the locks */
+	heap_close(rel, NoLock);
+	index_close(indexRelation, NoLock);
+}
+
+
+/*
+ * index_concurrent_swap
+ *
+ * Swap old index and new index in a concurrent context. For the time being
+ * what is done here is switching the relation relfilenode of the indexes. If
+ * extra operations are necessary during a concurrent swap, processing should
+ * be added here. AccessExclusiveLock is taken on the index relations that are
+ * swapped until the end of the transaction where this function is called.
+ * Note: a lower lock could be taken if catalog cache with SnapshotNow was
+ * correctly MVCC'd
+ */
+void
+index_concurrent_swap(Oid newIndexOid, Oid oldIndexOid)
+{
+	Relation		oldIndexRel, newIndexRel, pg_class;
+	HeapTuple		oldIndexTuple, newIndexTuple;
+	Form_pg_class	oldIndexForm, newIndexForm;
+	Oid				tmpnode;
+
+	/*
+	 * Take an exclusive lock on the old and new index before swapping them.
+	 */
+	oldIndexRel = relation_open(oldIndexOid, AccessExclusiveLock);
+	newIndexRel = relation_open(newIndexOid, AccessExclusiveLock);
+
+	/* Now swap relfilenode of those indexes */
+	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+	oldIndexTuple = SearchSysCacheCopy1(RELOID,
+										ObjectIdGetDatum(oldIndexOid));
+	if (!HeapTupleIsValid(oldIndexTuple))
+		elog(ERROR, "could not find tuple for relation %u", oldIndexOid);
+	newIndexTuple = SearchSysCacheCopy1(RELOID,
+										ObjectIdGetDatum(newIndexOid));
+	if (!HeapTupleIsValid(newIndexTuple))
+		elog(ERROR, "could not find tuple for relation %u", newIndexOid);
+	oldIndexForm = (Form_pg_class) GETSTRUCT(oldIndexTuple);
+	newIndexForm = (Form_pg_class) GETSTRUCT(newIndexTuple);
+
+	/* Here is where the actual swapping happens */
+	tmpnode = oldIndexForm->relfilenode;
+	oldIndexForm->relfilenode = newIndexForm->relfilenode;
+	newIndexForm->relfilenode = tmpnode;
+
+	/* Then update the tuples for each relation */
+	simple_heap_update(pg_class, &oldIndexTuple->t_self, oldIndexTuple);
+	simple_heap_update(pg_class, &newIndexTuple->t_self, newIndexTuple);
+	CatalogUpdateIndexes(pg_class, oldIndexTuple);
+	CatalogUpdateIndexes(pg_class, newIndexTuple);
+
+	/* Close relations and clean up */
+	heap_freetuple(oldIndexTuple);
+	heap_freetuple(newIndexTuple);
+	heap_close(pg_class, RowExclusiveLock);
+
+	/* The lock taken previously is not released until the end of transaction */
+	relation_close(oldIndexRel, NoLock);
+	relation_close(newIndexRel, NoLock);
+}
+
+/*
+ * index_concurrent_set_dead
+ *
+ * Perform the last invalidation stage of DROP INDEX CONCURRENTLY before
+ * actually dropping the index. After calling this function the index is
+ * seen by all the backends as dead.
+ */
+void
+index_concurrent_set_dead(Oid indexId, Oid heapId, LOCKTAG locktag)
+{
+	Relation	heapRelation;
+	Relation	indexRelation;
+
+	/*
+	 * Now we must wait until no running transaction could be using the
+	 * index for a query if necessary.
+	 *
+	 * Note: the reason we use actual lock acquisition here, rather than
+	 * just checking the ProcArray and sleeping, is that deadlock is
+	 * possible if one of the transactions in question is blocked trying
+	 * to acquire an exclusive lock on our table. The lock code will
+	 * detect deadlock and error out properly.
+	 */
+	WaitForVirtualLocks(locktag, AccessExclusiveLock);
+
+	/*
+	 * No more predicate locks will be acquired on this index, and we're
+	 * about to stop doing inserts into the index which could show
+	 * conflicts with existing predicate locks, so now is the time to move
+	 * them to the heap relation.
+	 */
+	heapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
+	indexRelation = index_open(indexId, ShareUpdateExclusiveLock);
+	TransferPredicateLocksToHeapRelation(indexRelation);
+
+	/*
+	 * Now we are sure that nobody uses the index for queries; they just
+	 * might have it open for updating it.	So now we can unset indisready
+	 * and indislive, then wait till nobody could be using it at all
+	 * anymore.
+	 */
+	index_set_state_flags(indexId, INDEX_DROP_SET_DEAD, true);
+
+	/*
+	 * Invalidate the relcache for the table, so that after this commit
+	 * all sessions will refresh the table's index list.  Forgetting just
+	 * the index's relcache entry is not enough.
+	 */
+	CacheInvalidateRelcache(heapRelation);
+
+	/*
+	 * Close the relations again, though still holding session lock.
+	 */
+	heap_close(heapRelation, NoLock);
+	index_close(indexRelation, NoLock);
+}
+
+/*
+ * index_concurrent_clear_valid
+ *
+ * Release the valid state of a given index and then release the cache of
+ * its parent relation. This function should be called when initializing an
+ * index drop in a concurrent context before setting the index as dead if
+ * if called in a concurrent context.
+ */
+void
+index_concurrent_clear_valid(Relation heapRelation,
+							 Oid indexOid,
+							 bool concurrent)
+{
+	/*
+	 * Mark index invalid by updating its pg_index entry
+	 */
+	index_set_state_flags(indexOid, INDEX_DROP_CLEAR_VALID, concurrent);
+
+	/*
+	 * Invalidate the relcache for the table, so that after this commit
+	 * all sessions will refresh any cached plans that might reference the
+	 * index.
+	 */
+	CacheInvalidateRelcache(heapRelation);
+}
+
+/*
+ * index_concurrent_drop
+ *
+ * Drop a single index concurrently as the last step of an index concurrent
+ * process. Deletion is done through performDeletion or dependencies of the
+ * index would not get dropped. At this point all the indexes are already
+ * considered as invalid and dead so they can be dropped without using any
+ * concurrent options as it is sure that they will not interact with other
+ * server sessions.
+ */
+void
+index_concurrent_drop(Oid indexOid)
+{
+	Oid				constraintOid = get_index_constraint(indexOid);
+	ObjectAddress	object;
+	Form_pg_index	indexForm;
+	Relation		pg_index;
+	HeapTuple		indexTuple;
+
+	/*
+	 * Check that the index dropped here is not alive, it might be used by
+	 * other backends in this case.
+	 */
+	pg_index = heap_open(IndexRelationId, RowExclusiveLock);
+
+	indexTuple = SearchSysCacheCopy1(INDEXRELID,
+									 ObjectIdGetDatum(indexOid));
+	if (!HeapTupleIsValid(indexTuple))
+		elog(ERROR, "cache lookup failed for index %u", indexOid);
+	indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
+
+	/*
+	 * This is only a safety check, just to avoid live indexes from being
+	 * dropped.
+	 */
+	if (indexForm->indislive)
+		elog(ERROR, "cannot drop live index with OID %u", indexOid);
+
+	/* Clean up */
+	heap_close(pg_index, RowExclusiveLock);
+
+	/*
+	 * We are sure to have a dead index, so begin the drop process.
+	 * Register constraint or index for drop.
+	 */
+	if (OidIsValid(constraintOid))
+	{
+		object.classId = ConstraintRelationId;
+		object.objectId = constraintOid;
+	}
+	else
+	{
+		object.classId = RelationRelationId;
+		object.objectId = indexOid;
+	}
+
+	object.objectSubId = 0;
+
+	/* Perform deletion for normal and toast indexes */
+	performDeletion(&object,
+					DROP_RESTRICT,
+					0);
+}
+
+
 /*
  * index_constraint_create
  *
@@ -1253,7 +1694,6 @@ index_constraint_create(Relation heapRelation,
 		index_update_stats(heapRelation,
 						   true,
 						   true,
-						   InvalidOid,
 						   -1.0);
 
 	/*
@@ -1326,7 +1766,6 @@ index_drop(Oid indexId, bool concurrent)
 				indexrelid;
 	LOCKTAG		heaplocktag;
 	LOCKMODE	lockmode;
-	VirtualTransactionId *old_lockholders;
 
 	/*
 	 * To drop an index safely, we must grab exclusive lock on its parent
@@ -1408,17 +1847,8 @@ index_drop(Oid indexId, bool concurrent)
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("DROP INDEX CONCURRENTLY must be first action in transaction")));
 
-		/*
-		 * Mark index invalid by updating its pg_index entry
-		 */
-		index_set_state_flags(indexId, INDEX_DROP_CLEAR_VALID);
-
-		/*
-		 * Invalidate the relcache for the table, so that after this commit
-		 * all sessions will refresh any cached plans that might reference the
-		 * index.
-		 */
-		CacheInvalidateRelcache(userHeapRelation);
+		/* Mark the index as invalid */
+		index_concurrent_clear_valid(userHeapRelation, indexId, true);
 
 		/* save lockrelid and locktag for below, then close but keep locks */
 		heaprelid = userHeapRelation->rd_lockInfo.lockRelId;
@@ -1446,63 +1876,8 @@ index_drop(Oid indexId, bool concurrent)
 		CommitTransactionCommand();
 		StartTransactionCommand();
 
-		/*
-		 * Now we must wait until no running transaction could be using the
-		 * index for a query.  To do this, inquire which xacts currently would
-		 * conflict with AccessExclusiveLock on the table -- ie, which ones
-		 * have a lock of any kind on the table. Then wait for each of these
-		 * xacts to commit or abort. Note we do not need to worry about xacts
-		 * that open the table for reading after this point; they will see the
-		 * index as invalid when they open the relation.
-		 *
-		 * Note: the reason we use actual lock acquisition here, rather than
-		 * just checking the ProcArray and sleeping, is that deadlock is
-		 * possible if one of the transactions in question is blocked trying
-		 * to acquire an exclusive lock on our table.  The lock code will
-		 * detect deadlock and error out properly.
-		 *
-		 * Note: GetLockConflicts() never reports our own xid, hence we need
-		 * not check for that.	Also, prepared xacts are not reported, which
-		 * is fine since they certainly aren't going to do anything more.
-		 */
-		old_lockholders = GetLockConflicts(&heaplocktag, AccessExclusiveLock);
-
-		while (VirtualTransactionIdIsValid(*old_lockholders))
-		{
-			VirtualXactLock(*old_lockholders, true);
-			old_lockholders++;
-		}
-
-		/*
-		 * No more predicate locks will be acquired on this index, and we're
-		 * about to stop doing inserts into the index which could show
-		 * conflicts with existing predicate locks, so now is the time to move
-		 * them to the heap relation.
-		 */
-		userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
-		userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock);
-		TransferPredicateLocksToHeapRelation(userIndexRelation);
-
-		/*
-		 * Now we are sure that nobody uses the index for queries; they just
-		 * might have it open for updating it.	So now we can unset indisready
-		 * and indislive, then wait till nobody could be using it at all
-		 * anymore.
-		 */
-		index_set_state_flags(indexId, INDEX_DROP_SET_DEAD);
-
-		/*
-		 * Invalidate the relcache for the table, so that after this commit
-		 * all sessions will refresh the table's index list.  Forgetting just
-		 * the index's relcache entry is not enough.
-		 */
-		CacheInvalidateRelcache(userHeapRelation);
-
-		/*
-		 * Close the relations again, though still holding session lock.
-		 */
-		heap_close(userHeapRelation, NoLock);
-		index_close(userIndexRelation, NoLock);
+		/* Finish invalidation of index and mark it as dead */
+		index_concurrent_set_dead(indexId, heapId, heaplocktag);
 
 		/*
 		 * Again, commit the transaction to make the pg_index update visible
@@ -1515,13 +1890,7 @@ index_drop(Oid indexId, bool concurrent)
 		 * Wait till every transaction that saw the old index state has
 		 * finished.  The logic here is the same as above.
 		 */
-		old_lockholders = GetLockConflicts(&heaplocktag, AccessExclusiveLock);
-
-		while (VirtualTransactionIdIsValid(*old_lockholders))
-		{
-			VirtualXactLock(*old_lockholders, true);
-			old_lockholders++;
-		}
+		WaitForVirtualLocks(heaplocktag, AccessExclusiveLock);
 
 		/*
 		 * Re-open relations to allow us to complete our actions.
@@ -1763,8 +2132,6 @@ 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
@@ -1780,8 +2147,9 @@ FormIndexDatum(IndexInfo *indexInfo,
  */
 static void
 index_update_stats(Relation rel,
-				   bool hasindex, bool isprimary,
-				   Oid reltoastidxid, double reltuples)
+				   bool hasindex,
+				   bool isprimary,
+				   double reltuples)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Relation	pg_class;
@@ -1875,15 +2243,6 @@ 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)
 	{
@@ -2071,14 +2430,11 @@ 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 */
@@ -3005,27 +3361,32 @@ validate_index_heapscan(Relation heapRelation,
  * index_set_state_flags - adjust pg_index state flags
  *
  * This is used during CREATE/DROP INDEX CONCURRENTLY to adjust the pg_index
- * flags that denote the index's state.  We must use an in-place update of
- * the pg_index tuple, because we do not have exclusive lock on the parent
- * table and so other sessions might concurrently be doing SnapshotNow scans
- * of pg_index to identify the table's indexes.  A transactional update would
- * risk somebody not seeing the index at all.  Because the update is not
- * transactional and will not roll back on error, this must only be used as
- * the last step in a transaction that has not made any transactional catalog
- * updates!
+ * flags that denote the index's state.  If this function is called in a
+ * concurrent process, we use an in-place update of the pg_index tuple,
+ * because we do not have exclusive lock on the parent table and so other
+ * sessions might concurrently be doing SnapshotNow scans of pg_index to
+ * identify the table's indexes.  A transactional update would risk somebody
+ * not seeing the index at all.  Because the update is not transactional
+ * and will not roll back on error, this must only be used as the last step
+ * in a transaction that has not made any transactional catalog updates!
  *
  * Note that heap_inplace_update does send a cache inval message for the
  * tuple, so other sessions will hear about the update as soon as we commit.
  */
 void
-index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
+index_set_state_flags(Oid indexId,
+					  IndexStateFlagsAction action,
+					  bool concurrent)
 {
 	Relation	pg_index;
 	HeapTuple	indexTuple;
 	Form_pg_index indexForm;
 
-	/* Assert that current xact hasn't done any transactional updates */
-	Assert(GetTopTransactionIdIfAny() == InvalidTransactionId);
+	/*
+	 * Assert that current xact hasn't done any transactional updates, there
+	 * is nothing to worry in a non-concurrent context.
+	 */
+	Assert(!concurrent || GetTopTransactionIdIfAny() == InvalidTransactionId);
 
 	/* Open pg_index and fetch a writable copy of the index's tuple */
 	pg_index = heap_open(IndexRelationId, RowExclusiveLock);
@@ -3085,8 +3446,20 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 			break;
 	}
 
-	/* ... and write it back in-place */
-	heap_inplace_update(pg_index, indexTuple);
+	/*
+	 * Write it back in-place in a concurrent context, and do a simple update
+	 * for a non-concurrent context.
+	 */
+	if (concurrent)
+	{
+		heap_inplace_update(pg_index, indexTuple);
+	}
+	else
+	{
+		simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
+		CommandCounterIncrement();
+		CatalogUpdateIndexes(pg_index, indexTuple);
+	}
 
 	heap_close(pg_index, RowExclusiveLock);
 }
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f727acd..01d58d9 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -473,16 +473,16 @@ 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
+            pg_stat_get_blocks_fetched(X.indrelid) -
+                    pg_stat_get_blocks_hit(X.indrelid) AS tidx_blks_read,
+            pg_stat_get_blocks_hit(X.indrelid) 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
+            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.oid;
+    GROUP BY C.oid, N.nspname, C.relname, T.oid, X.indrelid;
 
 CREATE VIEW pg_statio_sys_tables AS
     SELECT * FROM pg_statio_all_tables
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 385d64d..0c2971b 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -281,7 +281,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 				 rel->rd_rel->reltablespace,
 				 collationObjectId, classObjectId, coloptions, (Datum) 0,
 				 true, false, false, false,
-				 true, false, false, true);
+				 true, false, false, false, false);
 
 	heap_close(toast_rel, NoLock);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ef9c5f1..5ef164b 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1176,8 +1176,6 @@ 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
@@ -1396,19 +1394,62 @@ 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 we're swapping two toast tables by content, do the same for all of
+	 * their indexes. The swap can actually be safely done only if the
+	 * relations have 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);
+		relform1->reltoastrelid &&
+		relform2->reltoastrelid)
+	{
+		Relation toastRel1, toastRel2;
+
+		/* Open relations */
+		toastRel1 = heap_open(relform1->reltoastrelid, AccessExclusiveLock);
+		toastRel2 = heap_open(relform2->reltoastrelid, AccessExclusiveLock);
+
+		/* Obtain index list */
+		RelationGetIndexList(toastRel1);
+		RelationGetIndexList(toastRel2);
+
+		/* Check if the swap is possible for all the toast indexes */
+		if (list_length(toastRel1->rd_indexlist) == 1 &&
+			list_length(toastRel2->rd_indexlist) == 1)
+		{
+			ListCell *lc1, *lc2;
+
+			/* Now swap each couple */
+			lc2 = list_head(toastRel2->rd_indexlist);
+			foreach(lc1, toastRel1->rd_indexlist)
+			{
+				Oid indexOid1 = lfirst_oid(lc1);
+				Oid indexOid2 = lfirst_oid(lc2);
+				swap_relation_files(indexOid1,
+									indexOid2,
+									target_is_pg_class,
+									swap_toast_by_content,
+									is_internal,
+									InvalidTransactionId,
+									InvalidMultiXactId,
+									mapped_tables);
+				lc2 = lnext(lc2);
+			}
+		}
+		else
+		{
+			/*
+			 * As this code path is only taken by shared catalogs, who cannot
+			 * have multiple indexes on their toast relation, simply return
+			 * an error.
+			 */
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot swap relation files of a shared catalog with multiple indexes on toast relation")));
+		}
+
+		heap_close(toastRel1, AccessExclusiveLock);
+		heap_close(toastRel2, AccessExclusiveLock);
+	}
 
 	/* Clean up. */
 	heap_freetuple(reltup1);
@@ -1533,12 +1574,13 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 		if (OidIsValid(newrel->rd_rel->reltoastrelid))
 		{
 			Relation	toastrel;
-			Oid			toastidx;
 			char		NewToastName[NAMEDATALEN];
+			ListCell   *lc;
+			int			count = 0;
 
 			toastrel = relation_open(newrel->rd_rel->reltoastrelid,
 									 AccessShareLock);
-			toastidx = toastrel->rd_rel->reltoastidxid;
+			RelationGetIndexList(toastrel);
 			relation_close(toastrel, AccessShareLock);
 
 			/* rename the toast table ... */
@@ -1547,11 +1589,23 @@ 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);
+			/* ... and its indexes too */
+			foreach(lc, toastrel->rd_indexlist)
+			{
+				/*
+				 * The first index keeps the former toast name and the
+				 * following entries have a suffix appended.
+				 */
+				if (count == 0)
+					snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
+							 OIDOldHeap);
+				else
+					snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index_%d",
+							 OIDOldHeap, count);
+				RenameRelationInternal(lfirst_oid(lc),
+									   NewToastName, true);
+				count++;
+			}
 		}
 		relation_close(newrel, NoLock);
 	}
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index f855bef..2ea997f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -68,8 +68,9 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
 static Oid GetIndexOpClass(List *opclass, Oid attrType,
 				char *accessMethodName, Oid accessMethodId);
 static char *ChooseIndexName(const char *tabname, Oid namespaceId,
-				List *colnames, List *exclusionOpNames,
-				bool primary, bool isconstraint);
+							 List *colnames, List *exclusionOpNames,
+							 bool primary, bool isconstraint,
+							 bool concurrent);
 static char *ChooseIndexNameAddition(List *colnames);
 static List *ChooseIndexColumnNames(List *indexElems);
 static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
@@ -311,7 +312,6 @@ DefineIndex(IndexStmt *stmt,
 	Oid			tablespaceId;
 	List	   *indexColNames;
 	Relation	rel;
-	Relation	indexRelation;
 	HeapTuple	tuple;
 	Form_pg_am	accessMethodForm;
 	bool		amcanorder;
@@ -320,13 +320,9 @@ DefineIndex(IndexStmt *stmt,
 	int16	   *coloptions;
 	IndexInfo  *indexInfo;
 	int			numberOfAttributes;
-	VirtualTransactionId *old_lockholders;
-	VirtualTransactionId *old_snapshots;
-	int			n_old_snapshots;
 	LockRelId	heaprelid;
 	LOCKTAG		heaplocktag;
 	Snapshot	snapshot;
-	int			i;
 
 	/*
 	 * count attributes in index
@@ -453,7 +449,8 @@ DefineIndex(IndexStmt *stmt,
 											indexColNames,
 											stmt->excludeOpNames,
 											stmt->primary,
-											stmt->isconstraint);
+											stmt->isconstraint,
+											false);
 
 	/*
 	 * look up the access method, verify it can handle the requested features
@@ -600,7 +597,7 @@ DefineIndex(IndexStmt *stmt,
 					 stmt->isconstraint, stmt->deferrable, stmt->initdeferred,
 					 allowSystemTableMods,
 					 skip_build || stmt->concurrent,
-					 stmt->concurrent, !check_rights);
+					 stmt->concurrent, !check_rights, false);
 
 	/* Add any requested comment */
 	if (stmt->idxcomment != NULL)
@@ -663,18 +660,8 @@ DefineIndex(IndexStmt *stmt,
 	 * one of the transactions in question is blocked trying to acquire an
 	 * exclusive lock on our table.  The lock code will detect deadlock and
 	 * error out properly.
-	 *
-	 * Note: GetLockConflicts() never reports our own xid, hence we need not
-	 * check for that.	Also, prepared xacts are not reported, which is fine
-	 * since they certainly aren't going to do anything more.
 	 */
-	old_lockholders = GetLockConflicts(&heaplocktag, ShareLock);
-
-	while (VirtualTransactionIdIsValid(*old_lockholders))
-	{
-		VirtualXactLock(*old_lockholders, true);
-		old_lockholders++;
-	}
+	WaitForVirtualLocks(heaplocktag, ShareLock);
 
 	/*
 	 * At this moment we are sure that there are no transactions with the
@@ -694,34 +681,20 @@ DefineIndex(IndexStmt *stmt,
 	 * HOT-chain or the extension of the chain is HOT-safe for this index.
 	 */
 
-	/* Open and lock the parent heap relation */
-	rel = heap_openrv(stmt->relation, ShareUpdateExclusiveLock);
-
-	/* And the target index relation */
-	indexRelation = index_open(indexRelationId, RowExclusiveLock);
-
 	/* Set ActiveSnapshot since functions in the indexes may need it */
 	PushActiveSnapshot(GetTransactionSnapshot());
 
-	/* We have to re-build the IndexInfo struct, since it was lost in commit */
-	indexInfo = BuildIndexInfo(indexRelation);
-	Assert(!indexInfo->ii_ReadyForInserts);
-	indexInfo->ii_Concurrent = true;
-	indexInfo->ii_BrokenHotChain = false;
-
-	/* Now build the index */
-	index_build(rel, indexRelation, indexInfo, stmt->primary, false);
-
-	/* Close both the relations, but keep the locks */
-	heap_close(rel, NoLock);
-	index_close(indexRelation, NoLock);
+	/* Perform concurrent build of index */
+	index_concurrent_build(RangeVarGetRelid(stmt->relation, NoLock, false),
+						   indexRelationId,
+						   stmt->primary);
 
 	/*
 	 * Update the pg_index row to mark the index as ready for inserts. Once we
 	 * commit this transaction, any new transactions that open the table must
 	 * insert new entries into the index for insertions and non-HOT updates.
 	 */
-	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY);
+	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY, true);
 
 	/* we can do away with our snapshot */
 	PopActiveSnapshot();
@@ -738,13 +711,7 @@ DefineIndex(IndexStmt *stmt,
 	 * We once again wait until no transaction can have the table open with
 	 * the index marked as read-only for updates.
 	 */
-	old_lockholders = GetLockConflicts(&heaplocktag, ShareLock);
-
-	while (VirtualTransactionIdIsValid(*old_lockholders))
-	{
-		VirtualXactLock(*old_lockholders, true);
-		old_lockholders++;
-	}
+	WaitForVirtualLocks(heaplocktag, ShareLock);
 
 	/*
 	 * Now take the "reference snapshot" that will be used by validate_index()
@@ -773,79 +740,14 @@ DefineIndex(IndexStmt *stmt,
 	 * The index is now valid in the sense that it contains all currently
 	 * interesting tuples.	But since it might not contain tuples deleted just
 	 * before the reference snap was taken, we have to wait out any
-	 * transactions that might have older snapshots.  Obtain a list of VXIDs
-	 * of such transactions, and wait for them individually.
-	 *
-	 * We can exclude any running transactions that have xmin > the xmin of
-	 * our reference snapshot; their oldest snapshot must be newer than ours.
-	 * We can also exclude any transactions that have xmin = zero, since they
-	 * evidently have no live snapshot at all (and any one they might be in
-	 * process of taking is certainly newer than ours).  Transactions in other
-	 * DBs can be ignored too, since they'll never even be able to see this
-	 * index.
-	 *
-	 * We can also exclude autovacuum processes and processes running manual
-	 * lazy VACUUMs, because they won't be fazed by missing index entries
-	 * either.	(Manual ANALYZEs, however, can't be excluded because they
-	 * might be within transactions that are going to do arbitrary operations
-	 * later.)
-	 *
-	 * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
-	 * check for that.
-	 *
-	 * If a process goes idle-in-transaction with xmin zero, we do not need to
-	 * wait for it anymore, per the above argument.  We do not have the
-	 * infrastructure right now to stop waiting if that happens, but we can at
-	 * least avoid the folly of waiting when it is idle at the time we would
-	 * begin to wait.  We do this by repeatedly rechecking the output of
-	 * GetCurrentVirtualXIDs.  If, during any iteration, a particular vxid
-	 * doesn't show up in the output, we know we can forget about it.
+	 * transactions that might have older snapshots.
 	 */
-	old_snapshots = GetCurrentVirtualXIDs(snapshot->xmin, true, false,
-										  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
-										  &n_old_snapshots);
-
-	for (i = 0; i < n_old_snapshots; i++)
-	{
-		if (!VirtualTransactionIdIsValid(old_snapshots[i]))
-			continue;			/* found uninteresting in previous cycle */
-
-		if (i > 0)
-		{
-			/* see if anything's changed ... */
-			VirtualTransactionId *newer_snapshots;
-			int			n_newer_snapshots;
-			int			j;
-			int			k;
-
-			newer_snapshots = GetCurrentVirtualXIDs(snapshot->xmin,
-													true, false,
-										 PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
-													&n_newer_snapshots);
-			for (j = i; j < n_old_snapshots; j++)
-			{
-				if (!VirtualTransactionIdIsValid(old_snapshots[j]))
-					continue;	/* found uninteresting in previous cycle */
-				for (k = 0; k < n_newer_snapshots; k++)
-				{
-					if (VirtualTransactionIdEquals(old_snapshots[j],
-												   newer_snapshots[k]))
-						break;
-				}
-				if (k >= n_newer_snapshots)		/* not there anymore */
-					SetInvalidVirtualTransactionId(old_snapshots[j]);
-			}
-			pfree(newer_snapshots);
-		}
-
-		if (VirtualTransactionIdIsValid(old_snapshots[i]))
-			VirtualXactLock(old_snapshots[i], true);
-	}
+	WaitForOldSnapshots(snapshot);
 
 	/*
 	 * Index can now be marked valid -- update its pg_index entry
 	 */
-	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);
+	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID, true);
 
 	/*
 	 * The pg_index update will cause backends (including this one) to update
@@ -873,6 +775,544 @@ DefineIndex(IndexStmt *stmt,
 
 
 /*
+ * ReindexRelationConcurrently
+ *
+ * Process REINDEX CONCURRENTLY for given relation Oid. The relation can be
+ * either an index or a table. If a table is specified, each reindexing step
+ * is done in parallel with all the table's indexes as well as its dependent
+ * toast indexes.
+ */
+bool
+ReindexRelationConcurrently(Oid relationOid)
+{
+	List	   *concurrentIndexIds = NIL,
+			   *indexIds = NIL,
+			   *parentRelationIds = NIL,
+			   *lockTags = NIL,
+			   *relationLocks = NIL;
+	ListCell   *lc, *lc2;
+	Snapshot	snapshot;
+
+	/*
+	 * Extract the list of indexes that are going to be rebuilt based on the
+	 * list of relation Oids given by caller. For each element in given list,
+	 * If the relkind of given relation Oid is a table, all its valid indexes
+	 * will be rebuilt, including its associated toast table indexes. If
+	 * relkind is an index, this index itself will be rebuilt. The locks taken
+	 * parent relations and involved indexes are kept until this transaction
+	 * is committed to protect against schema changes that might occur until
+	 * the session lock is taken on each relation.
+	 */
+	switch (get_rel_relkind(relationOid))
+	{
+		case RELKIND_RELATION:
+		case RELKIND_MATVIEW:
+			{
+				/*
+				 * In the case of a relation, find all its indexes
+				 * including toast indexes.
+				 */
+				Relation	heapRelation = heap_open(relationOid,
+													ShareUpdateExclusiveLock);
+
+				/* Track this relation for session locks */
+				parentRelationIds = lappend_oid(parentRelationIds, relationOid);
+
+				/* Relation on which is based index cannot be shared */
+				if (heapRelation->rd_rel->relisshared)
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("concurrent reindex is not supported for shared relations")));
+
+				/* Add all the valid indexes of relation to list */
+				foreach(lc2, RelationGetIndexList(heapRelation))
+				{
+					Oid			cellOid = lfirst_oid(lc2);
+					Relation	indexRelation = index_open(cellOid,
+													ShareUpdateExclusiveLock);
+
+					if (!indexRelation->rd_index->indisvalid)
+						ereport(WARNING,
+								(errcode(ERRCODE_INDEX_CORRUPTED),
+								 errmsg("cannot reindex concurrently invalid index \"%s.%s\", skipping",
+										get_namespace_name(get_rel_namespace(cellOid)),
+										get_rel_name(cellOid))));
+					else
+						indexIds = lappend_oid(indexIds, cellOid);
+
+					index_close(indexRelation, NoLock);
+				}
+
+				/* Also add the toast indexes */
+				if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
+				{
+					Oid			toastOid = heapRelation->rd_rel->reltoastrelid;
+					Relation	toastRelation = heap_open(toastOid,
+												ShareUpdateExclusiveLock);
+
+					/* Track this relation for session locks */
+					parentRelationIds = lappend_oid(parentRelationIds, toastOid);
+
+					foreach(lc2, RelationGetIndexList(toastRelation))
+					{
+						Oid			cellOid = lfirst_oid(lc2);
+						Relation	indexRelation = index_open(cellOid,
+													ShareUpdateExclusiveLock);
+
+						if (!indexRelation->rd_index->indisvalid)
+							ereport(WARNING,
+									(errcode(ERRCODE_INDEX_CORRUPTED),
+									 errmsg("cannot reindex concurrently invalid index \"%s.%s\", skipping",
+											get_namespace_name(get_rel_namespace(cellOid)),
+											get_rel_name(cellOid))));
+						else
+							indexIds = lappend_oid(indexIds, cellOid);
+
+						index_close(indexRelation, NoLock);
+					}
+
+					heap_close(toastRelation, NoLock);
+				}
+
+				heap_close(heapRelation, NoLock);
+				break;
+			}
+		case RELKIND_INDEX:
+			{
+				/*
+				 * For an index simply add its Oid to list. Invalid indexes
+				 * cannot be included in list.
+				 */
+				Relation	indexRelation = index_open(relationOid, ShareUpdateExclusiveLock);
+
+				/* Track the parent relation of this index for session locks */
+				parentRelationIds = list_make1_oid(IndexGetRelation(relationOid, false));
+
+				if (!indexRelation->rd_index->indisvalid)
+					ereport(WARNING,
+							(errcode(ERRCODE_INDEX_CORRUPTED),
+							 errmsg("cannot reindex concurrently invalid index \"%s.%s\", skipping",
+									get_namespace_name(get_rel_namespace(relationOid)),
+									get_rel_name(relationOid))));
+				else
+					indexIds = list_make1_oid(relationOid);
+
+				index_close(indexRelation, NoLock);
+				break;
+			}
+		default:
+			/* Return error if type of relation is not supported */
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("cannot reindex concurrently this type of relation")));
+			break;
+	}
+
+	/* Definetely no indexes, so leave */
+	if (indexIds == NIL)
+		return false;
+
+	Assert(parentRelationIds != NIL);
+
+	/*
+	 * Phase 1 of REINDEX CONCURRENTLY
+	 *
+	 * Here begins the process for rebuilding concurrently the indexes.
+	 * We need first to create an index which is based on the same data
+	 * as the former index except that it will be only registered in catalogs
+	 * and will be built after. It is possible to perform all the operations
+	 * on all the indexes at the same time for a parent relation including
+	 * its indexes for toast relation.
+	 */
+
+	/* Do the concurrent index creation for each index */
+	foreach(lc, indexIds)
+	{
+		char	   *concurrentName;
+		Oid			indOid = lfirst_oid(lc);
+		Oid			concurrentOid = InvalidOid;
+		Relation	indexRel,
+					indexParentRel,
+					indexConcurrentRel;
+		LockRelId	lockrelid;
+
+		indexRel = index_open(indOid, ShareUpdateExclusiveLock);
+		/* Open the index parent relation, might be a toast or parent relation */
+		indexParentRel = heap_open(indexRel->rd_index->indrelid,
+								   ShareUpdateExclusiveLock);
+
+		/* Choose a relation name for concurrent index */
+		concurrentName = ChooseIndexName(get_rel_name(indOid),
+										 get_rel_namespace(indexRel->rd_index->indrelid),
+										 NULL,
+										 false,
+										 false,
+										 false,
+										 true);
+
+		/* Create concurrent index based on given index */
+		concurrentOid = index_concurrent_create(indexParentRel,
+												indOid,
+												concurrentName);
+
+		/*
+		 * Now open the relation of concurrent index, a lock is also needed on
+		 * it
+		 */
+		indexConcurrentRel = index_open(concurrentOid, ShareUpdateExclusiveLock);
+
+		/* Save the concurrent index Oid */
+		concurrentIndexIds = lappend_oid(concurrentIndexIds, concurrentOid);
+
+		/*
+		 * Save lockrelid to protect each concurrent relation from drop then
+		 * close relations. The lockrelid on parent relation is not taken here
+		 * to avoid multiple locks taken on the same relation, instead we rely
+		 * on parentRelationIds built earlier.
+		 */
+		lockrelid = indexRel->rd_lockInfo.lockRelId;
+		relationLocks = lappend(relationLocks, &lockrelid);
+		lockrelid = indexConcurrentRel->rd_lockInfo.lockRelId;
+		relationLocks = lappend(relationLocks, &lockrelid);
+
+		index_close(indexRel, NoLock);
+		index_close(indexConcurrentRel, NoLock);
+		heap_close(indexParentRel, NoLock);
+	}
+
+	/*
+	 * Save the heap lock for following visibility checks with other backends
+	 * might conflict with this session.
+	 */
+	foreach(lc, parentRelationIds)
+	{
+		Relation	heapRelation = heap_open(lfirst_oid(lc), ShareUpdateExclusiveLock);
+		LockRelId	lockrelid = heapRelation->rd_lockInfo.lockRelId;
+		LOCKTAG		*heaplocktag = (LOCKTAG *) palloc(sizeof(LOCKTAG));
+
+		/* Add lockrelid of parent relation to the list of locked relations */
+		relationLocks = lappend(relationLocks, &lockrelid);
+
+		/* Save the LOCKTAG for this parent relation for the wait phase */
+		SET_LOCKTAG_RELATION(*heaplocktag, lockrelid.dbId, lockrelid.relId);
+		lockTags = lappend(lockTags, heaplocktag);
+
+		/* Close heap relation */
+		heap_close(heapRelation, NoLock);
+	}
+
+	/*
+	 * For a concurrent build, it is necessary to make the catalog entries
+	 * visible to the other transactions before actually building the index.
+	 * This will prevent them from making incompatible HOT updates. The index
+	 * is marked as not ready and invalid so as no other transactions will try
+	 * to use it for INSERT or SELECT.
+	 *
+	 * Before committing, get a session level lock on the relation, the
+	 * concurrent index and its copy to insure that none of them are dropped
+	 * until the operation is done.
+	 */
+	foreach(lc, relationLocks)
+	{
+		LockRelId lockRel = * (LockRelId *) lfirst(lc);
+		LockRelationIdForSession(&lockRel, ShareUpdateExclusiveLock);
+	}
+
+	PopActiveSnapshot();
+	CommitTransactionCommand();
+
+	/*
+	 * Phase 2 of REINDEX CONCURRENTLY
+	 *
+	 * Build concurrent indexes in a separate transaction for each index to
+	 * avoid having open transactions for an unnecessary long time. A
+	 * concurrent build is done for each concurrent index that will replace
+	 * the old indexes. Before doing that, we need to wait on the parent
+	 * relations until no running transactions could have the parent table
+	 * of index open.
+	 */
+
+	/* Perform a wait on all the session locks */
+	StartTransactionCommand();
+	WaitForMultipleVirtualLocks(lockTags, ShareLock);
+	CommitTransactionCommand();
+
+	forboth(lc, indexIds, lc2, concurrentIndexIds)
+	{
+		Relation	indexRel;
+		Oid			indOid = lfirst_oid(lc);
+		Oid			concurrentOid = lfirst_oid(lc2);
+		bool		primary;
+
+		/* Check for any process interruption */
+		CHECK_FOR_INTERRUPTS();
+
+		/* Start new transaction for this index concurrent build */
+		StartTransactionCommand();
+
+		/* Set ActiveSnapshot since functions in the indexes may need it */
+		PushActiveSnapshot(GetTransactionSnapshot());
+
+		/* Index relation has been closed by previous commit, so reopen it */
+		indexRel = index_open(indOid, ShareUpdateExclusiveLock);
+		primary = indexRel->rd_index->indisprimary;
+		index_close(indexRel, ShareUpdateExclusiveLock);
+
+		/* Perform concurrent build of new index */
+		index_concurrent_build(indexRel->rd_index->indrelid,
+							   concurrentOid,
+							   primary);
+
+		/*
+		 * Update the pg_index row of the concurrent index as ready for inserts.
+		 * Once we commit this transaction, any new transactions that open the
+		 * table must insert new entries into the index for insertions and
+		 * non-HOT updates.
+		 */
+		index_set_state_flags(concurrentOid, INDEX_CREATE_SET_READY, true);
+
+		/* we can do away with our snapshot */
+		PopActiveSnapshot();
+
+		/*
+		 * Commit this transaction to make the indisready update visible for
+		 * concurrent index.
+		 */
+		CommitTransactionCommand();
+	}
+
+
+	/*
+	 * Phase 3 of REINDEX CONCURRENTLY
+	 *
+	 * During this phase the concurrent indexes catch up with the INSERT that
+	 * might have occurred in the parent table.
+	 *
+	 * We once again wait until no transaction can have the table open with
+	 * the index marked as read-only for updates. Each index validation is done
+	 * with a separate transaction to avoid opening transaction for an
+	 * unnecessary too long time.
+	 */
+
+	/* Perform a wait on all the session locks */
+	StartTransactionCommand();
+	WaitForMultipleVirtualLocks(lockTags, ShareLock);
+	CommitTransactionCommand();
+
+	/*
+	 * Perform a scan of each concurrent index with the heap, then insert
+	 * any missing index entries.
+	 */
+	foreach(lc, concurrentIndexIds)
+	{
+		Oid indOid = lfirst_oid(lc);
+		Oid relOid;
+
+		/* Check for any process interruption */
+		CHECK_FOR_INTERRUPTS();
+
+		/* Open separate transaction to validate index */
+		StartTransactionCommand();
+
+		/* Get the parent relation Oid */
+		relOid = IndexGetRelation(indOid, false);
+
+		/*
+		 * Take the reference snapshot that will be used for the concurrent indexes
+		 * validation.
+		 */
+		snapshot = RegisterSnapshot(GetTransactionSnapshot());
+		PushActiveSnapshot(snapshot);
+
+		/* Validate index, which might be a toast */
+		validate_index(relOid, indOid, snapshot);
+
+		/*
+		 * This concurrent index is now valid as they contain all the tuples
+		 * necessary. However, it might not have taken into account deleted tuples
+		 * before the reference snapshot was taken, so we need to wait for the
+		 * transactions that might have older snapshots than ours.
+		 */
+		WaitForOldSnapshots(snapshot);
+
+		/* we can now do away with our active snapshot */
+		PopActiveSnapshot();
+
+		/* And we can remove the validating snapshot too */
+		UnregisterSnapshot(snapshot);
+
+		/* Commit this transaction to make the concurrent index valid */
+		CommitTransactionCommand();
+	}
+
+	/*
+	 * Phase 4 of REINDEX CONCURRENTLY
+	 *
+	 * Now that the concurrent indexes are valid and can be used, we need to
+	 * swap each concurrent index with its corresponding old index. The
+	 * concurrent index is marked as valid before performing the swap, and
+	 * is invalidated once the swap is done, making it not usable by other
+	 * backends once its associated transaction is committed.
+	 */
+
+	/* Swap the indexes and mark the indexes that have the old data as invalid */
+	forboth(lc, indexIds, lc2, concurrentIndexIds)
+	{
+		Oid			indOid = lfirst_oid(lc);
+		Oid			concurrentOid = lfirst_oid(lc2);
+		Relation	indexRel, indexParentRel;
+
+		/* Check for any process interruption */
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * Each index needs to be swapped in a separate transaction, so start
+		 * a new one.
+		 */
+		StartTransactionCommand();
+
+		/*
+		 * Mark the cache of associated relation as invalid, open relation
+		 * relations. AccessExclusive Lock is taken here and not a lower lock
+		 * to reduce likelihood of deadlock as ShareUpdateExclusiveLock is
+		 * already taken within session.
+		 */
+		indexRel = index_open(indOid, AccessExclusiveLock);
+		indexParentRel = heap_open(indexRel->rd_index->indrelid,
+								   AccessExclusiveLock);
+
+		/*
+		 * Concurrent index can now be marked as valid before performing
+		 * the swap. Note here that as an exclusive lock is taken on the
+		 * relations involved it is safer to call this function in a non
+		 * concurrent context.
+		 */
+		index_set_state_flags(concurrentOid, INDEX_CREATE_SET_VALID, false);
+
+		/* Swap old index and its concurrent */
+		index_concurrent_swap(concurrentOid, indOid);
+
+		/*
+		 * Now mark the old index as invalid, the swap is done.
+		 */
+		index_concurrent_clear_valid(indexParentRel, concurrentOid, false);
+
+		/*
+		 * Invalidate the relcache for the table, so that after this commit
+		 * all sessions will refresh any cached plans that might reference the
+		 * index.
+		 */
+		CacheInvalidateRelcache(indexParentRel);
+
+		/* Close relations opened previously for cache invalidation */
+		index_close(indexRel, NoLock);
+		heap_close(indexParentRel, NoLock);
+
+		/* Commit this transaction and make old index invalidation visible */
+		CommitTransactionCommand();
+	}
+
+	/*
+	 * Phase 5 of REINDEX CONCURRENTLY
+	 *
+	 * The concurrent indexes now hold the old relfilenode of the other indexes
+	 *  transactions that might use them. Each operation is performed with a
+	 * separate transaction.
+	 */
+
+	/* Now mark the concurrent indexes as not ready */
+	foreach(lc, concurrentIndexIds)
+	{
+		Oid			indOid = lfirst_oid(lc);
+		Oid			relOid;
+		LOCKTAG	   *heapLockTag = NULL;
+		ListCell   *cell;
+
+		/* Check for any process interruption */
+		CHECK_FOR_INTERRUPTS();
+
+		StartTransactionCommand();
+		relOid = IndexGetRelation(indOid, false);
+
+		/*
+		 * Find the locktag of parent table for this index, we need to wait for
+		 * locks on it.
+		 */
+		foreach(cell, lockTags)
+		{
+			LOCKTAG *localTag = (LOCKTAG *) lfirst(cell);
+			if (relOid == localTag->locktag_field2)
+				heapLockTag = localTag;
+		}
+		Assert(heapLockTag && heapLockTag->locktag_field2 != InvalidOid);
+
+		/*
+		 * Finish the index invalidation and set it as dead. Note that it is
+		 * necessary to wait for for virtual locks on the parent relation
+		 * before setting the index as dead.
+		 */
+		index_concurrent_set_dead(indOid, relOid, *heapLockTag);
+
+		/* Commit this transaction to make the update visible. */
+		CommitTransactionCommand();
+	}
+
+	/*
+	 * Phase 6 of REINDEX CONCURRENTLY
+	 *
+	 * Drop the concurrent indexes. This needs to be done through
+	 * performDeletion or related dependencies will not be dropped for the old
+	 * indexes. The internal mechanism of DROP INDEX CONCURRENTLY is not used
+	 * as here the indexes are already considered as dead and invalid, so they
+	 * will not be used by other backends.
+	 */
+	foreach(lc, concurrentIndexIds)
+	{
+		Oid indexOid = lfirst_oid(lc);
+
+		/* Check for any process interruption */
+		CHECK_FOR_INTERRUPTS();
+
+		/* Start transaction to drop this index */
+		StartTransactionCommand();
+
+		/* Get fresh snapshot for next step */
+		PushActiveSnapshot(GetTransactionSnapshot());
+
+		/*
+		 * Open transaction if necessary, for the first index treated its
+		 * transaction has been already opened previously.
+		 */
+		index_concurrent_drop(indexOid);
+
+		/* We can do away with our snapshot */
+		PopActiveSnapshot();
+
+		/* Commit this transaction to make the update visible. */
+		CommitTransactionCommand();
+	}
+
+	/*
+	 * Last thing to do is release the session-level lock on the parent table
+	 * and the indexes of table.
+	 */
+	foreach(lc, relationLocks)
+	{
+		LockRelId lockRel = * (LockRelId *) lfirst(lc);
+		UnlockRelationIdForSession(&lockRel, ShareUpdateExclusiveLock);
+	}
+
+	/* Start a new transaction to finish process properly */
+	StartTransactionCommand();
+
+	/* Get fresh snapshot for the end of process */
+	PushActiveSnapshot(GetTransactionSnapshot());
+
+	return true;
+}
+
+
+/*
  * CheckMutability
  *		Test whether given expression is mutable
  */
@@ -1535,7 +1975,8 @@ ChooseRelationName(const char *name1, const char *name2,
 static char *
 ChooseIndexName(const char *tabname, Oid namespaceId,
 				List *colnames, List *exclusionOpNames,
-				bool primary, bool isconstraint)
+				bool primary, bool isconstraint,
+				bool concurrent)
 {
 	char	   *indexname;
 
@@ -1561,6 +2002,13 @@ ChooseIndexName(const char *tabname, Oid namespaceId,
 									   "key",
 									   namespaceId);
 	}
+	else if (concurrent)
+	{
+		indexname = ChooseRelationName(tabname,
+									   NULL,
+									   "cct",
+									   namespaceId);
+	}
 	else
 	{
 		indexname = ChooseRelationName(tabname,
@@ -1673,18 +2121,22 @@ ChooseIndexColumnNames(List *indexElems)
  *		Recreate a specific index.
  */
 Oid
-ReindexIndex(RangeVar *indexRelation)
+ReindexIndex(RangeVar *indexRelation, bool concurrent)
 {
 	Oid			indOid;
 	Oid			heapOid = InvalidOid;
 
-	/* lock level used here should match index lock reindex_index() */
-	indOid = RangeVarGetRelidExtended(indexRelation, AccessExclusiveLock,
-									  false, false,
-									  RangeVarCallbackForReindexIndex,
-									  (void *) &heapOid);
+	indOid = RangeVarGetRelidExtended(indexRelation,
+				concurrent ? ShareUpdateExclusiveLock : AccessExclusiveLock,
+				false, false,
+				RangeVarCallbackForReindexIndex,
+				(void *) &heapOid);
 
-	reindex_index(indOid, false);
+	/* Continue process for concurrent or non-concurrent case */
+	if (!concurrent)
+		reindex_index(indOid, false);
+	else
+		ReindexRelationConcurrently(indOid);
 
 	return indOid;
 }
@@ -1748,18 +2200,33 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
 	}
 }
 
+
 /*
  * ReindexTable
  *		Recreate all indexes of a table (and of its toast table, if any)
  */
 Oid
-ReindexTable(RangeVar *relation)
+ReindexTable(RangeVar *relation, bool concurrent)
 {
 	Oid			heapOid;
 
 	/* The lock level used here should match reindex_relation(). */
-	heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false,
-									   RangeVarCallbackOwnsTable, NULL);
+	heapOid = RangeVarGetRelidExtended(relation,
+		concurrent ? ShareUpdateExclusiveLock : ShareLock,
+		false, false,
+		RangeVarCallbackOwnsTable, NULL);
+
+	/* Run through the concurrent process if necessary */
+	if (concurrent)
+	{
+		if (!ReindexRelationConcurrently(heapOid))
+		{
+			ereport(NOTICE,
+					(errmsg("table \"%s\" has no indexes",
+							relation->relname)));
+		}
+		return heapOid;
+	}
 
 	if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST))
 		ereport(NOTICE,
@@ -1778,7 +2245,10 @@ ReindexTable(RangeVar *relation)
  * That means this must not be called within a user transaction block!
  */
 Oid
-ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
+ReindexDatabase(const char *databaseName,
+				bool do_system,
+				bool do_user,
+				bool concurrent)
 {
 	Relation	relationRelation;
 	HeapScanDesc scan;
@@ -1790,6 +2260,15 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
 
 	AssertArg(databaseName);
 
+	/*
+	 * CONCURRENTLY operation is not allowed for a system, but it is for a
+	 * database.
+	 */
+	if (concurrent && !do_user)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot reindex system concurrently")));
+
 	if (strcmp(databaseName, get_database_name(MyDatabaseId)) != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1873,15 +2352,40 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
 	foreach(l, relids)
 	{
 		Oid			relid = lfirst_oid(l);
+		bool		result = false;
+		bool		process_concurrent;
 
 		StartTransactionCommand();
 		/* functions in indexes may want a snapshot set */
 		PushActiveSnapshot(GetTransactionSnapshot());
-		if (reindex_relation(relid, REINDEX_REL_PROCESS_TOAST))
+
+		/* Determine if relation needs to be processed concurrently */
+		process_concurrent = concurrent &&
+			!IsSystemNamespace(get_rel_namespace(relid));
+
+		/*
+		 * Reindex relation with a concurrent or non-concurrent process.
+		 * System relations cannot be reindexed concurrently, but they
+		 * need to be reindexed including pg_class with a normal process
+		 * as they could be corrupted, and concurrent process might also
+		 * use them. This does not include toast relations, which are
+		 * reindexed when their parent relation is processed.
+		 */
+		if (process_concurrent)
+		{
+			old = MemoryContextSwitchTo(private_context);
+			result = ReindexRelationConcurrently(relid);
+			MemoryContextSwitchTo(old);
+		}
+		else
+			result = reindex_relation(relid, REINDEX_REL_PROCESS_TOAST);
+
+		if (result)
 			ereport(NOTICE,
-					(errmsg("table \"%s.%s\" was reindexed",
+					(errmsg("table \"%s.%s\" was reindexed%s",
 							get_namespace_name(get_rel_namespace(relid)),
-							get_rel_name(relid))));
+							get_rel_name(relid),
+							process_concurrent ? " concurrently" : "")));
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 	}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 536d232..d83e0b6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -899,6 +899,38 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
 	if (classform->relkind != relkind)
 		DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
 
+	/*
+	 * Check the case of a system index that might have been invalidated by a
+	 * failed concurrent process and allow its drop. For the time being, this
+	 * only concerns indexes of toast relations that became invalid during a
+	 * REINDEX CONCURRENTLY process.
+	 */
+	if (IsSystemClass(classform) &&
+		relkind == RELKIND_INDEX)
+	{
+		HeapTuple		locTuple;
+		Form_pg_index	indexform;
+		bool			indisvalid;
+
+		locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(state->heapOid));
+		if (!HeapTupleIsValid(locTuple))
+		{
+			ReleaseSysCache(tuple);
+			return;
+		}
+
+		indexform = (Form_pg_index) GETSTRUCT(locTuple);
+		indisvalid = indexform->indisvalid;
+		ReleaseSysCache(locTuple);
+
+		/* Leave if index entry is not valid */
+		if (!indisvalid)
+		{
+			ReleaseSysCache(tuple);
+			return;
+		}
+	}
+
 	/* Allow DROP to either table owner or schema owner */
 	if (!pg_class_ownercheck(relOid, GetUserId()) &&
 		!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
@@ -8726,7 +8758,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	Relation	rel;
 	Oid			oldTableSpace;
 	Oid			reltoastrelid;
-	Oid			reltoastidxid;
 	Oid			newrelfilenode;
 	RelFileNode newrnode;
 	SMgrRelation dstrel;
@@ -8734,6 +8765,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	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
@@ -8780,7 +8813,14 @@ 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;
+	/* Fetch the list of indexes on toast relation if necessary */
+	if (OidIsValid(reltoastrelid))
+	{
+		Relation toastRel = relation_open(reltoastrelid, lockmode);
+		RelationGetIndexList(toastRel);
+		reltoastidxids = list_copy(toastRel->rd_indexlist);
+		relation_close(toastRel, NoLock);
+	}
 
 	/* Get a modifiable copy of the relation's pg_class row */
 	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
@@ -8861,8 +8901,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	/* Move associated toast relation and/or index, too */
 	if (OidIsValid(reltoastrelid))
 		ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
-	if (OidIsValid(reltoastidxid))
-		ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
+	foreach(lc, reltoastidxids)
+	{
+		Oid idxid = lfirst_oid(lc);
+		if (OidIsValid(idxid))
+			ATExecSetTableSpace(idxid, newTableSpace, lockmode);
+	}
+
+	/* Clean up */
+	list_free(reltoastidxids);
 }
 
 /*
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 11be62e..c46bdcc 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1185,6 +1185,20 @@ check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
 	}
 
 	/*
+	 * As an invalid index only exists when created in a concurrent context,
+	 * and that this code path cannot be taken by CREATE INDEX CONCURRENTLY
+	 * as this feature is not available for exclusion constraints, this code
+	 * path can only be taken by REINDEX CONCURRENTLY. In this case the same
+	 * index exists in parallel to this one so we can bypass this check as
+	 * it has already been done on the other index existing in parallel.
+	 * If exclusion constraints are supported in the future for CREATE INDEX
+	 * CONCURRENTLY, this should be removed or completed especially for this
+	 * purpose.
+	 */
+	if (!index->rd_index->indisvalid)
+		return true;
+
+	/*
 	 * Search the tuples that are in the index for any violations, including
 	 * tuples that aren't visible yet.
 	 */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fd3823a..27408b4 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3618,6 +3618,7 @@ _copyReindexStmt(const ReindexStmt *from)
 	COPY_STRING_FIELD(name);
 	COPY_SCALAR_FIELD(do_system);
 	COPY_SCALAR_FIELD(do_user);
+	COPY_SCALAR_FIELD(concurrent);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 085cd5b..2687bf0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1853,6 +1853,7 @@ _equalReindexStmt(const ReindexStmt *a, const ReindexStmt *b)
 	COMPARE_STRING_FIELD(name);
 	COMPARE_SCALAR_FIELD(do_system);
 	COMPARE_SCALAR_FIELD(do_user);
+	COMPARE_SCALAR_FIELD(concurrent);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0d82141..2d91451 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -6761,29 +6761,32 @@ opt_if_exists: IF_P EXISTS						{ $$ = TRUE; }
  *****************************************************************************/
 
 ReindexStmt:
-			REINDEX reindex_type qualified_name opt_force
+			REINDEX reindex_type opt_concurrently qualified_name opt_force
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = $2;
-					n->relation = $3;
+					n->concurrent = $3;
+					n->relation = $4;
 					n->name = NULL;
 					$$ = (Node *)n;
 				}
-			| REINDEX SYSTEM_P name opt_force
+			| REINDEX SYSTEM_P opt_concurrently name opt_force
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = OBJECT_DATABASE;
-					n->name = $3;
+					n->concurrent = $3;
+					n->name = $4;
 					n->relation = NULL;
 					n->do_system = true;
 					n->do_user = false;
 					$$ = (Node *)n;
 				}
-			| REINDEX DATABASE name opt_force
+			| REINDEX DATABASE opt_concurrently name opt_force
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = OBJECT_DATABASE;
-					n->name = $3;
+					n->concurrent = $3;
+					n->name = $4;
 					n->relation = NULL;
 					n->do_system = true;
 					n->do_user = true;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index cb59f13..388685a 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -575,8 +575,8 @@ 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.
+		 * 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,7 +588,6 @@ 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;
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 4308128..1662a6e 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -2528,6 +2528,152 @@ XidCacheRemoveRunningXids(TransactionId xid,
 	LWLockRelease(ProcArrayLock);
 }
 
+
+/*
+ * WaitForMultipleVirtualLocks
+ *
+ * Wait until no transactions hold the relation related to lock those locks.
+ * To do this, inquire which xacts currently would conflict with each lock on
+ * the table referred by the respective LOCKTAG -- ie, which ones have a lock
+ * that permits writing the relation. Then wait for each of these xacts to
+ * commit or abort.
+ *
+ * To do this, inquire which xacts currently would conflict with lockmode
+ * on the relation.
+ *
+ * Note: GetLockConflicts() never reports our own xid, hence we need not
+ * check for that.  Also, prepared xacts are not reported, which is fine
+ * since they certainly aren't going to do anything more.
+ */
+void
+WaitForMultipleVirtualLocks(List *locktags, LOCKMODE lockmode)
+{
+	VirtualTransactionId **old_lockholders;
+	int i, count = 0;
+	ListCell *lc;
+
+	/* Leave if no locks to wait for */
+	if (list_length(locktags) == 0)
+		return;
+
+	old_lockholders = (VirtualTransactionId **)
+		palloc(list_length(locktags) * sizeof(VirtualTransactionId *));
+
+	/* Collect the transactions we need to wait on for each relation lock */
+	foreach(lc, locktags)
+	{
+		LOCKTAG *locktag = lfirst(lc);
+		old_lockholders[count++] = GetLockConflicts(locktag, lockmode);
+	}
+
+	/* Finally wait for each transaction to complete */
+	for (i = 0; i < count; i++)
+	{
+		VirtualTransactionId *lockholders = old_lockholders[i];
+
+		while (VirtualTransactionIdIsValid(*lockholders))
+		{
+			VirtualXactLock(*lockholders, true);
+			lockholders++;
+		}
+	}
+
+	pfree(old_lockholders);
+}
+
+
+/*
+ * WaitForVirtualLocks
+ *
+ * Similar to WaitForMultipleVirtualLocks, but for a single lock.
+ */
+void
+WaitForVirtualLocks(LOCKTAG heaplocktag, LOCKMODE lockmode)
+{
+	WaitForMultipleVirtualLocks(list_make1(&heaplocktag), lockmode);
+}
+
+
+/*
+ * WaitForOldSnapshots
+ *
+ * Wait for transactions that might have older snapshot than the given one,
+ * because is might not contain tuples deleted just before it has been taken.
+ * Obtain a list of VXIDs of such transactions, and wait for them
+ * individually.
+ *
+ * We can exclude any running transactions that have xmin > the xmin of
+ * our reference snapshot; their oldest snapshot must be newer than ours.
+ * We can also exclude any transactions that have xmin = zero, since they
+ * evidently have no live snapshot at all (and any one they might be in
+ * process of taking is certainly newer than ours).  Transactions in other
+ * DBs can be ignored too, since they'll never even be able to see this
+ * index.
+ *
+ * We can also exclude autovacuum processes and processes running manual
+ * lazy VACUUMs, because they won't be fazed by missing index entries
+ * either. (Manual ANALYZEs, however, can't be excluded because they
+ * might be within transactions that are going to do arbitrary operations
+ * later.)
+ *
+ * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
+ * check for that.
+ *
+ * If a process goes idle-in-transaction with xmin zero, we do not need to
+ * wait for it anymore, per the above argument.  We do not have the
+ * infrastructure right now to stop waiting if that happens, but we can at
+ * least avoid the folly of waiting when it is idle at the time we would
+ * begin to wait.  We do this by repeatedly rechecking the output of
+ * GetCurrentVirtualXIDs.  If, during any iteration, a particular vxid
+ * doesn't show up in the output, we know we can forget about it.
+ */
+void
+WaitForOldSnapshots(Snapshot snapshot)
+{
+	int i, n_old_snapshots;
+	VirtualTransactionId *old_snapshots;
+
+	old_snapshots = GetCurrentVirtualXIDs(snapshot->xmin, true, false,
+										  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
+										  &n_old_snapshots);
+
+	for (i = 0; i < n_old_snapshots; i++)
+	{
+		if (!VirtualTransactionIdIsValid(old_snapshots[i]))
+			continue; /* found uninteresting in previous cycle */
+
+		if (i > 0)
+		{
+			/* see if anything's changed ... */
+			VirtualTransactionId *newer_snapshots;
+			int n_newer_snapshots, j, k;
+
+			newer_snapshots = GetCurrentVirtualXIDs(snapshot->xmin,
+													true, false,
+													PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
+													&n_newer_snapshots);
+			for (j = i; j < n_old_snapshots; j++)
+			{
+				if (!VirtualTransactionIdIsValid(old_snapshots[j]))
+					continue; /* found uninteresting in previous cycle */
+				for (k = 0; k < n_newer_snapshots; k++)
+				{
+					if (VirtualTransactionIdEquals(old_snapshots[j],
+												   newer_snapshots[k]))
+					                                              break;
+				}
+				if (k >= n_newer_snapshots) /* not there anymore */
+					SetInvalidVirtualTransactionId(old_snapshots[j]);
+			}
+			pfree(newer_snapshots);
+		}
+
+		if (VirtualTransactionIdIsValid(old_snapshots[i]))
+			VirtualXactLock(old_snapshots[i], true);
+	}
+}
+
+
 #ifdef XIDCACHE_DEBUG
 
 /*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a1c03f1..6a0341b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1292,16 +1292,20 @@ standard_ProcessUtility(Node *parsetree,
 			{
 				ReindexStmt *stmt = (ReindexStmt *) parsetree;
 
+				if (stmt->concurrent)
+					PreventTransactionChain(isTopLevel,
+											"REINDEX CONCURRENTLY");
+
 				/* we choose to allow this during "read only" transactions */
 				PreventCommandDuringRecovery("REINDEX");
 				switch (stmt->kind)
 				{
 					case OBJECT_INDEX:
-						ReindexIndex(stmt->relation);
+						ReindexIndex(stmt->relation, stmt->concurrent);
 						break;
 					case OBJECT_TABLE:
 					case OBJECT_MATVIEW:
-						ReindexTable(stmt->relation);
+						ReindexTable(stmt->relation, stmt->concurrent);
 						break;
 					case OBJECT_DATABASE:
 
@@ -1313,8 +1317,8 @@ standard_ProcessUtility(Node *parsetree,
 						 */
 						PreventTransactionChain(isTopLevel,
 												"REINDEX DATABASE");
-						ReindexDatabase(stmt->name,
-										stmt->do_system, stmt->do_user);
+						ReindexDatabase(stmt->name, stmt->do_system,
+										stmt->do_user, stmt->concurrent);
 						break;
 					default:
 						elog(ERROR, "unrecognized object type: %d",
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index d589d26..86ab62a 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -332,7 +332,7 @@ pg_relation_size(PG_FUNCTION_ARGS)
 }
 
 /*
- * Calculate total on-disk size of a TOAST relation, including its index.
+ * Calculate total on-disk size of a TOAST relation, including its indexes.
  * Must not be applied to non-TOAST relations.
  */
 static int64
@@ -340,8 +340,8 @@ calculate_toast_table_size(Oid toastrelid)
 {
 	int64		size = 0;
 	Relation	toastRel;
-	Relation	toastIdxRel;
 	ForkNumber	forkNum;
+	ListCell   *lc;
 
 	toastRel = relation_open(toastrelid, AccessShareLock);
 
@@ -351,12 +351,20 @@ 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);
+	RelationGetIndexList(toastRel);
 
-	relation_close(toastIdxRel, AccessShareLock);
+	/* Size is evaluated based using all the indexes available */
+	foreach(lc, toastRel->rd_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);
+	}
 	relation_close(toastRel, AccessShareLock);
 
 	return size;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 093be9e..843536f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -2677,16 +2677,17 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 	Oid			pg_class_reltoastidxid;
 
 	appendPQExpBuffer(upgrade_query,
-					  "SELECT c.reltoastrelid, t.reltoastidxid "
+					  "SELECT c.reltoastrelid, t.indexrelid "
 					  "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;",
+					  "pg_catalog.pg_index t ON (c.reltoastrelid = t.indrelid) "
+					  "WHERE c.oid = '%u'::pg_catalog.oid AND t.indisvalid "
+					  "LIMIT 1",
 					  pg_class_oid);
 
 	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")));
+	pg_class_reltoastidxid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid")));
 
 	appendPQExpBuffer(upgrade_buffer,
 				   "\n-- For binary upgrade, must preserve pg_class oids\n");
@@ -2712,7 +2713,7 @@ 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 */
+			/* every toast table has at least one valid index */
 			appendPQExpBuffer(upgrade_buffer,
 							  "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
 							  pg_class_reltoastidxid);
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index e697275..0693e3d 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -60,7 +60,28 @@ extern Oid index_create(Relation heapRelation,
 			 bool allow_system_table_mods,
 			 bool skip_build,
 			 bool concurrent,
-			 bool is_internal);
+			 bool is_internal,
+			 bool is_reindex);
+
+extern Oid index_concurrent_create(Relation heapRelation,
+								   Oid indOid,
+								   char *concurrentName);
+
+extern void index_concurrent_build(Oid heapOid,
+								   Oid indexOid,
+								   bool isprimary);
+
+extern void index_concurrent_swap(Oid newIndexOid, Oid oldIndexOid);
+
+extern void index_concurrent_set_dead(Oid indexId,
+									  Oid heapId,
+									  LOCKTAG locktag);
+
+extern void index_concurrent_clear_valid(Relation heapRelation,
+										 Oid indexOid,
+										 bool concurrent);
+
+extern void index_concurrent_drop(Oid indexOid);
 
 extern void index_constraint_create(Relation heapRelation,
 						Oid indexRelationId,
@@ -100,7 +121,9 @@ extern double IndexBuildHeapScan(Relation heapRelation,
 
 extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
 
-extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action);
+extern void index_set_state_flags(Oid indexId,
+								  IndexStateFlagsAction action,
+								  bool concurrent);
 
 extern void reindex_index(Oid indexId, bool skip_constraint_checks);
 
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index fd97141..ea46e38 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -48,7 +48,6 @@ 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 */
@@ -93,7 +92,7 @@ typedef FormData_pg_class *Form_pg_class;
  * ----------------
  */
 
-#define Natts_pg_class					28
+#define Natts_pg_class					27
 #define Anum_pg_class_relname			1
 #define Anum_pg_class_relnamespace		2
 #define Anum_pg_class_reltype			3
@@ -106,22 +105,21 @@ 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_relfrozenxid		25
-#define Anum_pg_class_relminmxid		26
-#define Anum_pg_class_relacl			27
-#define Anum_pg_class_reloptions		28
+#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_relfrozenxid		24
+#define Anum_pg_class_relminmxid		25
+#define Anum_pg_class_relacl			26
+#define Anum_pg_class_reloptions		27
 
 /* ----------------
  *		initial contents of pg_class
@@ -136,13 +134,13 @@ 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 3 1 _null_ _null_ ));
+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 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 3 1 _null_ _null_ ));
+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 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 3 1 _null_ _null_ ));
+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 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 28 0 t f f f f 3 1 _null_ _null_ ));
+DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 1 _null_ _null_ ));
 DESCR("");
 
 
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 62515b2..54137c6 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -26,10 +26,11 @@ extern Oid DefineIndex(IndexStmt *stmt,
 			bool check_rights,
 			bool skip_build,
 			bool quiet);
-extern Oid ReindexIndex(RangeVar *indexRelation);
-extern Oid ReindexTable(RangeVar *relation);
+extern Oid ReindexIndex(RangeVar *indexRelation, bool concurrent);
+extern Oid ReindexTable(RangeVar *relation, bool concurrent);
 extern Oid ReindexDatabase(const char *databaseName,
-				bool do_system, bool do_user);
+							bool do_system, bool do_user, bool concurrent);
+extern bool ReindexRelationConcurrently(Oid relOid);
 extern char *makeObjectName(const char *name1, const char *name2,
 			   const char *label);
 extern char *ChooseRelationName(const char *name1, const char *name2,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2229ef0..bb3ae47 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2538,6 +2538,7 @@ typedef struct ReindexStmt
 	const char *name;			/* name of database to reindex */
 	bool		do_system;		/* include system tables in database case */
 	bool		do_user;		/* include user tables in database case */
+	bool		concurrent;		/* reindex concurrently? */
 } ReindexStmt;
 
 /* ----------------------
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index d5fdfea..d4a0981 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -76,4 +76,8 @@ extern void XidCacheRemoveRunningXids(TransactionId xid,
 						  int nxids, const TransactionId *xids,
 						  TransactionId latestXid);
 
+extern void WaitForMultipleVirtualLocks(List *locktags, LOCKMODE lockmode);
+extern void WaitForVirtualLocks(LOCKTAG heaplocktag, LOCKMODE lockmode);
+extern void WaitForOldSnapshots(Snapshot snapshot);
+
 #endif   /* PROCARRAY_H */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 8ac2549..31309ed 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -29,6 +29,16 @@ typedef struct RelationData *Relation;
 typedef Relation *RelationPtr;
 
 /*
+ * RelationGetIndexListIfValid
+ * Get index list of relation without recomputing it.
+ */
+#define RelationGetIndexListIfValid(rel) \
+do { \
+	if (rel->rd_indexvalid == 0) \
+		RelationGetIndexList(rel); \
+} while(0)
+
+/*
  * Routines to open (lookup) and close a relcache entry
  */
 extern Relation RelationIdGetRelation(Oid relationId);
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 2ae991e..23fff1f 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2721,3 +2721,58 @@ ORDER BY thousand;
         1 |     1001
 (2 rows)
 
+--
+-- Check behavior of REINDEX and REINDEX CONCURRENTLY
+--
+CREATE TABLE concur_reindex_tab (c1 int);
+-- REINDEX
+REINDEX TABLE concur_reindex_tab; -- notice
+NOTICE:  table "concur_reindex_tab" has no indexes
+REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice
+NOTICE:  table "concur_reindex_tab" has no indexes
+ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index
+-- Normal index with integer column
+CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab(c1);
+-- Normal index with text column
+CREATE INDEX concur_reindex_ind2 ON concur_reindex_tab(c2);
+-- UNIQUE index with expression
+CREATE UNIQUE INDEX concur_reindex_ind3 ON concur_reindex_tab(abs(c1));
+-- Duplicate column names
+CREATE INDEX concur_reindex_ind4 ON concur_reindex_tab(c1, c1, c2);
+-- Create table for check on foreign key dependence switch with indexes swapped
+ALTER TABLE concur_reindex_tab ADD PRIMARY KEY USING INDEX concur_reindex_ind1;
+CREATE TABLE concur_reindex_tab2 (c1 int REFERENCES concur_reindex_tab);
+INSERT INTO concur_reindex_tab VALUES  (1, 'a');
+INSERT INTO concur_reindex_tab VALUES  (2, 'a');
+-- Check materialized views
+CREATE MATERIALIZED VIEW concur_reindex_matview AS SELECT * FROM concur_reindex_tab;
+REINDEX INDEX CONCURRENTLY concur_reindex_ind1;
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+REINDEX TABLE CONCURRENTLY concur_reindex_matview;
+-- Check errors
+-- Cannot run inside a transaction block
+BEGIN;
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+ERROR:  REINDEX CONCURRENTLY cannot run inside a transaction block
+COMMIT;
+REINDEX TABLE CONCURRENTLY pg_database; -- no shared relation
+ERROR:  concurrent reindex is not supported for shared relations
+REINDEX SYSTEM CONCURRENTLY postgres; -- not allowed for SYSTEM
+ERROR:  cannot reindex system concurrently
+-- Check the relation status, there should not be invalid indexes
+\d concur_reindex_tab
+Table "public.concur_reindex_tab"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ c1     | integer | not null
+ c2     | text    | 
+Indexes:
+    "concur_reindex_ind1" PRIMARY KEY, btree (c1)
+    "concur_reindex_ind3" UNIQUE, btree (abs(c1))
+    "concur_reindex_ind2" btree (c2)
+    "concur_reindex_ind4" btree (c1, c1, c2)
+Referenced by:
+    TABLE "concur_reindex_tab2" CONSTRAINT "concur_reindex_tab2_c1_fkey" FOREIGN KEY (c1) REFERENCES concur_reindex_tab(c1)
+
+DROP MATERIALIZED VIEW concur_reindex_matview;
+DROP TABLE concur_reindex_tab, concur_reindex_tab2;
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 06ed856..6c5cb5a 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -353,14 +353,6 @@ 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
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a4ecfd2..7a68fb9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1852,15 +1852,15 @@ 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                                                                                                                                                             +
+                                 |     (pg_stat_get_blocks_fetched(x.indrelid) - pg_stat_get_blocks_hit(x.indrelid)) AS tidx_blks_read,                                                                                                           +
+                                 |     pg_stat_get_blocks_hit(x.indrelid) 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_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.oid;
+                                 |   GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indrelid;
  pg_statio_sys_indexes           |  SELECT pg_statio_all_indexes.relid,                                                                                                                                                                           +
                                  |     pg_statio_all_indexes.indexrelid,                                                                                                                                                                          +
                                  |     pg_statio_all_indexes.schemaname,                                                                                                                                                                          +
@@ -2347,11 +2347,11 @@ 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
+select reltoastrelid, relkind, relfrozenxid
   from pg_class where oid = 'fooview'::regclass;
- reltoastrelid | reltoastidxid | relkind | relfrozenxid 
----------------+---------------+---------+--------------
-             0 |             0 | v       |            0
+ reltoastrelid | relkind | relfrozenxid 
+---------------+---------+--------------
+             0 | v       |            0
 (1 row)
 
 drop view fooview;
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 914e7a5..a338794 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -912,3 +912,43 @@ ORDER BY thousand;
 SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
 ORDER BY thousand;
+
+--
+-- Check behavior of REINDEX and REINDEX CONCURRENTLY
+--
+CREATE TABLE concur_reindex_tab (c1 int);
+-- REINDEX
+REINDEX TABLE concur_reindex_tab; -- notice
+REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice
+ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index
+-- Normal index with integer column
+CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab(c1);
+-- Normal index with text column
+CREATE INDEX concur_reindex_ind2 ON concur_reindex_tab(c2);
+-- UNIQUE index with expression
+CREATE UNIQUE INDEX concur_reindex_ind3 ON concur_reindex_tab(abs(c1));
+-- Duplicate column names
+CREATE INDEX concur_reindex_ind4 ON concur_reindex_tab(c1, c1, c2);
+-- Create table for check on foreign key dependence switch with indexes swapped
+ALTER TABLE concur_reindex_tab ADD PRIMARY KEY USING INDEX concur_reindex_ind1;
+CREATE TABLE concur_reindex_tab2 (c1 int REFERENCES concur_reindex_tab);
+INSERT INTO concur_reindex_tab VALUES  (1, 'a');
+INSERT INTO concur_reindex_tab VALUES  (2, 'a');
+-- Check materialized views
+CREATE MATERIALIZED VIEW concur_reindex_matview AS SELECT * FROM concur_reindex_tab;
+REINDEX INDEX CONCURRENTLY concur_reindex_ind1;
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+REINDEX TABLE CONCURRENTLY concur_reindex_matview;
+
+-- Check errors
+-- Cannot run inside a transaction block
+BEGIN;
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+COMMIT;
+REINDEX TABLE CONCURRENTLY pg_database; -- no shared relation
+REINDEX SYSTEM CONCURRENTLY postgres; -- not allowed for SYSTEM
+
+-- Check the relation status, there should not be invalid indexes
+\d concur_reindex_tab
+DROP MATERIALIZED VIEW concur_reindex_matview;
+DROP TABLE concur_reindex_tab, concur_reindex_tab2;
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index 6422da2..9b91683 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -177,10 +177,6 @@ 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
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 4f49a0d..2d24961 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -872,7 +872,7 @@ 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
+select reltoastrelid, relkind, relfrozenxid
   from pg_class where oid = 'fooview'::regclass;
 
 drop view fooview;
diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README
index b5c4d1b..e3e8a2a 100644
--- a/src/tools/findoidjoins/README
+++ b/src/tools/findoidjoins/README
@@ -86,7 +86,6 @@ 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
