Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
** xref:v1.17/17.adoc[7、兼容Oracle函数与存储过程]
** xref:v1.17/18.adoc[8、内置数据类型与内置函数]
** xref:v1.17/19.adoc[9、新增Oracle兼容模式的端口与IP]
** xref:v1.17/41.adoc[10、全局唯一索引]
* IvorySQL试验田
** xref:v1.17/41.adoc[1、全局唯一索引]
** xref:v1.17/42.adoc[2、新增无主键表默认支持逻辑复制]
* xref:v1.17/20.adoc[社区贡献指南]
* xref:v1.17/21.adoc[工具参考]
* xref:v1.17/22.adoc[FAQ]
129 changes: 129 additions & 0 deletions CN/modules/ROOT/pages/v1.17/42.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
:sectnums:
:sectnumlevels: 5

:imagesdir: ./_images

= 新增无主键表默认支持逻辑复制

== 功能介绍

在 PostgreSQL/IvorySQL 的逻辑复制中,UPDATE 和 DELETE 操作需要依赖复制标识(Replica Identity)来定位订阅端的目标行。默认情况下,表使用 `REPLICA IDENTITY DEFAULT`,此时系统依赖主键(Primary Key)来定位行。如果表没有主键,复制策略将回退为 `REPLICA IDENTITY NOTHING`,此时 UPDATE 和 DELETE 操作将因无法定位行而报错。

IvorySQL 新增了 GUC 参数 `logical_replication_fallback_to_full_identity`。当该参数开启后,如果表的复制标识为 `DEFAULT` 且没有主键,系统会自动将其回退为 `REPLICA IDENTITY FULL`——即在 WAL 中记录完整的旧行数据,使无主键表的 UPDATE 和 DELETE 操作能够通过逻辑复制正常执行。

该功能仅在发布端(Publisher)生效,订阅端无需任何额外配置。

== 参数说明

[source,sql]
----
# postgresql.conf
logical_replication_fallback_to_full_identity = on
----

参数说明:

- 类型:`boolean`;
- 默认值:`off`;
- 作用域:`sighup`,可通过修改 `postgresql.conf` 后执行 `SELECT pg_reload_conf();` 使配置生效,无需重启数据库;
- 适用节点:仅在发布端(Publisher)生效,订阅端无需配置。

== 测试用例

=== 未开启参数时,无主键表的 UPDATE 和 DELETE 复制失败

[source,sql]
----
-- 发布端:创建无主键表
CREATE TABLE test_no_pk (id int, name text);

-- 订阅端:创建相同的表结构
CREATE TABLE test_no_pk (id int, name text);

-- 发布端:创建发布并添加表
CREATE PUBLICATION tap_pub FOR TABLE test_no_pk;

-- 订阅端:创建订阅
CREATE SUBSCRIPTION tap_sub CONNECTION 'host=publisher dbname=postgres' PUBLICATION tap_pub;

-- 发布端:INSERT 操作始终正常(不依赖复制标识)
INSERT INTO test_no_pk VALUES (1, 'alice');

-- 发布端:UPDATE 操作失败(无主键,无法定位目标行)
UPDATE test_no_pk SET name = 'bob' WHERE id = 1;
-- ERROR: cannot update table "test_no_pk" because it does not have a replica identity and publishes updates

-- 发布端:DELETE 操作同样失败
DELETE FROM test_no_pk WHERE id = 1;
-- ERROR: cannot delete from table "test_no_pk" because it does not have a replica identity and publishes deletes
----

=== 开启参数后,无主键表的 UPDATE 和 DELETE 复制正常

[source,sql]
----
-- 发布端:开启参数并重新加载配置
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = on;
SELECT pg_reload_conf();

-- 发布端:INSERT 正常
INSERT INTO test_no_pk VALUES (1, 'alice');

-- 发布端:UPDATE 正常(自动按 FULL 方式记录完整旧行数据)
UPDATE test_no_pk SET name = 'bob' WHERE id = 1;

-- 发布端:DELETE 正常
DELETE FROM test_no_pk WHERE id = 1;
----

=== 参数不影响有主键的表

[source,sql]
----
-- 有主键的表始终使用主键定位行,不受该参数影响
CREATE TABLE test_with_pk (id int PRIMARY KEY, name text);

-- 无论参数是否开启,UPDATE 和 DELETE 均正常工作
INSERT INTO test_with_pk VALUES (1, 'alice');
UPDATE test_with_pk SET name = 'bob' WHERE id = 1;
DELETE FROM test_with_pk WHERE id = 1;
----

