diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 3a4e71ca1c..2d34e44fff 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -129,6 +129,11 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] . + + For temporary tables, CREATE INDEX is always + non-concurrent, as no other session can access them, and + non-concurrent index creation is cheaper. + diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml index 2a8ca5bf68..0aedd71bd6 100644 --- a/doc/src/sgml/ref/drop_index.sgml +++ b/doc/src/sgml/ref/drop_index.sgml @@ -58,6 +58,11 @@ DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] nameDROP INDEX CONCURRENTLY cannot. + + For temporary tables, DROP INDEX is always + non-concurrent, as no other session can access them, and + non-concurrent index drop is cheaper. + diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index bc20d7c750..bb7d981327 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1493,6 +1493,15 @@ index_drop(Oid indexId, bool concurrent) 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 diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 1faaaf625d..568a32bb4e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -335,6 +335,7 @@ DefineIndex(Oid relationId, bool skip_build, bool quiet) { + bool concurrent; char *indexRelationName; char *accessMethodName; Oid *typeObjectId; @@ -371,6 +372,18 @@ DefineIndex(Oid relationId, 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 key attributes in index */ @@ -413,7 +426,7 @@ DefineIndex(Oid relationId, * parallel workers under the control of certain particular ambuild * functions will need to be updated, too. */ - lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock; + lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock; rel = heap_open(relationId, lockmode); relationId = RelationGetRelid(rel); @@ -457,6 +470,12 @@ DefineIndex(Oid relationId, partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; if (partitioned) { + /* + * Note: we check 'stmt->concurrent' rather than 'concurrent', so that + * the error is thrown also for temporary tables. Seems better to be + * consistent, even though we could do it on temporary table because + * we're not actually doing it concurrently. + */ if (stmt->concurrent) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -645,8 +664,8 @@ DefineIndex(Oid relationId, 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; indexInfo->ii_ParallelWorkers = 0; indexInfo->ii_Am = accessMethodId; @@ -814,7 +833,7 @@ DefineIndex(Oid relationId, * 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. This @@ -825,11 +844,11 @@ DefineIndex(Oid relationId, flags = constr_flags = 0; if (stmt->isconstraint) flags |= INDEX_CREATE_ADD_CONSTRAINT; - if (skip_build || stmt->concurrent || partitioned) + if (skip_build || concurrent || partitioned) flags |= INDEX_CREATE_SKIP_BUILD; if (stmt->if_not_exists) flags |= INDEX_CREATE_IF_NOT_EXISTS; - if (stmt->concurrent) + if (concurrent) flags |= INDEX_CREATE_CONCURRENT; if (partitioned) flags |= INDEX_CREATE_PARTITIONED; @@ -1107,7 +1126,7 @@ DefineIndex(Oid relationId, return address; } - if (!stmt->concurrent) + if (!concurrent) { /* Close the heap and we're done, in the non-concurrent case */ heap_close(rel, NoLock); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a120a18f02..38386fb9cf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1106,7 +1106,11 @@ RemoveRelations(DropStmt *drop) /* 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) @@ -1197,6 +1201,18 @@ RemoveRelations(DropStmt *drop) 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; diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index e7d7c7aee6..c3deccca57 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -2571,6 +2571,31 @@ Indexes: "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 -- diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 283c4e6c18..668076e215 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -825,6 +825,31 @@ VACUUM FULL concur_heap; 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 --