PostgreSQL lock mode
SHARE lock in PostgreSQL
CREATE INDEX (without CONCURRENTLY) takes SHARE on the table: reads continue, but every INSERT, UPDATE and DELETE waits until the index is built and the transaction ends. Several SHARE holders can coexist, so two index builds can run at once.
Updated 9 October 2026
- Conflicts with
- ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE
What takes it
SHARE (ShareLock in pg_locks, with locktype = relation) protects a table’s data from
changing while still letting it be read. Its main user is CREATE INDEX without CONCURRENTLY,
which needs the rows to hold still while it reads them all:
BEGIN;
CREATE INDEX orders_customer_idx ON orders (customer_id);
SELECT relation::regclass AS relation, mode
FROM pg_locks
WHERE pid = pg_backend_pid()
AND locktype = 'relation'
AND relation <> 'pg_locks'::regclass
ORDER BY 1, 2;
ROLLBACK;
relation | mode
---------------------+---------------------
orders | ShareLock
orders_customer_idx | AccessExclusiveLock
(2 rows)
REINDEX takes it on the table too, with ACCESS EXCLUSIVE on the index being rebuilt. In practice
that also stops reads: the planner locks every index of the table while planning, so our test
SELECT by primary key waited during a REINDEX of another index.
Don’t confuse it with the ShareLock you see on a row of locktype = transactionid. That one
means a session is waiting for another transaction to finish, usually to update the same row; it
has nothing to do with this table lock.
What it blocks
SHARE conflicts with ROW EXCLUSIVE, so INSERT, UPDATE, DELETE,
MERGE and COPY … FROM all wait. On a large table, CREATE INDEX can take minutes, and writes
stop for all of that time. It also conflicts with SHARE UPDATE EXCLUSIVE
(VACUUM, ANALYZE), SHARE ROW EXCLUSIVE,
EXCLUSIVE and ACCESS EXCLUSIVE.
It doesn’t conflict with ACCESS SHARE or ROW SHARE, so
SELECT and SELECT … FOR UPDATE keep working. Nor does it conflict with itself: two sessions can
build two indexes on the same table at the same time.
On a live table, use CREATE INDEX CONCURRENTLY instead. It takes
SHARE UPDATE EXCLUSIVE, which lets writes continue, at the cost of
a slower build that can’t run inside a transaction block. See
CREATE INDEX and
CREATE INDEX CONCURRENTLY. Even a plain
CREATE INDEX has to wait for open write transactions first, and while it waits, every new write
queues behind it, so set a lock_timeout before running it.
See who holds it
Who holds or waits for SHARE on a table, and which sessions each one blocks:
SELECT l.relation::regclass AS table_name,
l.pid,
l.granted,
a.state,
date_trunc('second', now() - a.xact_start) AS open_for,
left(a.query, 40) AS query,
ARRAY(SELECT w.pid FROM pg_stat_activity w
WHERE l.pid = ANY (pg_blocking_pids(w.pid))
ORDER BY w.pid) AS blocking
FROM pg_locks l
JOIN pg_stat_activity a ON a.pid = l.pid
WHERE l.locktype = 'relation'
AND l.mode = 'ShareLock'
AND l.relation = 'orders'::regclass
ORDER BY l.granted DESC, a.query_start;
With two sessions each building an index in an open transaction, and an UPDATE waiting:
table_name | pid | granted | state | open_for | query | blocking
------------+--------+---------+---------------------+----------+------------------------------------------+----------
orders | 277304 | t | idle in transaction | 00:00:01 | CREATE INDEX orders_customer_idx ON orde | {277319}
orders | 277316 | t | idle in transaction | 00:00:00 | CREATE INDEX orders_total_id_idx ON orde | {277319}
(2 rows)
Both index builds hold SHARE at once, and the UPDATE (277319) is blocked by both. A build that
is still active can be stopped with SELECT pg_cancel_backend(<pid>);; the half-built index is
discarded with the transaction.
How we checked
On PostgreSQL 18.6, with two psql sessions on a test table. Session 1 held SHARE in an open
transaction; session 2 asked for each mode with a short lock_timeout:
-- session 1
BEGIN;
LOCK TABLE orders IN SHARE MODE;
-- session 2
SET lock_timeout = '150ms';
BEGIN;
LOCK TABLE orders IN ROW EXCLUSIVE MODE;
ROLLBACK;
ERROR: canceling statement due to lock timeout
| Mode requested in session 2 | Result (PostgreSQL 14, 15, 16, 17, 18) |
|---|---|
| ACCESS SHARE | Granted |
| ROW SHARE | Granted |
| ROW EXCLUSIVE | Waits |
| SHARE UPDATE EXCLUSIVE | Waits |
| SHARE | Granted |
| SHARE ROW EXCLUSIVE | Waits |
| EXCLUSIVE | Waits |
| ACCESS EXCLUSIVE | Waits |
CREATE INDEX and REINDEX we ran in a transaction and read from pg_locks; the two-index test
above ran both builds and the UPDATE in separate sessions at the same time.
In Inlet
Inlet’s Activity monitor shows sessions and the locks they hold, which query blocks which, and can
cancel a query or terminate a session. The structure editor shows the CREATE INDEX statement
before it runs.