=== 参数不影响显式设置为 REPLICA IDENTITY NOTHING 的表

[source,sql]
----
-- 创建表并显式设置为 REPLICA IDENTITY NOTHING
CREATE TABLE test_nothing (id int, data text);
ALTER TABLE test_nothing REPLICA IDENTITY NOTHING;

-- 即使开启了 logical_replication_fallback_to_full_identity,
-- UPDATE 和 DELETE 仍然失败(参数不会覆盖显式的 NOTHING 设置)
INSERT INTO test_nothing VALUES (1, 'test');
UPDATE test_nothing SET data = 'modified' WHERE id = 1;
-- ERROR: cannot update table "test_nothing" because it does not have a replica identity and publishes updates

DELETE FROM test_nothing WHERE id = 1;
-- ERROR: cannot delete from table "test_nothing" because it does not have a replica identity and publishes deletes
----

=== 运行时动态切换参数

[source,sql]
----
-- 关闭参数:无主键表的 UPDATE/DELETE 将恢复为报错行为
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = off;
SELECT pg_reload_conf();

-- 开启参数:无主键表的 UPDATE/DELETE 将正常工作
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = on;
SELECT pg_reload_conf();
----

== 功能限制

. 该参数仅对复制标识为 `REPLICA IDENTITY DEFAULT` 且没有主键的表生效;已显式设置为 `FULL`、`USING INDEX` 或 `NOTHING` 的表不受影响;
. 开启该参数后,无主键表的 UPDATE 和 DELETE 会在 WAL 中记录完整的旧行数据(与 `REPLICA IDENTITY FULL` 效果相同),相比有主键时只记录主键列,会增加 WAL 体积和网络传输量;
. 该参数仅在发布端生效,订阅端无需配置;
. INSERT 操作不依赖复制标识,无论该参数是否开启均可正常复制;
. 该参数不替代显式的 `ALTER TABLE ... REPLICA IDENTITY FULL` 设置,也不会覆盖显式设置为 `NOTHING` 的表。
4 changes: 3 additions & 1 deletion EN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
** xref:v1.17/17.adoc[7、Compatible with Oracle functions and stored procedures]
** xref:v1.17/18.adoc[8、Built-in data types and built-in functions]
** xref:v1.17/19.adoc[9、Added Oracle compatibility mode ports and IP]
** xref:v1.17/41.adoc[10、Global Unique Index]
* IvorySQL Experimental Features
** xref:v1.17/41.adoc[1、Global Unique Index]
** xref:v1.17/42.adoc[2、Default Logical Replication Support for Tables Without Primary Key]
* xref:v1.17/20.adoc[Community contribution]
* xref:v1.17/21.adoc[Tool Reference]
* xref:v1.17/22.adoc[FAQ]
129 changes: 129 additions & 0 deletions EN/modules/ROOT/pages/v1.17/42.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
:sectnums:
:sectnumlevels: 5

:imagesdir: ./_images

= Default Logical Replication Support for Tables Without Primary Key

== Overview

In PostgreSQL/IvorySQL logical replication, UPDATE and DELETE operations rely on the Replica Identity to locate target rows on the subscriber side. By default, tables use `REPLICA IDENTITY DEFAULT`, where the system relies on the Primary Key to locate rows. If a table has no primary key, the replication strategy falls back to `REPLICA IDENTITY NOTHING`, causing UPDATE and DELETE operations to fail because the target rows cannot be located.

IvorySQL introduces the GUC parameter `logical_replication_fallback_to_full_identity`. When this parameter is enabled, if a table's replica identity is `DEFAULT` and it has no primary key, the system automatically falls back to `REPLICA IDENTITY FULL` — recording the complete old row data in the WAL, allowing UPDATE and DELETE operations on tables without a primary key to work correctly through logical replication.

This feature only takes effect on the publisher side; no additional configuration is required on the subscriber side.

== Parameter Description

[source,sql]
----
# postgresql.conf
logical_replication_fallback_to_full_identity = on
----

Parameter description:

- Type: `boolean`;
- Default value: `off`;
- Scope: `sighup`; takes effect by modifying `postgresql.conf` and executing `SELECT pg_reload_conf();`, no database restart required;
- Applicable node: only takes effect on the publisher side; no configuration needed on the subscriber side.

== Test Cases

