From 8e3676e21684caa0e193d3ef38c4f0a4c0848e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=9D=E6=96=A4?= <51957749+JerseyYee@users.noreply.github.com> Date: Sat, 17 Oct 2026 20:07:10 +0800 Subject: [PATCH 1/5] docs: add guide for upgrading from PostgreSQL to IvorySQL --- CN/modules/ROOT/nav.adoc | 3 +- .../ROOT/pages/master/pg_upgrade_guide.adoc | 229 ++++++++++++++++++ EN/modules/ROOT/nav.adoc | 1 + .../ROOT/pages/master/pg_upgrade_guide.adoc | 229 ++++++++++++++++++ 4 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc create mode 100644 EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index 66bc54e6..97b5afa3 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -117,7 +117,8 @@ ** xref:master/getting-started/daily_maintenance.adoc[日常维护] ** xref:master/operation_guide.adoc[运维管理指南] * 数据迁移 -** xref:master/migration_guide.adoc[迁移指南] +** xref:master/migration_guide.adoc[迁移指南] +** xref:master/pg_upgrade_guide.adoc[PostgreSQL 升级指南] * IvorySQL开发者 ** xref:master/contribution/contribution_guide.adoc[贡献指南] ** xref:master/developer_guide.adoc[开发者指南] diff --git a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc new file mode 100644 index 00000000..72ef831e --- /dev/null +++ b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -0,0 +1,229 @@ +:sectnums: +:sectnumlevels: 5 + += PostgreSQL 至 IvorySQL 升级指南 + +== 概述 + +IvorySQL 基于 PostgreSQL 最新内核构建,并在此基础上提供 Oracle 兼容能力。对于已经在使用 PostgreSQL 的业务,可以按照本指南将数据平滑升级到 IvorySQL,并按需启用 Oracle 兼容模式。 + +本文介绍三种升级路径,并给出升级前后的检查清单: + +[cols="2,2,3,2"] +|=== +|路径 |适用场景 |优点 |限制 + +|pg_dump / pg_restore 逻辑迁移 +|跨版本、跨平台迁移,数据量中小 +|最安全,兼容性最好,支持版本跨度大 +|停机窗口取决于导出导入耗时 + +|pg_dumpall 全库迁移 +|整实例(含全局对象)一次性搬迁 +|保留角色、表空间等全局对象 +|同样需要停机窗口 + +|逻辑复制 +|追求最小停机时间的在线迁移 +|源库持续可用,可随时切换 +|需 PostgreSQL 10+,配置相对复杂 +|=== + +[IMPORTANT] +不推荐使用 `pg_upgrade` 直接从原生 PostgreSQL 升级到 IvorySQL。IvorySQL 在内核中加入了 Oracle 兼容相关的对象与配置,直接使用 `pg_upgrade` 无法保证系统目录中的兼容组件被正确初始化。请使用本指南中的逻辑迁移或逻辑复制路径。 + +== 升级前准备 + +=== 确认 IvorySQL 与 PostgreSQL 版本 + +IvorySQL 5.x 基于 PostgreSQL 18。可通过以下命令确认两端版本: + +[source,shell] +---- +# 源端 PostgreSQL +psql -U postgres -c "SELECT version();" + +# 目标端 IvorySQL +psql -U postgres -p 5432 -c "SELECT version();" +---- + +=== 安装 IvorySQL + +参考 xref:master/installation_guide.adoc[安装指南] 完成安装。初始化实例时,可按业务需要选择兼容模式与大小写不敏感(CI)选项: + +[source,shell] +---- +/usr/local/ivorysql/ivorysql-5/bin/initdb -D /data/ivorysql -U postgres +---- + +初始化完成后检查 `ivorysql.conf` 中的关键参数: + +[source,ini] +---- +ivorysql.compatible_mode = oracle +ivorysql.port = 1521 +port = 5432 +---- + +IvorySQL 默认同时监听 PostgreSQL 模式端口(5432)与 Oracle 兼容模式端口(1521),升级期间可先通过 5432 端口验证 PostgreSQL 行为,再逐步切换到 1521 端口使用 Oracle 兼容特性。 + +=== 源端检查清单 + +* 记录源库版本、`encoding`、`lc_collate`/`lc_ctype`,目标端 initdb 时保持一致。 +* 使用 `pg_dump --schema-only` 提前演练建表过程,标记依赖 Oracle 兼容特性才可创建的对象。 +* 检查是否存在使用 `EMPTY_STRING` 与 `NULL` 语义差异的数据:PostgreSQL 中空字符串 `''` 与 `NULL` 不同,而 IvorySQL 的 Oracle 兼容模式遵循 Oracle 语义,将空字符串视为 `NULL`。涉及此语义的查询结果会发生变化,需要业务侧评估。 +* 统计大表行数,作为迁移后的数据校验基准。 + +== 路径一:pg_dump / pg_restore 逻辑迁移 + +=== 导出源库 + +在源端 PostgreSQL 上导出指定数据库(建议使用与源库同大版本的 `pg_dump`): + +[source,shell] +---- +pg_dump -U postgres -Fc -d appdb -f appdb.dump +---- + +如需排除某些对象,可使用 `-N`/`-n` 与 `-T`/`-t` 选项精细控制。 + +=== 恢复到 IvorySQL + +将转储文件恢复到 IvorySQL 实例(通过 5432 端口,保持 PostgreSQL 行为): + +[source,shell] +---- +# 先创建目标数据库 +psql -U postgres -p 5432 -c "CREATE DATABASE appdb;" + +# 恢复数据 +pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges appdb.dump +---- + +`--no-owner` 与 `--no-privileges` 用于跳过源端的角色与授权信息;如目标端已重建同名角色,可去掉这两个选项。 + +=== 迁移全局对象 + +`pg_dump` 不包含角色与表空间。如目标端为全新实例,需要单独导出导入全局对象: + +[source,shell] +---- +# 源端导出全局对象 +pg_dumpall -U postgres --globals-only -f globals.sql + +# 目标端导入(先编辑去除超级用户与已存在对象) +psql -U postgres -p 5432 -f globals.sql +---- + +== 路径二:pg_dumpall 全库迁移 + +如果源实例规模不大,或需要连同全局对象一起搬迁,可直接使用 `pg_dumpall`: + +[source,shell] +---- +# 源端全库导出(纯 SQL 文本) +pg_dumpall -U postgres -f full_cluster.sql + +# 目标端导入 +psql -U postgres -p 5432 -f full_cluster.sql +---- + +[NOTE] +`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。 + +== 路径三:逻辑复制在线迁移 + +对于不允许长时间停机的业务,可以使用逻辑复制保持源库与 IvorySQL 的数据同步,最后短暂切换。 + +=== 源端配置 + +在源端 PostgreSQL 的 `postgresql.conf` 中调整参数并重启: + +[source,ini] +---- +wal_level = logical +max_replication_slots = 10 +max_wal_senders = 10 +---- + +确认 `pg_hba.conf` 允许 IvorySQL 所在主机以复制方式连接。 + +=== 目标端预建结构 + +在 IvorySQL 上预先创建与源端一致的表结构(可由 `pg_dump --schema-only` 生成),并确保目标端表已启用复制标识: + +[source,sql] +---- +-- 有主键的表无需额外操作 +-- 无主键的表需要设置复制标识 +ALTER TABLE app_table REPLICA IDENTITY FULL; +---- + +=== 创建发布与订阅 + +[source,sql] +---- +-- 源端创建发布 +CREATE PUBLICATION app_pub FOR ALL TABLES; + +-- IvorySQL 端创建订阅 +CREATE SUBSCRIPTION app_sub + CONNECTION 'host=src_host port=5432 dbname=appdb user=replica password=***' + PUBLICATION app_pub; +---- + +订阅创建后,IvorySQL 会先完成全量同步,随后持续应用增量变更。 + +=== 割接切换 + +1. 停止源端业务写入,等待订阅延迟归零(`pg_stat_subscription` 中 `latest_end_lsn` 追平)。 +2. 在 IvorySQL 端停止订阅:`ALTER SUBSCRIPTION app_sub DISABLE;` +3. 将业务连接串切换到 IvorySQL,按需使用 1521 端口启用 Oracle 兼容行为。 +4. 观察一段时间稳定后,删除订阅并下线源库。 + +== 升级后验证 + +=== 基础校验 + +[source,sql] +---- +-- 对比关键表行数(源端与目标端各执行一次) +SELECT count(*) FROM app_table; + +-- 检查序列当前值,避免割接后主键冲突 +SELECT last_value FROM app_id_seq; +---- + +逻辑复制不复制序列值,割接前必须手动同步序列: + +[source,shell] +---- +# 源端导出序列状态 +psql -U postgres -d appdb -Atc \ + "SELECT 'SELECT setval(' || quote_literal(quote_ident(sequencename)) || ',' || last_value || ');' \ + FROM pg_sequences WHERE schemaname = 'public'" > sync_sequences.sql + +# IvorySQL 端执行 +psql -U postgres -p 5432 -d appdb -f sync_sequences.sql +---- + +=== 启用 Oracle 兼容模式 + +数据校验通过后,通过 1521 端口连接验证 Oracle 兼容行为: + +[source,sql] +---- +-- 确认兼容模式 +SHOW ivorysql.compatible_mode; + +-- 验证 Oracle 风格对象 +SELECT SYSDATE FROM DUAL; +SELECT NVL(NULL, 'N/A') FROM DUAL; +---- + +== 常见问题 + +* **空字符串语义差异**:Oracle 兼容模式下 `'' = NULL`。若业务依赖 PostgreSQL 中空字符串与 NULL 的区别,需改造相关查询或数据。 +* **标识符大小写**:Oracle 兼容模式遵循 Oracle 的大小写折叠规则,未加双引号的标识符视为大写。从 PostgreSQL 迁移的小写对象名在 Oracle 模式下仍可通过双引号精确访问,建议在切换前统一评估命名规范。 +* **扩展兼容性**:源端使用的 PostgreSQL 扩展需确认 IvorySQL 是否已适配,适配列表见 xref:master/ecosystem_components/ecosystem_overview.adoc[生态组件适配]。 +* **只读对象**:`--no-owner` 恢复后对象属主为执行恢复的用户,如需保持原有权限模型,请在目标端提前创建角色。 diff --git a/EN/modules/ROOT/nav.adoc b/EN/modules/ROOT/nav.adoc index b2a82c50..acaacaef 100644 --- a/EN/modules/ROOT/nav.adoc +++ b/EN/modules/ROOT/nav.adoc @@ -117,6 +117,7 @@ ** xref:master/operation_guide.adoc[Operation Management] * Data Migration ** xref:master/migration_guide.adoc[Migration] +** xref:master/pg_upgrade_guide.adoc[Upgrading from PostgreSQL] * IvorySQL Developers ** xref:master/contribution/contribution_guide.adoc[Contribution Guide] ** xref:master/developer_guide.adoc[Developer] diff --git a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc new file mode 100644 index 00000000..fa34a750 --- /dev/null +++ b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -0,0 +1,229 @@ +:sectnums: +:sectnumlevels: 5 + += Upgrading from PostgreSQL to IvorySQL + +== Overview + +IvorySQL is built on the latest PostgreSQL kernel and adds Oracle compatibility on top of it. If you are already running PostgreSQL, this guide helps you upgrade your data to IvorySQL smoothly and enable Oracle-compatible mode as needed. + +This guide describes three upgrade paths and provides pre/post-migration checklists: + +[cols="2,2,3,2"] +|=== +|Path |Use case |Advantages |Limitations + +|pg_dump / pg_restore logical migration +|Cross-version, cross-platform migration with small to medium data volumes +|Safest, best compatibility, supports large version gaps +|Downtime depends on export/import duration + +|pg_dumpall full-cluster migration +|Moving an entire instance (including global objects) at once +|Preserves roles, tablespaces, and other global objects +|Also requires a downtime window + +|Logical replication +|Online migration with minimal downtime +|Source stays available; switch at any time +|Requires PostgreSQL 10+, more complex configuration +|=== + +[IMPORTANT] +Do not use `pg_upgrade` to upgrade directly from vanilla PostgreSQL to IvorySQL. IvorySQL adds Oracle-compatibility objects and configuration inside the kernel, and `pg_upgrade` cannot guarantee that these compatibility components are initialized correctly in the system catalogs. Use the logical migration or logical replication paths described in this guide. + +== Pre-upgrade preparation + +=== Confirm IvorySQL and PostgreSQL versions + +IvorySQL 5.x is based on PostgreSQL 18. Confirm both versions: + +[source,shell] +---- +# Source PostgreSQL +psql -U postgres -c "SELECT version();" + +# Target IvorySQL +psql -U postgres -p 5432 -c "SELECT version();" +---- + +=== Install IvorySQL + +Install IvorySQL by following the xref:master/installation_guide.adoc[installation guide]. When initializing the instance, choose the compatibility mode and case-insensitive (CI) options according to your business needs: + +[source,shell] +---- +/usr/local/ivorysql/ivorysql-5/bin/initdb -D /data/ivorysql -U postgres +---- + +After initialization, check the key parameters in `ivorysql.conf`: + +[source,ini] +---- +ivorysql.compatible_mode = oracle +ivorysql.port = 1521 +port = 5432 +---- + +IvorySQL listens on both the PostgreSQL-mode port (5432) and the Oracle-compatible-mode port (1521) by default. During the upgrade you can verify PostgreSQL behavior through port 5432 first, then switch to port 1521 to use Oracle compatibility features. + +=== Source-side checklist + +* Record the source version, `encoding`, and `lc_collate`/`lc_ctype`, and keep them consistent when running initdb on the target. +* Rehearse table creation in advance with `pg_dump --schema-only` and flag any objects that require Oracle compatibility features. +* Check whether your data relies on the difference between empty strings and `NULL`: in PostgreSQL, `''` and `NULL` are distinct, while IvorySQL's Oracle-compatible mode follows Oracle semantics and treats empty strings as `NULL`. Query results that depend on this semantic change and need business-side evaluation. +* Capture row counts of large tables as the baseline for post-migration validation. + +== Path 1: pg_dump / pg_restore logical migration + +=== Export the source database + +Dump the database on the source PostgreSQL (prefer a `pg_dump` of the same major version as the source): + +[source,shell] +---- +pg_dump -U postgres -Fc -d appdb -f appdb.dump +---- + +Use the `-N`/`-n` and `-T`/`-t` options to exclude specific schemas or tables. + +=== Restore into IvorySQL + +Restore the dump into the IvorySQL instance (via port 5432 to keep PostgreSQL behavior): + +[source,shell] +---- +# Create the target database first +psql -U postgres -p 5432 -c "CREATE DATABASE appdb;" + +# Restore the data +pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges appdb.dump +---- + +`--no-owner` and `--no-privileges` skip source-side ownership and grants. If you recreated the same roles on the target, you can drop these options. + +=== Migrate global objects + +`pg_dump` does not include roles or tablespaces. For a fresh target instance, migrate global objects separately: + +[source,shell] +---- +# Export globals from the source +pg_dumpall -U postgres --globals-only -f globals.sql + +# Import on the target (edit out superusers and pre-existing objects first) +psql -U postgres -p 5432 -f globals.sql +---- + +== Path 2: pg_dumpall full-cluster migration + +If the source instance is small, or you want to move global objects together with the data, use `pg_dumpall` directly: + +[source,shell] +---- +# Full-cluster export as plain SQL +pg_dumpall -U postgres -f full_cluster.sql + +# Import on the target +psql -U postgres -p 5432 -f full_cluster.sql +---- + +[NOTE] +`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. + +== Path 3: logical replication online migration + +For businesses that cannot tolerate long downtime, use logical replication to keep the source and IvorySQL in sync, then cut over within a short window. + +=== Configure the source + +Adjust `postgresql.conf` on the source PostgreSQL and restart: + +[source,ini] +---- +wal_level = logical +max_replication_slots = 10 +max_wal_senders = 10 +---- + +Make sure `pg_hba.conf` allows the IvorySQL host to connect for replication. + +=== Pre-create the structure on the target + +Create the same table structures on IvorySQL in advance (you can generate them with `pg_dump --schema-only`) and ensure the target tables have a replica identity: + +[source,sql] +---- +-- Tables with primary keys need no extra step +-- Tables without primary keys require a replica identity +ALTER TABLE app_table REPLICA IDENTITY FULL; +---- + +=== Create publication and subscription + +[source,sql] +---- +-- Create a publication on the source +CREATE PUBLICATION app_pub FOR ALL TABLES; + +-- Create a subscription on IvorySQL +CREATE SUBSCRIPTION app_sub + CONNECTION 'host=src_host port=5432 dbname=appdb user=replica password=***' + PUBLICATION app_pub; +---- + +After the subscription is created, IvorySQL performs the initial sync and then continuously applies incremental changes. + +=== Cutover + +1. Stop writes on the source and wait until the subscription lag reaches zero (check `pg_stat_subscription` for `latest_end_lsn`). +2. Disable the subscription on IvorySQL: `ALTER SUBSCRIPTION app_sub DISABLE;` +3. Point the application connection string to IvorySQL, using port 1521 if Oracle-compatible behavior is desired. +4. After a stability observation period, drop the subscription and decommission the source. + +== Post-upgrade validation + +=== Basic checks + +[source,sql] +---- +-- Compare row counts of key tables (run on both source and target) +SELECT count(*) FROM app_table; + +-- Check sequence values to avoid primary-key conflicts after cutover +SELECT last_value FROM app_id_seq; +---- + +Logical replication does not replicate sequence values. Synchronize sequences manually before cutover: + +[source,shell] +---- +# Export sequence state from the source +psql -U postgres -d appdb -Atc \ + "SELECT 'SELECT setval(' || quote_literal(quote_ident(sequencename)) || ',' || last_value || ');' \ + FROM pg_sequences WHERE schemaname = 'public'" > sync_sequences.sql + +# Execute on IvorySQL +psql -U postgres -p 5432 -d appdb -f sync_sequences.sql +---- + +=== Enable Oracle-compatible mode + +After validation, connect through port 1521 and verify Oracle-compatible behavior: + +[source,sql] +---- +-- Confirm the compatibility mode +SHOW ivorysql.compatible_mode; + +-- Verify Oracle-style objects +SELECT SYSDATE FROM DUAL; +SELECT NVL(NULL, 'N/A') FROM DUAL; +---- + +== FAQ + +* **Empty-string semantics**: in Oracle-compatible mode `''` equals `NULL`. If your business depends on the PostgreSQL distinction between empty strings and NULL, adjust the related queries or data. +* **Identifier casing**: Oracle-compatible mode follows Oracle's case-folding rules; unquoted identifiers are treated as uppercase. Lowercase object names migrated from PostgreSQL remain accessible with double quotes, but review naming conventions before switching. +* **Extension compatibility**: confirm that PostgreSQL extensions used on the source are adapted for IvorySQL. See the xref:master/ecosystem_components/ecosystem_overview.adoc[ecosystem component list]. +* **Ownership**: after restoring with `--no-owner`, objects belong to the user who ran the restore. Create the roles on the target in advance to preserve the original permission model. From 6a34e92dde1fba1a0d197824818a4c009eee1e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=9D=E6=96=A4?= <51957749+JerseyYee@users.noreply.github.com> Date: Mon, 19 Oct 2026 20:12:57 +0800 Subject: [PATCH 2/5] docs: address review feedback on pg_upgrade guide (CN/EN) - Remove ivorysql.compatible_mode from ivorysql.conf examples (GUC_DISALLOW_IN_FILE); document that compatibility mode is determined by the connection port - Move global objects/tablespaces migration before data restore and add --no-tablespaces guidance - Require stopping source writes before final dump for offline paths - Fix replica identity guidance: set REPLICA IDENTITY FULL on the source for PK-less tables, add detection query - Expand cutover checks: pg_subscription_rel srsubstate='r' plus LSN comparison and data validation - Sequence sync script now covers all non-system schemas with qualified names --- .../ROOT/pages/master/pg_upgrade_guide.adoc | 127 ++++++++++++----- .../ROOT/pages/master/pg_upgrade_guide.adoc | 128 +++++++++++++----- 2 files changed, 190 insertions(+), 65 deletions(-) diff --git a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index 72ef831e..1429f1cf 100644 --- a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -56,16 +56,15 @@ psql -U postgres -p 5432 -c "SELECT version();" /usr/local/ivorysql/ivorysql-5/bin/initdb -D /data/ivorysql -U postgres ---- -初始化完成后检查 `ivorysql.conf` 中的关键参数: +初始化完成后,确认双端口监听与各自的兼容模式: -[source,ini] +[source,shell] ---- -ivorysql.compatible_mode = oracle -ivorysql.port = 1521 -port = 5432 +psql -U postgres -p 5432 -c "SHOW ivorysql.compatible_mode;" +psql -U postgres -p 1521 -c "SHOW ivorysql.compatible_mode;" ---- -IvorySQL 默认同时监听 PostgreSQL 模式端口(5432)与 Oracle 兼容模式端口(1521),升级期间可先通过 5432 端口验证 PostgreSQL 行为,再逐步切换到 1521 端口使用 Oracle 兼容特性。 +IvorySQL 同一实例同时监听 PostgreSQL 模式端口(5432)与 Oracle 兼容模式端口(1521)。兼容行为由**连接端口**决定:`ivorysql.compatible_mode` 由系统根据端口自动设置,不允许在 `ivorysql.conf` 中手工配置。升级与校验阶段先通过 5432 端口验证 PostgreSQL 行为,割接后再切换到 1521 端口使用 Oracle 兼容特性。 === 源端检查清单 @@ -76,8 +75,28 @@ IvorySQL 默认同时监听 PostgreSQL 模式端口(5432)与 Oracle 兼容 == 路径一:pg_dump / pg_restore 逻辑迁移 +=== 迁移全局对象与表空间(先于数据恢复) + +`pg_dump` 不包含角色与表空间,目标端必须先就绪这些全局对象,再恢复数据:使用自定义表空间的表在恢复时要求对应表空间已存在,否则 `pg_restore` 直接失败。 + +[source,shell] +---- +# 源端导出全局对象 +pg_dumpall -U postgres --globals-only -f globals.sql + +# 目标端导入前先编辑 globals.sql: +# 1) 去除超级用户相关语句 +# 2) 核对 CREATE TABLESPACE 语句中的目录路径,并提前在 IvorySQL 主机上创建这些目录 +psql -U postgres -p 5432 -f globals.sql +---- + +若计划将所有表恢复到默认表空间(不保留源端表空间布局),可跳过表空间相关检查,并在下一步恢复时使用 `--no-tablespaces`。 + === 导出源库 +[IMPORTANT] +逻辑导出基于某一时刻的一致性快照。执行最终导出前必须**停止源端业务写入**,并保持停止直到数据校验完成、业务切换到 IvorySQL;否则导出快照之后的写入不会包含在迁移数据中。 + 在源端 PostgreSQL 上导出指定数据库(建议使用与源库同大版本的 `pg_dump`): [source,shell] @@ -96,24 +115,11 @@ pg_dump -U postgres -Fc -d appdb -f appdb.dump # 先创建目标数据库 psql -U postgres -p 5432 -c "CREATE DATABASE appdb;" -# 恢复数据 -pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges appdb.dump +# 恢复数据(默认将所有对象放入默认表空间) +pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges --no-tablespaces appdb.dump ---- -`--no-owner` 与 `--no-privileges` 用于跳过源端的角色与授权信息;如目标端已重建同名角色,可去掉这两个选项。 - -=== 迁移全局对象 - -`pg_dump` 不包含角色与表空间。如目标端为全新实例,需要单独导出导入全局对象: - -[source,shell] ----- -# 源端导出全局对象 -pg_dumpall -U postgres --globals-only -f globals.sql - -# 目标端导入(先编辑去除超级用户与已存在对象) -psql -U postgres -p 5432 -f globals.sql ----- +`--no-owner` 与 `--no-privileges` 用于跳过源端的角色与授权信息;如目标端已重建同名角色,可去掉这两个选项。`--no-tablespaces` 忽略转储中的表空间设置,将所有对象放入默认表空间;如需保留源端表空间布局,请确认前置步骤中的表空间已在目标端创建,再省略该选项。 == 路径二:pg_dumpall 全库迁移 @@ -128,8 +134,11 @@ pg_dumpall -U postgres -f full_cluster.sql psql -U postgres -p 5432 -f full_cluster.sql ---- +[IMPORTANT] +最终导出前必须停止源端业务写入,并保持停止直到割接完成;导出快照之后的写入不会包含在转储文件中。 + [NOTE] -`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。 +`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。转储文件开头的全局对象(角色、表空间)会先于各数据库执行;若源端使用了自定义表空间,请先在 IvorySQL 主机创建对应目录,否则 `CREATE TABLESPACE` 失败将中断整个恢复。 == 路径三:逻辑复制在线迁移 @@ -150,13 +159,29 @@ max_wal_senders = 10 === 目标端预建结构 -在 IvorySQL 上预先创建与源端一致的表结构(可由 `pg_dump --schema-only` 生成),并确保目标端表已启用复制标识: +在 IvorySQL 上预先创建与源端一致的表结构(可由 `pg_dump --schema-only` 生成)。建议目标端表与源端保持相同的复制标识设置,以提升增量应用的执行效率。 + +=== 源端设置复制标识 + +逻辑复制由 publisher 端按表的复制标识查找待复制的行:无主键的表默认没有复制标识,其 UPDATE/DELETE 无法发布。请在源端为所有无主键的待复制表设置复制标识: [source,sql] ---- --- 有主键的表无需额外操作 --- 无主键的表需要设置复制标识 +-- 有主键的表无需额外操作(主键即复制标识) +-- 无主键的表在源端设置 REPLICA IDENTITY FULL ALTER TABLE app_table REPLICA IDENTITY FULL; + +-- 检查源端缺少复制标识的用户表(relreplident = 'd' 且无主键) +SELECT n.nspname AS schema_name, c.relname AS table_name +FROM pg_class c +JOIN pg_namespace n ON n.oid = c.relnamespace +WHERE c.relkind = 'r' + AND c.relreplident = 'd' + AND NOT EXISTS ( + SELECT 1 FROM pg_index i + WHERE i.indrelid = c.oid AND i.indisprimary + ) + AND n.nspname NOT IN ('pg_catalog', 'information_schema'); ---- === 创建发布与订阅 @@ -176,10 +201,42 @@ CREATE SUBSCRIPTION app_sub === 割接切换 -1. 停止源端业务写入,等待订阅延迟归零(`pg_stat_subscription` 中 `latest_end_lsn` 追平)。 -2. 在 IvorySQL 端停止订阅:`ALTER SUBSCRIPTION app_sub DISABLE;` -3. 将业务连接串切换到 IvorySQL,按需使用 1521 端口启用 Oracle 兼容行为。 -4. 观察一段时间稳定后,删除订阅并下线源库。 +1. 停止源端业务写入,此后源库不再产生新的变更。 + +2. 确认初始同步完成。在 IvorySQL 端检查所有订阅表的状态均为 `r`(进入正常复制): + +[source,sql] +---- +-- IvorySQL 端执行 +SELECT srsubid::regsubscription AS subscription, + srrelid::regclass AS table_name, + srsubstate +FROM pg_subscription_rel +ORDER BY 1, 2; + +-- srsubstate:'d' 初始拷贝进行中;'s' 已同步数据; +-- 'r' 拷贝完成并进入正常复制(割接前所有表必须为 'r') +---- + +3. 确认停写后的最终增量已全部应用:在源端记录停写时的 WAL 位置,等待 IvorySQL 端的 `latest_end_lsn` 追平该位置。 + +[source,sql] +---- +-- 源端(停写后执行):记录当前 WAL 写入位置 +SELECT pg_current_wal_lsn(); + +-- IvorySQL 端:latest_end_lsn 应等于上面记录的位置 +SELECT subname, received_lsn, latest_end_lsn, latest_end_time +FROM pg_stat_subscription; +---- + +4. 对关键表执行行数与抽样校验(见"升级后验证")。 + +5. 校验通过后,在 IvorySQL 端停止订阅:`ALTER SUBSCRIPTION app_sub DISABLE;` + +6. 将业务连接串切换到 IvorySQL,按需使用 1521 端口启用 Oracle 兼容行为。 + +7. 观察一段时间稳定后,删除订阅并下线源库。 == 升级后验证 @@ -198,15 +255,19 @@ SELECT last_value FROM app_id_seq; [source,shell] ---- -# 源端导出序列状态 +# 源端导出序列状态(覆盖除系统 schema 外的所有 schema,生成 schema 限定的序列名) psql -U postgres -d appdb -Atc \ - "SELECT 'SELECT setval(' || quote_literal(quote_ident(sequencename)) || ',' || last_value || ');' \ - FROM pg_sequences WHERE schemaname = 'public'" > sync_sequences.sql + "SELECT 'SELECT setval(' || quote_literal(format('%I.%I', schemaname, sequencename)) || ',' || last_value || ');' \ + FROM pg_sequences \ + WHERE last_value IS NOT NULL \ + AND schemaname NOT IN ('pg_catalog', 'information_schema')" > sync_sequences.sql # IvorySQL 端执行 psql -U postgres -p 5432 -d appdb -f sync_sequences.sql ---- +脚本说明:`format('%I.%I', ...)` 生成带引号限定的完整序列名,多 schema 场景同样适用;`last_value IS NOT NULL` 过滤掉尚未使用的序列(其当前值即起始值,无需同步)。 + === 启用 Oracle 兼容模式 数据校验通过后,通过 1521 端口连接验证 Oracle 兼容行为: diff --git a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index fa34a750..d5f80fdb 100644 --- a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -56,16 +56,15 @@ Install IvorySQL by following the xref:master/installation_guide.adoc[installati /usr/local/ivorysql/ivorysql-5/bin/initdb -D /data/ivorysql -U postgres ---- -After initialization, check the key parameters in `ivorysql.conf`: +After initialization, verify the dual-port listeners and their compatibility modes: -[source,ini] +[source,shell] ---- -ivorysql.compatible_mode = oracle -ivorysql.port = 1521 -port = 5432 +psql -U postgres -p 5432 -c "SHOW ivorysql.compatible_mode;" +psql -U postgres -p 1521 -c "SHOW ivorysql.compatible_mode;" ---- -IvorySQL listens on both the PostgreSQL-mode port (5432) and the Oracle-compatible-mode port (1521) by default. During the upgrade you can verify PostgreSQL behavior through port 5432 first, then switch to port 1521 to use Oracle compatibility features. +IvorySQL listens on both the PostgreSQL-mode port (5432) and the Oracle-compatible-mode port (1521) in a single instance. Compatibility behavior is determined by the **connection port**: `ivorysql.compatible_mode` is set automatically by the system according to the port and cannot be configured manually in `ivorysql.conf`. During the upgrade you can verify PostgreSQL behavior through port 5432 first, then switch to port 1521 to use Oracle compatibility features after cutover. === Source-side checklist @@ -76,8 +75,29 @@ IvorySQL listens on both the PostgreSQL-mode port (5432) and the Oracle-compatib == Path 1: pg_dump / pg_restore logical migration +=== Migrate global objects and tablespaces (before data restore) + +`pg_dump` does not include roles or tablespaces. The target must have these global objects ready **before** restoring data: tables that use custom tablespaces fail during restore unless the corresponding tablespaces already exist. + +[source,shell] +---- +# Export globals from the source +pg_dumpall -U postgres --globals-only -f globals.sql + +# Before importing on the target, edit globals.sql: +# 1) Remove superuser-related statements +# 2) Verify the directory paths in CREATE TABLESPACE statements +# and create these directories on the IvorySQL host in advance +psql -U postgres -p 5432 -f globals.sql +---- + +If you plan to restore all tables into the default tablespace (without preserving the source tablespace layout), you can skip the tablespace checks and use `--no-tablespaces` in the restore step below. + === Export the source database +[IMPORTANT] +Logical dumps are based on a consistent snapshot taken at a point in time. You must **stop writes on the source** before the final export and keep them stopped until validation completes and the business switches to IvorySQL; otherwise writes made after the snapshot are lost. + Dump the database on the source PostgreSQL (prefer a `pg_dump` of the same major version as the source): [source,shell] @@ -96,24 +116,11 @@ Restore the dump into the IvorySQL instance (via port 5432 to keep PostgreSQL be # Create the target database first psql -U postgres -p 5432 -c "CREATE DATABASE appdb;" -# Restore the data -pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges appdb.dump +# Restore the data (all objects go into the default tablespace) +pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges --no-tablespaces appdb.dump ---- -`--no-owner` and `--no-privileges` skip source-side ownership and grants. If you recreated the same roles on the target, you can drop these options. - -=== Migrate global objects - -`pg_dump` does not include roles or tablespaces. For a fresh target instance, migrate global objects separately: - -[source,shell] ----- -# Export globals from the source -pg_dumpall -U postgres --globals-only -f globals.sql - -# Import on the target (edit out superusers and pre-existing objects first) -psql -U postgres -p 5432 -f globals.sql ----- +`--no-owner` and `--no-privileges` skip source-side ownership and grants. If you recreated the same roles on the target, you can drop these options. `--no-tablespaces` ignores tablespace settings in the dump and places all objects in the default tablespace; to preserve the source tablespace layout, confirm the tablespaces were created in the preparation step and omit this option. == Path 2: pg_dumpall full-cluster migration @@ -128,8 +135,11 @@ pg_dumpall -U postgres -f full_cluster.sql psql -U postgres -p 5432 -f full_cluster.sql ---- +[IMPORTANT] +Stop writes on the source before the final export and keep them stopped until cutover completes; writes made after the snapshot are not included in the dump. + [NOTE] -`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. +`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. Global objects (roles, tablespaces) at the beginning of the dump are executed before the databases; if the source uses custom tablespaces, create the corresponding directories on the IvorySQL host first, otherwise a failed `CREATE TABLESPACE` aborts the entire restore. == Path 3: logical replication online migration @@ -150,13 +160,30 @@ Make sure `pg_hba.conf` allows the IvorySQL host to connect for replication. === Pre-create the structure on the target -Create the same table structures on IvorySQL in advance (you can generate them with `pg_dump --schema-only`) and ensure the target tables have a replica identity: +Create the same table structures on IvorySQL in advance (you can generate them with `pg_dump --schema-only`). It is recommended that target tables keep the same replica identity settings as the source to improve incremental apply performance. + +=== Set replica identity on the source + +Logical replication locates rows to publish on the publisher side using the table's replica identity: tables without a primary key have no replica identity by default, and their UPDATE/DELETE operations cannot be published. Set the replica identity on the source for every primary-key-less table you plan to replicate: [source,sql] ---- --- Tables with primary keys need no extra step --- Tables without primary keys require a replica identity +-- Tables with primary keys need no extra step (the primary key is the replica identity) +-- Tables without primary keys: set REPLICA IDENTITY FULL on the source ALTER TABLE app_table REPLICA IDENTITY FULL; + +-- Check user tables on the source that lack a replica identity +-- (relreplident = 'd' and no primary key) +SELECT n.nspname AS schema_name, c.relname AS table_name +FROM pg_class c +JOIN pg_namespace n ON n.oid = c.relnamespace +WHERE c.relkind = 'r' + AND c.relreplident = 'd' + AND NOT EXISTS ( + SELECT 1 FROM pg_index i + WHERE i.indrelid = c.oid AND i.indisprimary + ) + AND n.nspname NOT IN ('pg_catalog', 'information_schema'); ---- === Create publication and subscription @@ -176,10 +203,42 @@ After the subscription is created, IvorySQL performs the initial sync and then c === Cutover -1. Stop writes on the source and wait until the subscription lag reaches zero (check `pg_stat_subscription` for `latest_end_lsn`). -2. Disable the subscription on IvorySQL: `ALTER SUBSCRIPTION app_sub DISABLE;` -3. Point the application connection string to IvorySQL, using port 1521 if Oracle-compatible behavior is desired. -4. After a stability observation period, drop the subscription and decommission the source. +1. Stop writes on the source; the source now produces no further changes. + +2. Confirm the initial sync is complete. On IvorySQL, verify that every subscribed table has state `r` (normal replication): + +[source,sql] +---- +-- Run on IvorySQL +SELECT srsubid::regsubscription AS subscription, + srrelid::regclass AS table_name, + srsubstate +FROM pg_subscription_rel +ORDER BY 1, 2; + +-- srsubstate: 'd' data copy in progress; 's' data synchronized; +-- 'r' copy finished and in normal replication (all tables must be 'r' before cutover) +---- + +3. Confirm all final changes after the write stop have been applied: record the WAL position at write stop on the source, then wait until `latest_end_lsn` on IvorySQL catches up. + +[source,sql] +---- +-- Source (run after stopping writes): record the current WAL flush position +SELECT pg_current_wal_lsn(); + +-- IvorySQL: latest_end_lsn should equal the recorded position +SELECT subname, received_lsn, latest_end_lsn, latest_end_time +FROM pg_stat_subscription; +---- + +4. Validate key tables with row counts and sampling checks (see "Post-upgrade validation"). + +5. Once validation passes, disable the subscription on IvorySQL: `ALTER SUBSCRIPTION app_sub DISABLE;` + +6. Point the application connection string to IvorySQL, using port 1521 if Oracle-compatible behavior is desired. + +7. After a stability observation period, drop the subscription and decommission the source. == Post-upgrade validation @@ -199,14 +258,19 @@ Logical replication does not replicate sequence values. Synchronize sequences ma [source,shell] ---- # Export sequence state from the source +# (covers all non-system schemas with schema-qualified sequence names) psql -U postgres -d appdb -Atc \ - "SELECT 'SELECT setval(' || quote_literal(quote_ident(sequencename)) || ',' || last_value || ');' \ - FROM pg_sequences WHERE schemaname = 'public'" > sync_sequences.sql + "SELECT 'SELECT setval(' || quote_literal(format('%I.%I', schemaname, sequencename)) || ',' || last_value || ');' \ + FROM pg_sequences \ + WHERE last_value IS NOT NULL \ + AND schemaname NOT IN ('pg_catalog', 'information_schema')" > sync_sequences.sql # Execute on IvorySQL psql -U postgres -p 5432 -d appdb -f sync_sequences.sql ---- +Script notes: `format('%I.%I', ...)` produces fully qualified, quoted sequence names that work in multi-schema scenarios; `last_value IS NOT NULL` excludes sequences that have never been used (their current value equals the start value, so no sync is needed). + === Enable Oracle-compatible mode After validation, connect through port 1521 and verify Oracle-compatible behavior: From 17f7d49370aff7af404ca94d895e3da4f1259836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=9D=E6=96=A4?= <51957749+JerseyYee@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:59:44 +0800 Subject: [PATCH 3/5] docs: fix pg_upgrade cutover checks and psql error handling (CN/EN) - Fix pg_subscription_rel readiness query: regsubscription type does not exist; join pg_subscription and select subname - LSN final-apply check: no longer require exact equality; run CHECKPOINT after write stop, wait for latest_end_lsn to catch up (>=) and stop advancing - psql -f for globals.sql and full_cluster.sql now uses -v ON_ERROR_STOP=1; document that default behavior continues on error and may place tables in default tablespace --- .../ROOT/pages/master/pg_upgrade_guide.adoc | 34 +++++++++++------ .../ROOT/pages/master/pg_upgrade_guide.adoc | 37 +++++++++++++------ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index 1429f1cf..8c1fdbb8 100644 --- a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -87,9 +87,14 @@ pg_dumpall -U postgres --globals-only -f globals.sql # 目标端导入前先编辑 globals.sql: # 1) 去除超级用户相关语句 # 2) 核对 CREATE TABLESPACE 语句中的目录路径,并提前在 IvorySQL 主机上创建这些目录 -psql -U postgres -p 5432 -f globals.sql +# 3) 如需跳过已存在的角色等冲突,可删除对应 CREATE ROLE 语句 +# (-v ON_ERROR_STOP=1 遇错即停,确保表空间等关键对象创建成功) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f globals.sql ---- +[NOTE] +`psql -f` 默认在遇到 SQL 错误时不会中止执行,而是继续处理后续语句并可能返回退出码 0。若表空间目录缺失,`CREATE TABLESPACE` 会报错但脚本继续执行,最终表可能被创建到默认表空间而非预期位置。使用 `-v ON_ERROR_STOP=1` 可在遇到首个错误时立即停止,便于发现并修复问题。 + 若计划将所有表恢复到默认表空间(不保留源端表空间布局),可跳过表空间相关检查,并在下一步恢复时使用 `--no-tablespaces`。 === 导出源库 @@ -130,15 +135,15 @@ pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges --no-tablespa # 源端全库导出(纯 SQL 文本) pg_dumpall -U postgres -f full_cluster.sql -# 目标端导入 -psql -U postgres -p 5432 -f full_cluster.sql +# 目标端导入(-v ON_ERROR_STOP=1 遇错即停,避免表空间等错误被静默跳过) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql ---- [IMPORTANT] 最终导出前必须停止源端业务写入,并保持停止直到割接完成;导出快照之后的写入不会包含在转储文件中。 [NOTE] -`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。转储文件开头的全局对象(角色、表空间)会先于各数据库执行;若源端使用了自定义表空间,请先在 IvorySQL 主机创建对应目录,否则 `CREATE TABLESPACE` 失败将中断整个恢复。 +`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。转储文件开头的全局对象(角色、表空间)会先于各数据库执行;若源端使用了自定义表空间,请先在 IvorySQL 主机创建对应目录。注意 `psql -f` 默认遇错不中止:`CREATE TABLESPACE` 失败后脚本会继续,相关表最终可能落在默认表空间而非预期位置,因此建议配合 `-v ON_ERROR_STOP=1` 使用。 == 路径三:逻辑复制在线迁移 @@ -208,24 +213,29 @@ CREATE SUBSCRIPTION app_sub [source,sql] ---- -- IvorySQL 端执行 -SELECT srsubid::regsubscription AS subscription, - srrelid::regclass AS table_name, - srsubstate -FROM pg_subscription_rel +SELECT s.subname AS subscription, + r.srrelid::regclass AS table_name, + r.srsubstate +FROM pg_subscription_rel r +JOIN pg_subscription s ON s.oid = r.srsubid ORDER BY 1, 2; -- srsubstate:'d' 初始拷贝进行中;'s' 已同步数据; -- 'r' 拷贝完成并进入正常复制(割接前所有表必须为 'r') ---- -3. 确认停写后的最终增量已全部应用:在源端记录停写时的 WAL 位置,等待 IvorySQL 端的 `latest_end_lsn` 追平该位置。 +3. 确认停写后的最终增量已全部应用:停写后源端不再产生新事务,但 CHECKPOINT 等内部操作会使 WAL 位置继续前进,因此不应要求 LSN 精确相等,而应确认订阅端已追平停写时的位置且不再前进。 [source,sql] ---- --- 源端(停写后执行):记录当前 WAL 写入位置 -SELECT pg_current_wal_lsn(); +-- 源端:停写后执行 CHECKPOINT,将已提交事务全部刷入 WAL +CHECKPOINT; + +-- 源端:记录当前 WAL 位置 L1 +SELECT pg_current_wal_lsn() AS source_lsn; --- IvorySQL 端:latest_end_lsn 应等于上面记录的位置 +-- IvorySQL 端:等待 latest_end_lsn >= L1(追平或超过即可) +-- 并观察一段时间 latest_end_lsn 不再前进,说明增量已全部应用 SELECT subname, received_lsn, latest_end_lsn, latest_end_time FROM pg_stat_subscription; ---- diff --git a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index d5f80fdb..46e550fc 100644 --- a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -88,9 +88,15 @@ pg_dumpall -U postgres --globals-only -f globals.sql # 1) Remove superuser-related statements # 2) Verify the directory paths in CREATE TABLESPACE statements # and create these directories on the IvorySQL host in advance -psql -U postgres -p 5432 -f globals.sql +# 3) To skip conflicts such as pre-existing roles, remove the corresponding +# CREATE ROLE statements (-v ON_ERROR_STOP=1 stops at the first error, +# ensuring critical objects like tablespaces are created successfully) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f globals.sql ---- +[NOTE] +By default, `psql -f` does not stop on SQL errors: it continues with subsequent statements and may return exit code 0. If a tablespace directory is missing, `CREATE TABLESPACE` fails but the script keeps running, and affected tables may end up in the default tablespace instead of the intended one. Adding `-v ON_ERROR_STOP=1` stops at the first error so problems are surfaced immediately. + If you plan to restore all tables into the default tablespace (without preserving the source tablespace layout), you can skip the tablespace checks and use `--no-tablespaces` in the restore step below. === Export the source database @@ -131,15 +137,16 @@ If the source instance is small, or you want to move global objects together wit # Full-cluster export as plain SQL pg_dumpall -U postgres -f full_cluster.sql -# Import on the target -psql -U postgres -p 5432 -f full_cluster.sql +# Import on the target (-v ON_ERROR_STOP=1 stops at the first error so +# tablespace and other failures are not silently skipped) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql ---- [IMPORTANT] Stop writes on the source before the final export and keep them stopped until cutover completes; writes made after the snapshot are not included in the dump. [NOTE] -`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. Global objects (roles, tablespaces) at the beginning of the dump are executed before the databases; if the source uses custom tablespaces, create the corresponding directories on the IvorySQL host first, otherwise a failed `CREATE TABLESPACE` aborts the entire restore. +`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. Global objects (roles, tablespaces) at the beginning of the dump are executed before the databases; if the source uses custom tablespaces, create the corresponding directories on the IvorySQL host first. Note that `psql -f` does not stop on errors by default: a failed `CREATE TABLESPACE` is followed by remaining statements, and affected tables may end up in the default tablespace, so `-v ON_ERROR_STOP=1` is recommended. == Path 3: logical replication online migration @@ -210,24 +217,30 @@ After the subscription is created, IvorySQL performs the initial sync and then c [source,sql] ---- -- Run on IvorySQL -SELECT srsubid::regsubscription AS subscription, - srrelid::regclass AS table_name, - srsubstate -FROM pg_subscription_rel +SELECT s.subname AS subscription, + r.srrelid::regclass AS table_name, + r.srsubstate +FROM pg_subscription_rel r +JOIN pg_subscription s ON s.oid = r.srsubid ORDER BY 1, 2; -- srsubstate: 'd' data copy in progress; 's' data synchronized; -- 'r' copy finished and in normal replication (all tables must be 'r' before cutover) ---- -3. Confirm all final changes after the write stop have been applied: record the WAL position at write stop on the source, then wait until `latest_end_lsn` on IvorySQL catches up. +3. Confirm all final changes after the write stop have been applied: after writes stop, the source produces no new transactions, but internal operations such as CHECKPOINT still advance the WAL position. Therefore exact equality of LSNs should not be required; instead, confirm the subscriber has caught up to the write-stop position and no longer advances. [source,sql] ---- --- Source (run after stopping writes): record the current WAL flush position -SELECT pg_current_wal_lsn(); +-- Source: run CHECKPOINT after stopping writes to flush all committed transactions to WAL +CHECKPOINT; + +-- Source: record the current WAL position L1 +SELECT pg_current_wal_lsn() AS source_lsn; --- IvorySQL: latest_end_lsn should equal the recorded position +-- IvorySQL: wait until latest_end_lsn >= L1 (caught up or beyond) +-- and observe that latest_end_lsn no longer advances for a while, +-- which confirms all incremental changes have been applied SELECT subname, received_lsn, latest_end_lsn, latest_end_time FROM pg_stat_subscription; ---- From f63a5e8a311e7d2e5e40d41eb27b0ceddcc98926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=9D=E6=96=A4?= <51957749+JerseyYee@users.noreply.github.com> Date: Wed, 21 Oct 2026 20:00:46 +0800 Subject: [PATCH 4/5] docs: resolve pg_dumpall role conflicts before restore (CN/EN) - Path 2: document that full_cluster.sql contains CREATE ROLE postgres, which already exists on the IvorySQL target - Add pre-import steps: list role creation statements, compare with existing target roles, comment out conflicting CREATE ROLE lines - Explain that ON_ERROR_STOP=1 exits with code 3 before the database is created when a role conflict remains; keep the flag after resolving conflicts --- .../ROOT/pages/master/pg_upgrade_guide.adoc | 39 +++++++++++++++-- .../ROOT/pages/master/pg_upgrade_guide.adoc | 42 ++++++++++++++++--- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index 8c1fdbb8..dbd9abcc 100644 --- a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -134,16 +134,47 @@ pg_restore -U postgres -p 5432 -d appdb --no-owner --no-privileges --no-tablespa ---- # 源端全库导出(纯 SQL 文本) pg_dumpall -U postgres -f full_cluster.sql - -# 目标端导入(-v ON_ERROR_STOP=1 遇错即停,避免表空间等错误被静默跳过) -psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql ---- [IMPORTANT] 最终导出前必须停止源端业务写入,并保持停止直到割接完成;导出快照之后的写入不会包含在转储文件中。 +=== 导入前处理角色冲突 + +`pg_dumpall` 的转储文件开头是全局对象定义,其中每个角色都会生成一条 `CREATE ROLE` 语句,包括源端的 `postgres` 角色。IvorySQL 目标实例在 `initdb` 时已经创建了 `postgres` 角色,因此直接导入会在这条语句处失败: + +[literal] +---- +ERROR: role "postgres" already exists +---- + +配合 `-v ON_ERROR_STOP=1` 时,`psql` 会在该错误处立即退出(退出码 3),后续的建库与数据恢复均不会执行。导入前请先检查转储文件,并删除或注释掉目标端已存在角色的 `CREATE ROLE` 语句: + +[source,shell] +---- +# 1. 列出转储中的全部角色创建语句(位于文件开头的全局对象部分) +grep -n '^CREATE ROLE' full_cluster.sql + +# 2. 列出目标端已存在的角色 +psql -U postgres -p 5432 -c '\du' + +# 3. 对比上述结果,删除或注释目标端已有角色对应的 CREATE ROLE 行 +# 例如目标端必然存在 postgres 角色: +sed -i 's/^CREATE ROLE postgres;/-- CREATE ROLE postgres;/' full_cluster.sql +---- + +只需处理冲突的 `CREATE ROLE` 语句:紧随其后的 `ALTER ROLE` 仍会执行,用于对齐角色属性,请保留。若目标端已存在多个源端角色,按同样方式逐一注释。 + +=== 导入 + +[source,shell] +---- +# 目标端导入(-v ON_ERROR_STOP=1 遇错即停,避免错误被静默跳过) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql +---- + [NOTE] -`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。转储文件开头的全局对象(角色、表空间)会先于各数据库执行;若源端使用了自定义表空间,请先在 IvorySQL 主机创建对应目录。注意 `psql -f` 默认遇错不中止:`CREATE TABLESPACE` 失败后脚本会继续,相关表最终可能落在默认表空间而非预期位置,因此建议配合 `-v ON_ERROR_STOP=1` 使用。 +`pg_dumpall` 生成的纯文本 SQL 不支持并行恢复,大实例建议优先使用路径一。若源端使用了自定义表空间,请先在 IvorySQL 主机创建对应目录。`psql -f` 默认遇错不中止,`CREATE TABLESPACE` 失败后脚本会继续,相关表最终可能落在默认表空间而非预期位置;`-v ON_ERROR_STOP=1` 会让此类错误立即暴露,请在完成上述角色冲突处理后保留该选项。 == 路径三:逻辑复制在线迁移 diff --git a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index 46e550fc..78d56432 100644 --- a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -136,17 +136,49 @@ If the source instance is small, or you want to move global objects together wit ---- # Full-cluster export as plain SQL pg_dumpall -U postgres -f full_cluster.sql - -# Import on the target (-v ON_ERROR_STOP=1 stops at the first error so -# tablespace and other failures are not silently skipped) -psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql ---- [IMPORTANT] Stop writes on the source before the final export and keep them stopped until cutover completes; writes made after the snapshot are not included in the dump. +=== Resolve role conflicts before import + +The beginning of a `pg_dumpall` dump contains global object definitions, and every role produces a `CREATE ROLE` statement, including the source `postgres` role. The IvorySQL target already has a `postgres` role created by `initdb`, so importing directly fails at this statement: + +[literal] +---- +ERROR: role "postgres" already exists +---- + +With `-v ON_ERROR_STOP=1`, `psql` exits immediately at this error (exit code 3), and neither the application database nor the data is restored. Inspect the dump and remove or comment out `CREATE ROLE` statements for roles that already exist on the target before importing: + +[source,shell] +---- +# 1. List all role creation statements in the dump +# (they are in the global objects section at the top of the file) +grep -n '^CREATE ROLE' full_cluster.sql + +# 2. List roles that already exist on the target +psql -U postgres -p 5432 -c '\du' + +# 3. Compare the two lists and delete or comment out the CREATE ROLE lines +# for roles that already exist; the postgres role always exists: +sed -i 's/^CREATE ROLE postgres;/-- CREATE ROLE postgres;/' full_cluster.sql +---- + +Only the conflicting `CREATE ROLE` statements need to be removed: the following `ALTER ROLE` statement still runs and aligns role attributes, so keep it. Repeat the same approach for any other source roles that already exist on the target. + +=== Import + +[source,shell] +---- +# Import on the target (-v ON_ERROR_STOP=1 stops at the first error so +# failures are not silently skipped) +psql -U postgres -p 5432 -v ON_ERROR_STOP=1 -f full_cluster.sql +---- + [NOTE] -`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. Global objects (roles, tablespaces) at the beginning of the dump are executed before the databases; if the source uses custom tablespaces, create the corresponding directories on the IvorySQL host first. Note that `psql -f` does not stop on errors by default: a failed `CREATE TABLESPACE` is followed by remaining statements, and affected tables may end up in the default tablespace, so `-v ON_ERROR_STOP=1` is recommended. +`pg_dumpall` produces plain-text SQL and does not support parallel restore. Prefer Path 1 for large instances. If the source uses custom tablespaces, create the corresponding directories on the IvorySQL host first. Note that `psql -f` does not stop on errors by default: a failed `CREATE TABLESPACE` is followed by remaining statements, and affected tables may end up in the default tablespace. `-v ON_ERROR_STOP=1` surfaces such errors immediately, so keep this option after resolving the role conflicts described above. == Path 3: logical replication online migration From 36183043216e3fc891a57224ed532b89ad9cb722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=9D=E6=96=A4?= <51957749+JerseyYee@users.noreply.github.com> Date: Thu, 22 Oct 2026 19:11:21 +0800 Subject: [PATCH 5/5] docs: align pg_upgrade guidance with operation guide (CN/EN) - Remove the blanket recommendation against pg_upgrade and the unsupported initialization claim - Describe the documented physical upgrade path (target-version tool, -g/--using-ora-pg, compatibility check, post-upgrade validation) and link to operation_guide.adoc --- CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc | 8 +++++--- EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index dbd9abcc..68c150d0 100644 --- a/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/CN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -7,7 +7,7 @@ IvorySQL 基于 PostgreSQL 最新内核构建,并在此基础上提供 Oracle 兼容能力。对于已经在使用 PostgreSQL 的业务,可以按照本指南将数据平滑升级到 IvorySQL,并按需启用 Oracle 兼容模式。 -本文介绍三种升级路径,并给出升级前后的检查清单: +本文介绍三种逻辑迁移路径,并给出升级前后的检查清单: [cols="2,2,3,2"] |=== @@ -29,8 +29,10 @@ IvorySQL 基于 PostgreSQL 最新内核构建,并在此基础上提供 Oracle |需 PostgreSQL 10+,配置相对复杂 |=== -[IMPORTANT] -不推荐使用 `pg_upgrade` 直接从原生 PostgreSQL 升级到 IvorySQL。IvorySQL 在内核中加入了 Oracle 兼容相关的对象与配置,直接使用 `pg_upgrade` 无法保证系统目录中的兼容组件被正确初始化。请使用本指南中的逻辑迁移或逻辑复制路径。 +[NOTE] +如果希望采用物理升级(就地升级、停机时间更短),IvorySQL 也支持使用 `pg_upgrade` 将原生 PostgreSQL 集群升级到 IvorySQL。该路径必须使用**目标 IvorySQL 版本自带的** `pg_upgrade`,并指定 `-g`(`--using-ora-pg`)参数以表示源端为原生 PostgreSQL。升级前应先执行兼容性检查(`-c`),升级后按业务需要完成验证(统计信息、扩展、Oracle 兼容行为等)。具体步骤、参数与注意事项请参阅 xref:master/operation_guide.adoc[运维管理指南]。 + +本文聚焦上述三种逻辑迁移路径,适用于跨版本、跨平台以及需要精细控制迁移对象范围的场景;物理升级路径请以运维管理指南为准。 == 升级前准备 diff --git a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc index 78d56432..bf1e686a 100644 --- a/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc +++ b/EN/modules/ROOT/pages/master/pg_upgrade_guide.adoc @@ -7,7 +7,7 @@ IvorySQL is built on the latest PostgreSQL kernel and adds Oracle compatibility on top of it. If you are already running PostgreSQL, this guide helps you upgrade your data to IvorySQL smoothly and enable Oracle-compatible mode as needed. -This guide describes three upgrade paths and provides pre/post-migration checklists: +This guide describes three logical migration paths and provides pre/post-migration checklists: [cols="2,2,3,2"] |=== @@ -29,8 +29,10 @@ This guide describes three upgrade paths and provides pre/post-migration checkli |Requires PostgreSQL 10+, more complex configuration |=== -[IMPORTANT] -Do not use `pg_upgrade` to upgrade directly from vanilla PostgreSQL to IvorySQL. IvorySQL adds Oracle-compatibility objects and configuration inside the kernel, and `pg_upgrade` cannot guarantee that these compatibility components are initialized correctly in the system catalogs. Use the logical migration or logical replication paths described in this guide. +[NOTE] +If you prefer a physical (in-place) upgrade with a shorter downtime window, IvorySQL also supports upgrading a native PostgreSQL cluster to IvorySQL with `pg_upgrade`. This path requires the `pg_upgrade` binary shipped with the **target IvorySQL version** and the `-g` (`--using-ora-pg`) option to indicate that the source is a native PostgreSQL cluster. Run the compatibility check (`-c`) before upgrading, and validate the result afterwards as required by your workload (statistics, extensions, Oracle-compatible behavior, and so on). For the detailed steps, options, and caveats, see xref:master/operation_guide.adoc[Operation and Maintenance Guide]. + +This guide focuses on the three logical migration paths above, which suit cross-version, cross-platform migrations and scenarios that need fine-grained control over the objects being migrated. For the physical upgrade path, refer to the operation guide. == Pre-upgrade preparation