mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Fix concurrent indexing operations with temporary tables
Attempting to use CREATE INDEX, DROP INDEX or REINDEX with CONCURRENTLY on a temporary relation with ON COMMIT actions triggered unexpected errors because those operations use multiple transactions internally to complete their work. Here is for example one confusing error when using ON COMMIT DELETE ROWS: ERROR: index "foo" already contains data Issues related to temporary relations and concurrent indexing are fixed in this commit by enforcing the non-concurrent path to be taken for temporary relations even if using CONCURRENTLY, transparently to the user. Using a non-concurrent path does not matter in practice as locks cannot be taken on a temporary relation by a session different than the one owning the relation, and the non-concurrent operation is more effective. The problem exists with REINDEX since v12 with the introduction of CONCURRENTLY, and with CREATE/DROP INDEX since CONCURRENTLY exists for those commands. In all supported versions, this caused only confusing error messages to be generated. Note that with REINDEX, it was also possible to issue a REINDEX CONCURRENTLY for a temporary relation owned by a different session, leading to a server crash. The idea to enforce transparently the non-concurrent code path for temporary relations comes originally from Andres Freund. Reported-by: Manuel Rigger Author: Michael Paquier, Heikki Linnakangas Reviewed-by: Andres Freund, Álvaro Herrera, Heikki Linnakangas Discussion: https://postgr.es/m/CA+u7OA6gP7YAeCguyseusYcc=uR8+ypjCcgDDCTzjQ+k6S9ksQ@mail.gmail.com Backpatch-through: 9.4
This commit is contained in:
@ -1435,6 +1435,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
|
||||
--
|
||||
@ -2405,6 +2430,55 @@ SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass);
|
||||
(1 row)
|
||||
|
||||
DROP TABLE concur_exprs_tab;
|
||||
-- Temporary tables and on-commit actions, where CONCURRENTLY is ignored.
|
||||
-- ON COMMIT PRESERVE ROWS, the default.
|
||||
CREATE TEMP TABLE concur_temp_tab_1 (c1 int, c2 text)
|
||||
ON COMMIT PRESERVE ROWS;
|
||||
INSERT INTO concur_temp_tab_1 VALUES (1, 'foo'), (2, 'bar');
|
||||
CREATE INDEX concur_temp_ind_1 ON concur_temp_tab_1(c2);
|
||||
REINDEX TABLE CONCURRENTLY concur_temp_tab_1;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
|
||||
-- Still fails in transaction blocks
|
||||
BEGIN;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
|
||||
ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block
|
||||
COMMIT;
|
||||
-- ON COMMIT DELETE ROWS
|
||||
CREATE TEMP TABLE concur_temp_tab_2 (c1 int, c2 text)
|
||||
ON COMMIT DELETE ROWS;
|
||||
CREATE INDEX concur_temp_ind_2 ON concur_temp_tab_2(c2);
|
||||
REINDEX TABLE CONCURRENTLY concur_temp_tab_2;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_2;
|
||||
-- ON COMMIT DROP
|
||||
BEGIN;
|
||||
CREATE TEMP TABLE concur_temp_tab_3 (c1 int, c2 text)
|
||||
ON COMMIT PRESERVE ROWS;
|
||||
INSERT INTO concur_temp_tab_3 VALUES (1, 'foo'), (2, 'bar');
|
||||
CREATE INDEX concur_temp_ind_3 ON concur_temp_tab_3(c2);
|
||||
-- Fails when running in a transaction
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_3;
|
||||
ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block
|
||||
COMMIT;
|
||||
-- REINDEX SCHEMA processes all temporary relations
|
||||
CREATE TABLE reindex_temp_before AS
|
||||
SELECT oid, relname, relfilenode, relkind, reltoastrelid
|
||||
FROM pg_class
|
||||
WHERE relname IN ('concur_temp_ind_1', 'concur_temp_ind_2');
|
||||
SELECT pg_my_temp_schema()::regnamespace as temp_schema_name \gset
|
||||
REINDEX SCHEMA CONCURRENTLY :temp_schema_name;
|
||||
SELECT b.relname,
|
||||
b.relkind,
|
||||
CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged'
|
||||
ELSE 'relfilenode has changed' END
|
||||
FROM reindex_temp_before b JOIN pg_class a ON b.oid = a.oid
|
||||
ORDER BY 1;
|
||||
relname | relkind | case
|
||||
-------------------+---------+-------------------------
|
||||
concur_temp_ind_1 | i | relfilenode has changed
|
||||
concur_temp_ind_2 | i | relfilenode has changed
|
||||
(2 rows)
|
||||
|
||||
DROP TABLE concur_temp_tab_1, concur_temp_tab_2, reindex_temp_before;
|
||||
--
|
||||
-- REINDEX SCHEMA
|
||||
--
|
||||
|
@ -501,6 +501,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
|
||||
--
|
||||
@ -966,6 +991,48 @@ SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass);
|
||||
SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass);
|
||||
DROP TABLE concur_exprs_tab;
|
||||
|
||||
-- Temporary tables and on-commit actions, where CONCURRENTLY is ignored.
|
||||
-- ON COMMIT PRESERVE ROWS, the default.
|
||||
CREATE TEMP TABLE concur_temp_tab_1 (c1 int, c2 text)
|
||||
ON COMMIT PRESERVE ROWS;
|
||||
INSERT INTO concur_temp_tab_1 VALUES (1, 'foo'), (2, 'bar');
|
||||
CREATE INDEX concur_temp_ind_1 ON concur_temp_tab_1(c2);
|
||||
REINDEX TABLE CONCURRENTLY concur_temp_tab_1;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
|
||||
-- Still fails in transaction blocks
|
||||
BEGIN;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
|
||||
COMMIT;
|
||||
-- ON COMMIT DELETE ROWS
|
||||
CREATE TEMP TABLE concur_temp_tab_2 (c1 int, c2 text)
|
||||
ON COMMIT DELETE ROWS;
|
||||
CREATE INDEX concur_temp_ind_2 ON concur_temp_tab_2(c2);
|
||||
REINDEX TABLE CONCURRENTLY concur_temp_tab_2;
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_2;
|
||||
-- ON COMMIT DROP
|
||||
BEGIN;
|
||||
CREATE TEMP TABLE concur_temp_tab_3 (c1 int, c2 text)
|
||||
ON COMMIT PRESERVE ROWS;
|
||||
INSERT INTO concur_temp_tab_3 VALUES (1, 'foo'), (2, 'bar');
|
||||
CREATE INDEX concur_temp_ind_3 ON concur_temp_tab_3(c2);
|
||||
-- Fails when running in a transaction
|
||||
REINDEX INDEX CONCURRENTLY concur_temp_ind_3;
|
||||
COMMIT;
|
||||
-- REINDEX SCHEMA processes all temporary relations
|
||||
CREATE TABLE reindex_temp_before AS
|
||||
SELECT oid, relname, relfilenode, relkind, reltoastrelid
|
||||
FROM pg_class
|
||||
WHERE relname IN ('concur_temp_ind_1', 'concur_temp_ind_2');
|
||||
SELECT pg_my_temp_schema()::regnamespace as temp_schema_name \gset
|
||||
REINDEX SCHEMA CONCURRENTLY :temp_schema_name;
|
||||
SELECT b.relname,
|
||||
b.relkind,
|
||||
CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged'
|
||||
ELSE 'relfilenode has changed' END
|
||||
FROM reindex_temp_before b JOIN pg_class a ON b.oid = a.oid
|
||||
ORDER BY 1;
|
||||
DROP TABLE concur_temp_tab_1, concur_temp_tab_2, reindex_temp_before;
|
||||
|
||||
--
|
||||
-- REINDEX SCHEMA
|
||||
--
|
||||
|
Reference in New Issue
Block a user