diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index 3a61e2d688932..34da42eacf539 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -150,57 +150,13 @@
snapshot once the transaction has committed.
-
- Replica Identity
-
-
- A published table must have a replica identity
- configured in order to be able to replicate UPDATE
- and DELETE operations, so that appropriate rows to
- update or delete can be identified on the subscriber side.
-
-
-
- By default, this is the primary key, if there is one. Another unique index
- (with certain additional requirements) can also be set to be the replica
- identity. If the table does not have any suitable key, then it can be set
- to replica identity FULL, which means the entire row
- becomes the key. When replica identity FULL is
- specified, indexes can be used on the subscriber side for searching the
- rows. Candidate indexes must be btree or hash, non-partial, and the
- leftmost index field must be a column (not an expression) that references
- the published table column. These restrictions on the non-unique index
- properties adhere to some of the restrictions that are enforced for
- primary keys. If there are no such suitable indexes, the search on the
- subscriber side can be very inefficient, therefore replica identity
- FULL should only be used as a fallback if no other
- solution is possible.
-
-
-
- If a replica identity other than FULL is set on the
- publisher side, a replica identity comprising the same or fewer columns
- must also be set on the subscriber side.
-
-
-
- Tables with a replica identity defined as NOTHING,
- DEFAULT without a primary key, or USING
- INDEX with a dropped index, cannot support
- UPDATE or DELETE operations when
- included in a publication replicating these actions. Attempting such
- operations will result in an error on the publisher.
-
-
-
- INSERT operations can proceed regardless of any replica identity.
-
-
-
- See ALTER TABLE...REPLICA IDENTITY
- for details on how to set the replica identity.
-
-
+
+ To be able to replicate UPDATE and
+ DELETE operations, a published table must have a
+ replica identity configured, so that the rows to be updated or deleted can
+ be identified on the subscriber side. See
+ for details.
+
@@ -846,6 +802,375 @@ HINT: To initiate replication, you must manually create the replication slot, e
+
+ Replica Identity
+
+
+ replica identity
+
+
+
+ Logical replication transfers changes row by row, so the apply process on
+ the subscriber has to determine which row of its copy of a published table
+ corresponds to the row that was updated or deleted on the publisher. The
+ replica identity of a table provides the information
+ needed for that: it determines which old column values are written to the
+ write-ahead log when a row is updated or deleted, so that those values can
+ be decoded later and sent to the subscriber together with the change.
+
+
+
+ Every table has a replica identity, which is recorded in
+ pg_class.relreplident,
+ but it only has an effect when the table's changes are decoded logically,
+ that is, when is set to
+ logical. Only UPDATE and
+ DELETE depend on it: INSERT
+ replicates the complete new row anyway, and TRUNCATE
+ does not refer to individual rows, so those operations can be replicated
+ regardless of the replica identity.
+
+
+
+ Replica Identity Modes
+
+
+ The replica identity of a table is set using
+ ALTER TABLE ... REPLICA IDENTITY,
+ which supports the four modes summarized in
+ .
+
+
+
+ Replica Identity Modes
+
+
+
+
+
+
+ Mode
+ Old values written to WAL
+ Notes
+
+
+
+
+ DEFAULT
+ the columns of the primary key
+
+ This is the default for user tables. If the table has no primary
+ key, the behavior is the same as NOTHING.
+
+
+
+ USING INDEX index_name
+ the columns covered by the named index
+
+ The index must be unique, must not be partial, must not be
+ deferrable, must not be an expression index, and must contain only
+ columns marked NOT NULL. If the index is dropped,
+ the behavior is the same as NOTHING.
+
+
+
+ FULL
+ all columns of the old row
+
+ A fallback for tables that have no suitable key; it is more expensive
+ on both sides, see
+ .
+
+
+
+ NOTHING
+ nothing
+
+ This is the default for system catalogs. UPDATE
+ and DELETE cannot be replicated.
+
+
+
+
+
+
+
+ With DEFAULT and USING INDEX, the
+ old values of the key columns are written for every
+ DELETE, but for UPDATE they are
+ only written if at least one key column was modified; however, if the old
+ value of a key column is stored externally (see
+ ), it is always written, whether or not it
+ changed. With FULL, the complete old row is written
+ for every UPDATE and DELETE.
+
+
+
+ Note that a table can be in a state where no old values are available at
+ all, namely when its replica identity is NOTHING,
+ DEFAULT without a primary key, or USING
+ INDEX with an index that has since been dropped. Such a table
+ cannot replicate UPDATE or DELETE;
+ see .
+
+
+
+
+ Setting and Examining the Replica Identity
+
+
+ The following example gives a table a replica identity based on a unique
+ index, because the table has no primary key:
+
+CREATE TABLE t (a int NOT NULL, b text);
+CREATE UNIQUE INDEX t_a_key ON t (a);
+ALTER TABLE t REPLICA IDENTITY USING INDEX t_a_key;
+
+
+
+ In psql, an index that is used as the replica
+ identity is marked as such in the table description, while the
+ FULL and NOTHING modes are reported
+ by \d+:
+
+\d t
+ Table "public.t"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | not null |
+ b | text | | |
+Indexes:
+ "t_a_key" UNIQUE, btree (a) REPLICA IDENTITY
+
+ The setting can also be queried directly from the system catalogs:
+
+SELECT relreplident FROM pg_class WHERE relname = 't';
+ relreplident
+--------------
+ i
+(1 row)
+
+
+
+ Changing the replica identity of a table requires an
+ ACCESS EXCLUSIVE lock on it, but it neither rewrites
+ the table nor affects data that has already been written to the
+ write-ahead log; it only changes what is logged for subsequent
+ operations. It is therefore best to set the replica identity of a table
+ before adding it to a publication.
+
+
+
+
+ Requirements on the Publisher
+
+
+ If a table is part of a publication that replicates
+ UPDATE or DELETE operations, and no
+ old values would be available for it, the operation fails on the
+ publisher:
+
+UPDATE t SET b = 'x';
+ERROR: cannot update table "t" because it does not have a replica identity and publishes updates
+HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
+
+ The error is raised when the operation is executed, not when the table is
+ added to the publication, so a table that is only ever inserted into can
+ be published without any replica identity at all.
+
+
+
+ In addition, the replica identity restricts which columns may be used by
+ other publication features, and it must itself be fully replicated:
+
+
+
+
+
+ A row filter of a publication that publishes
+ UPDATE or DELETE may only
+ reference columns covered by the replica identity. See
+ .
+
+
+
+
+
+ A column list of such a publication must include all columns of the
+ replica identity. See .
+
+
+
+
+
+ Generated columns that are part of the replica identity must be
+ published, that is, the publication must either set
+ publish_generated_columns
+ to stored or list those columns in its column list.
+ See .
+
+
+
+
+
+ Violations of these rules are likewise reported when an
+ UPDATE or DELETE is executed on the
+ published table.
+
+
+
+
+ Requirements on the Subscriber
+
+
+ The apply process uses the old values received from the publisher to
+ search for the local row to update or delete. The target table on the
+ subscriber therefore has to be searchable with exactly those values:
+
+
+
+
+
+ If the published table has a replica identity other than
+ FULL, the target table must have a primary key or a
+ replica identity index, and its columns must all be covered by the
+ replica identity of the published table. In other words, a replica
+ identity comprising the same or fewer columns must be set on the
+ subscriber side. Otherwise the apply process reports an error such as:
+
+ERROR: publisher did not send replica identity column expected by the logical replication target relation "public.t"
+
+
+
+
+
+
+ If the target table has neither a primary key nor a replica identity
+ index, the published table must have replica identity
+ FULL, so that the whole old row is available for the
+ search. Otherwise the apply process reports:
+
+ERROR: logical replication target relation "public.t" has neither REPLICA IDENTITY index nor PRIMARY KEY and published relation does not have REPLICA IDENTITY FULL
+
+
+
+
+
+
+ The search is performed with a primary key or replica identity index if
+ the target table has one. When the published table has replica identity
+ FULL and the target table has no such index, any other
+ suitable index may be used instead. Candidate indexes must be btree or
+ hash, non-partial, and the leftmost index field must be a column (not an
+ expression) that references the published table column. These
+ restrictions on the non-unique index properties adhere to some of the
+ restrictions that are enforced for primary keys. If there is no suitable
+ index, the search falls back to a sequential scan of the whole target
+ table for every change, which can be very slow.
+
+
+
+
+ A search that is not based on a unique index can match several rows, for
+ example when a table with replica identity FULL
+ contains duplicate rows. In that case one of the matching rows is
+ updated or deleted, and which one that is, is not determined.
+
+
+
+
+
+ Partitioned Tables
+
+
+ Rows are physically stored in leaf partitions, and that is also where the
+ old values are written to the write-ahead log. The replica identity of
+ each partition therefore determines whether an UPDATE or
+ DELETE can be replicated, also when the publication
+ parameter
+ publish_via_partition_root
+ is enabled.
+
+
+
+ Unlike most other ALTER TABLE forms,
+ ALTER TABLE ... REPLICA IDENTITY does not recurse to
+ the partitions of a partitioned table, and partitions created later do
+ not inherit the setting either. The replica identity has to be set on
+ each partition individually:
+
+CREATE TABLE p (a int NOT NULL, b text) PARTITION BY RANGE (a);
+CREATE TABLE p1 PARTITION OF p FOR VALUES FROM (0) TO (10);
+ALTER TABLE p REPLICA IDENTITY FULL;
+ALTER TABLE p1 REPLICA IDENTITY FULL;
+
+ Note that a primary key or a unique constraint defined on the partitioned
+ table does exist on every partition, so replica identity
+ DEFAULT and a partitioned primary key work without any
+ per-partition setup.
+
+
+
+ When publish_via_partition_root is enabled, changes
+ are reported using the identity and schema of the root table, and the
+ replica identity of that root table is announced to the subscriber. In
+ that case the root table and its partitions should be given matching
+ replica identities.
+
+
+
+
+ Using REPLICA IDENTITY FULL
+
+
+ Replica identity FULL makes the entire row the key, so
+ it works for any table. It comes at a price on both sides of the
+ replication setup, and should only be used as a fallback if no other
+ solution is possible:
+
+
+
+
+
+ On the publisher, every UPDATE and
+ DELETE writes a copy of the complete old row into
+ the write-ahead log, which increases the amount of WAL generated, and
+ with it the work of decoding and of sending the changes. Values that
+ are stored externally are written inline, so wide rows are especially
+ expensive.
+
+
+
+
+
+ On the subscriber, the search for the row to update or delete compares
+ all columns. Unless a suitable index exists, this requires a
+ sequential scan per change, as described in
+ .
+
+
+
+
+
+ UPDATE and DELETE cannot be
+ applied on the subscriber if the table includes attributes with data
+ types (such as point or box) that do not have
+ a default operator class for btree or hash, because the old and the new
+ row cannot be compared. Defining a primary key or another replica
+ identity for the table avoids this limitation.
+
+
+
+
+
+ So, in order of preference, use a primary key, or a unique index on
+ NOT NULL columns via USING INDEX,
+ and resort to FULL only where neither is available.
+
+
+
+
+
Row Filters
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index ff7071bef5b4d..7a2bf78e92cfd 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -986,6 +986,8 @@ WITH ( MODULUS numeric_literal, REM
from the new value; however, if the old value is stored externally, it is
always logged regardless of whether it changed.
This option has no effect except when logical replication is in use.
+ See for more
+ information about how the replica identity is used.
DEFAULT