=== Without the Parameter Enabled, UPDATE and DELETE Replication Fails for Tables Without Primary Key

[source,sql]
----
-- Publisher: create a table without a primary key
CREATE TABLE test_no_pk (id int, name text);

-- Subscriber: create the same table structure
CREATE TABLE test_no_pk (id int, name text);

-- Publisher: create a publication and add the table
CREATE PUBLICATION tap_pub FOR TABLE test_no_pk;

-- Subscriber: create a subscription
CREATE SUBSCRIPTION tap_sub CONNECTION 'host=publisher dbname=postgres' PUBLICATION tap_pub;

-- Publisher: INSERT operations always work (do not depend on replica identity)
INSERT INTO test_no_pk VALUES (1, 'alice');

-- Publisher: UPDATE fails (no primary key, cannot locate the target row)
UPDATE test_no_pk SET name = 'bob' WHERE id = 1;
-- ERROR: cannot update table "test_no_pk" because it does not have a replica identity and publishes updates

-- Publisher: DELETE also fails
DELETE FROM test_no_pk WHERE id = 1;
-- ERROR: cannot delete from table "test_no_pk" because it does not have a replica identity and publishes deletes
----

=== With the Parameter Enabled, UPDATE and DELETE Replication Works for Tables Without Primary Key

[source,sql]
----
-- Publisher: enable the parameter and reload the configuration
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = on;
SELECT pg_reload_conf();

-- Publisher: INSERT works
INSERT INTO test_no_pk VALUES (1, 'alice');

-- Publisher: UPDATE works (automatically records the complete old row data in FULL mode)
UPDATE test_no_pk SET name = 'bob' WHERE id = 1;

-- Publisher: DELETE works
DELETE FROM test_no_pk WHERE id = 1;
----

=== The Parameter Does Not Affect Tables With a Primary Key

[source,sql]
----
-- Tables with a primary key always use the primary key to locate rows, regardless of this parameter
CREATE TABLE test_with_pk (id int PRIMARY KEY, name text);

-- Whether the parameter is enabled or not, UPDATE and DELETE work normally
INSERT INTO test_with_pk VALUES (1, 'alice');
UPDATE test_with_pk SET name = 'bob' WHERE id = 1;
DELETE FROM test_with_pk WHERE id = 1;
----

=== The Parameter Does Not Affect Tables Explicitly Set to REPLICA IDENTITY NOTHING

[source,sql]
----
-- Create a table and explicitly set it to REPLICA IDENTITY NOTHING
CREATE TABLE test_nothing (id int, data text);
ALTER TABLE test_nothing REPLICA IDENTITY NOTHING;

-- Even with logical_replication_fallback_to_full_identity enabled,
-- UPDATE and DELETE still fail (the parameter does not override an explicit NOTHING setting)
INSERT INTO test_nothing VALUES (1, 'test');
UPDATE test_nothing SET data = 'modified' WHERE id = 1;
-- ERROR: cannot update table "test_nothing" because it does not have a replica identity and publishes updates

DELETE FROM test_nothing WHERE id = 1;
-- ERROR: cannot delete from table "test_nothing" because it does not have a replica identity and publishes deletes
----

=== Dynamically Switching the Parameter at Runtime

[source,sql]
----
-- Disable the parameter: UPDATE/DELETE on tables without a primary key will revert to error behavior
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = off;
SELECT pg_reload_conf();

-- Enable the parameter: UPDATE/DELETE on tables without a primary key will work normally
ALTER SYSTEM SET logical_replication_fallback_to_full_identity = on;
SELECT pg_reload_conf();
----

== Limitations

. This parameter only takes effect for tables whose replica identity is `REPLICA IDENTITY DEFAULT` and that have no primary key; tables explicitly set to `FULL`, `USING INDEX`, or `NOTHING` are not affected;
. When this parameter is enabled, UPDATE and DELETE on tables without a primary key will record the complete old row data in the WAL (same effect as `REPLICA IDENTITY FULL`), which increases WAL size and network traffic compared to recording only primary key columns when a primary key exists;
. This parameter only takes effect on the publisher side; no configuration is needed on the subscriber side;
. INSERT operations do not depend on replica identity and can always be replicated normally, regardless of whether this parameter is enabled;
. This parameter does not replace an explicit `ALTER TABLE ... REPLICA IDENTITY FULL` setting, nor does it override tables explicitly set to `NOTHING`.
Loading