— see <xref linkend="SQL-CREATEINDEX-CONCURRENTLY"
         endterm="SQL-CREATEINDEX-CONCURRENTLY-title">.
        </para>
+       <para>
+        For temporary tables, <command>CREATE INDEX</command> is always
+        non-concurrent, as no other session can access them, and
+        non-concurrent index creation is cheaper.
+       </para>
       </listitem>
      </varlistentry>
 
 
       performed within a transaction block, but
       <command>DROP INDEX CONCURRENTLY</> cannot.
      </para>
+     <para>
+      For temporary tables, <command>DROP INDEX</command> is always
+      non-concurrent, as no other session can access them, and
+      non-concurrent index drop is cheaper.
+     </para>
     </listitem>
    </varlistentry>
 
 
    LOCKTAG     heaplocktag;
    LOCKMODE    lockmode;
 
+   /*
+    * A temporary relation uses a non-concurrent DROP.  Other backends can't
+    * access a temporary relation, so there's no harm in grabbing a stronger
+    * lock (see comments in RemoveRelations), and a non-concurrent DROP is
+    * more efficient.
+    */
+   Assert(get_rel_persistence(indexId) != RELPERSISTENCE_TEMP ||
+          !concurrent);
+
    /*
     * To drop an index safely, we must grab exclusive lock on its parent
     * table.  Exclusive lock on the index alone is insufficient because
 
            bool skip_build,
            bool quiet)
 {
+   bool        concurrent;
    char       *indexRelationName;
    char       *accessMethodName;
    Oid        *typeObjectId;
    Snapshot    snapshot;
    int         i;
 
+   /*
+    * Force non-concurrent build on temporary relations, even if CONCURRENTLY
+    * was requested.  Other backends can't access a temporary relation, so
+    * there's no harm in grabbing a stronger lock, and a non-concurrent DROP
+    * is more efficient.  Do this before any use of the concurrent option is
+    * done.
+    */
+   if (stmt->concurrent && get_rel_persistence(relationId) != RELPERSISTENCE_TEMP)
+       concurrent = true;
+   else
+       concurrent = false;
+
    /*
     * count attributes in index
     */
     * relation.  To avoid lock upgrade hazards, that lock should be at least
     * as strong as the one we take here.
     */
-   lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock;
+   lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock;
    rel = heap_open(relationId, lockmode);
 
    relationId = RelationGetRelid(rel);
    indexInfo->ii_ExclusionStrats = NULL;
    indexInfo->ii_Unique = stmt->unique;
    /* In a concurrent build, mark it not-ready-for-inserts */
-   indexInfo->ii_ReadyForInserts = !stmt->concurrent;
-   indexInfo->ii_Concurrent = stmt->concurrent;
+   indexInfo->ii_ReadyForInserts = !concurrent;
+   indexInfo->ii_Concurrent = concurrent;
    indexInfo->ii_BrokenHotChain = false;
 
    typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
     * A valid stmt->oldNode implies that we already have a built form of the
     * index.  The caller should also decline any index build.
     */
-   Assert(!OidIsValid(stmt->oldNode) || (skip_build && !stmt->concurrent));
+   Assert(!OidIsValid(stmt->oldNode) || (skip_build && !concurrent));
 
    /*
     * Make the catalog entries for the index, including constraints. Then, if
                     coloptions, reloptions, stmt->primary,
                     stmt->isconstraint, stmt->deferrable, stmt->initdeferred,
                     allowSystemTableMods,
-                    skip_build || stmt->concurrent,
-                    stmt->concurrent, !check_rights,
+                    skip_build || concurrent,
+                    concurrent, !check_rights,
                     stmt->if_not_exists);
 
    ObjectAddressSet(address, RelationRelationId, indexRelationId);
        CreateComments(indexRelationId, RelationRelationId, 0,
                       stmt->idxcomment);
 
-   if (!stmt->concurrent)
+   if (!concurrent)
    {
        /* Close the heap and we're done, in the non-concurrent case */
        heap_close(rel, NoLock);
 
    /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    if (drop->concurrent)
    {
-       flags |= PERFORM_DELETION_CONCURRENTLY;
+       /*
+        * Note that for temporary relations this lock may get upgraded
+        * later on, but as no other session can access a temporary
+        * relation, this is actually fine.
+        */
        lockmode = ShareUpdateExclusiveLock;
        Assert(drop->removeType == OBJECT_INDEX);
        if (list_length(drop->objects) != 1)
            continue;
        }
 
+       /*
+        * Decide if concurrent mode needs to be used here or not.  The
+        * relation persistence cannot be known without its OID.
+        */
+       if (drop->concurrent &&
+           get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
+       {
+           Assert(list_length(drop->objects) == 1 &&
+                  drop->removeType == OBJECT_INDEX);
+           flags |= PERFORM_DELETION_CONCURRENTLY;
+       }
+
        /* OK, we're ready to delete this one */
        obj.classId = RelationRelationId;
        obj.objectId = relOid;
 
     "concur_index5" btree (f2) WHERE f1 = 'x'::text
     "std_index" btree (f2)
 
+-- Temporary tables with concurrent builds and on-commit actions
+-- CONCURRENTLY used with CREATE INDEX and DROP INDEX is ignored.
+-- PRESERVE ROWS, the default.
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT PRESERVE ROWS;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+DROP INDEX CONCURRENTLY concur_temp_ind;
+DROP TABLE concur_temp;
+-- ON COMMIT DROP
+BEGIN;
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT DROP;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+-- Fails when running in a transaction.
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+ERROR:  CREATE INDEX CONCURRENTLY cannot run inside a transaction block
+COMMIT;
+-- ON COMMIT DELETE ROWS
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT DELETE ROWS;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+DROP INDEX CONCURRENTLY concur_temp_ind;
+DROP TABLE concur_temp;
 --
 -- Try some concurrent index drops
 --
 
 REINDEX TABLE concur_heap;
 \d concur_heap
 
+-- Temporary tables with concurrent builds and on-commit actions
+-- CONCURRENTLY used with CREATE INDEX and DROP INDEX is ignored.
+-- PRESERVE ROWS, the default.
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT PRESERVE ROWS;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+DROP INDEX CONCURRENTLY concur_temp_ind;
+DROP TABLE concur_temp;
+-- ON COMMIT DROP
+BEGIN;
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT DROP;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+-- Fails when running in a transaction.
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+COMMIT;
+-- ON COMMIT DELETE ROWS
+CREATE TEMP TABLE concur_temp (f1 int, f2 text)
+  ON COMMIT DELETE ROWS;
+INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
+CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
+DROP INDEX CONCURRENTLY concur_temp_ind;
+DROP TABLE concur_temp;
+
 --
 -- Try some concurrent index drops
 --