This issue requires an assignee. Since you haven't specified one, we've assigned TestManager as the default assignee for this issue.


Welcome To openGauss Community
Hey @wuchenlong , thanks for your contribution to the community.
Bot Usage Manual
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands.
Contact Guide
If you have any questions, please contact the SIG: StorageEngine, AI, CM, CloudNative, SecurityTechnology, SQLEngine ,
and any of the maintainers: @CarrotGo, @chendong76, @chenxiaobin19, @congzhou2603, @dodders, @hwworkholic, @jemappellehc, @muyulinzhong, @quemingjian, @shenzheng4, @shirley_zhengx, @superlchf, @totaj, @wlff234, @wofanzheng, @ywzq1161327784 ,
and any of the committers: @Igali, @bihua111, @cailei19, @h_ray, @levy53071, @libiao2024, @lihaixiao, @mrzack, @wangfeihuo, @wuyuechuan, @xiong_xjun, @zhangfengzhi123, @zhangxubo, @zhangzq131, @zjh_hw .


是问题。USTORE 有索引表的批量 COPY 路径遗漏 AFTER ROW INSERT 触发器调用。
-
oG的外键校验不是普通 CHECK,而是内部的 AFTER ROW INSERT 触发器.
-
COPY FROM 遇到只有 AFTER 触发器的表,仍会启用批量插入优化。
-
在 UHeapCopyFromInsertBatch() 中:
表有索引时,只执行数据写入和索引更新,然后直接释放 recheckIndexes。漏掉了 ExecARInsertTriggers() 调用。
表无索引时反而会正常调用 AFTER 触发器ASTORE 的相同分支会正确调用 ExecARInsertTriggers(),所以能够报外键错误。
COPY FROM
→ USTORE 批量插入
→ 写数据
→ 更新主键/其他索引
→ 漏执行 AFTER INSERT 触发器
→ 外键未检查
→ 孤儿行写入成功


验证版本:606B002UB
验证结果:通过



【回归版本】openGauss 7.0.0-RC3 build 7a39d3ac(7.0.0LTS B017,compiled at 2026-08-05 14:35:47)
【回归日期】20260806
【回归人员】wuchenlong
【回归结论】通过
测试结果
USTORE 父子表 + 外键场景:父表为空时对子表执行 COPY FROM,现在正确报外键约束错误,不再写入孤儿行(孤儿查询 0 行);与同结构 ASTORE、普通 INSERT 的外键检查行为一致。
说明
历史异常:USTORE 表存在索引时,COPY FROM 批量插入路径(UHeapCopyFromInsertBatch())遗漏 ExecARInsertTriggers() 调用,导致 AFTER ROW INSERT 触发器未执行、外键不校验、可写入孤儿行。
B017 上 USTORE COPY 与 ASTORE/INSERT 行为一致,不再复现。
【回归步骤】
执行 SQL(postgres 库,A 兼容):
\c postgres
DROP DATABASE IF EXISTS t2_fkcopy;
DROP TABLE IF EXISTS locations CASCADE;
DROP TABLE IF EXISTS countries CASCADE;
DROP TABLE IF EXISTS regions CASCADE;
CREATE TABLE regions (
region_id number PRIMARY KEY,
region_name varchar2(25)
) WITH (storage_type=ustore);
CREATE TABLE countries (
country_id char(2) PRIMARY KEY,
country_name varchar2(40),
region_id number,
CONSTRAINT fk_c_r FOREIGN KEY (region_id) REFERENCES regions(region_id)
) WITH (storage_type=ustore);
CREATE TABLE locations (
location_id number(4) PRIMARY KEY,
street_address varchar2(40),
postal_code varchar2(12),
city varchar2(30) NOT NULL,
state_province varchar2(25),
country_id char(2),
CONSTRAINT fk_l_c FOREIGN KEY (country_id) REFERENCES countries(country_id)
) WITH (storage_type=ustore);
INSERT INTO regions VALUES (1,'Europe'),(2,'Americas'),(3,'Asia'),(4,'ME');
INSERT INTO countries VALUES ('IT','Italy',1),('JP','Japan',3),('US','USA',2);
INSERT INTO locations VALUES (1000,'a','1','Roma',NULL,'IT'),(1200,'b','2','Tokyo','TP','JP');
COPY locations TO '/tmp/reg_loc_fk.dat';
COPY countries TO '/tmp/reg_cty_fk.dat';
COPY regions TO '/tmp/reg_reg_fk.dat';
CREATE DATABASE t2_fkcopy;
\c t2_fkcopy
-- 仅建空 USTORE 结构(regions/countries/locations 同上面定义,storage_type=ustore)
-- 2) 父表为空时 COPY 子表(期望报 FK 错误)
COPY locations FROM '/tmp/reg_loc_fk.dat';
SELECT l.location_id, l.country_id FROM locations l
LEFT JOIN countries c ON l.country_id=c.country_id WHERE c.country_id IS NULL;
COPY countries FROM '/tmp/reg_cty_fk.dat';
SELECT c.country_id, c.region_id FROM countries c
LEFT JOIN regions r ON c.region_id=r.region_id WHERE r.region_id IS NULL;
执行结果:
=== USTORE: COPY locations while countries empty ===
ERROR: insert or update on table "locations" violates foreign key constraint "fk_l_c"
DETAIL: Key (country_id)=(IT) is not present in table "countries".
orphan locations:
location_id | country_id
-------------+------------
(0 rows)
=== USTORE: COPY countries while regions empty ===
ERROR: insert or update on table "countries" violates foreign key constraint "fk_c_r"
DETAIL: Key (region_id)=(1) is not present in table "regions".
orphan countries:
country_id | region_id
------------+-----------
(0 rows)
-- ASTORE 对照(同结构,同样反序 COPY,报 FK 错误)
=== ASTORE: COPY locations while countries empty ===
ERROR: insert or update on table "locations" violates foreign key constraint "fk_l_c2"
DETAIL: Key (country_id)=(IT) is not present in table "countries".
=== ASTORE: COPY countries while regions empty ===
ERROR: insert or update on table "countries" violates foreign key constraint "fk_c_r2"
DETAIL: Key (region_id)=(1) is not present in table "regions".


测试类型
SQL功能
存储功能
测试版本
6.0.6LTS/7.0.0LTS
问题描述
【ustore】ustore 表存在外键时,COPY FROM 导入数据未受外键约束。空父表情况下 COPY 子表可成功并写入孤儿行;同结构 ASTORE 会正确报 FK 错误;USTORE 上 INSERT 仍检查外键。官方文档未声明该差异,且称 USTORE 与 ASTORE SQL 语法基本一致。
操作系统和硬件信息
openEuler release 24.03 (LTS) aarch64
测试环境
企业版主备
操作步骤
-- 1) USTORE 父子表 + 外键,导出后空库反序 COPY DROP DATABASE IF EXISTS t2_fkcopy; \c postgres DROP TABLE IF EXISTS locations, countries, regions CASCADE; CREATE TABLE regions ( region_id number PRIMARY KEY, region_name varchar2(25) ) WITH (storage_type=ustore); CREATE TABLE countries ( country_id char(2) PRIMARY KEY, country_name varchar2(40), region_id number, CONSTRAINT fk_c_r FOREIGN KEY (region_id) REFERENCES regions(region_id) ) WITH (storage_type=ustore); CREATE TABLE locations ( location_id number(4) PRIMARY KEY, street_address varchar2(40), postal_code varchar2(12), city varchar2(30) NOT NULL, state_province varchar2(25), country_id char(2), CONSTRAINT fk_l_c FOREIGN KEY (country_id) REFERENCES countries(country_id) ) WITH (storage_type=ustore); INSERT INTO regions VALUES (1,'Europe'),(2,'Americas'),(3,'Asia'),(4,'ME'); INSERT INTO countries VALUES ('IT','Italy',1),('JP','Japan',3),('US','USA',2); INSERT INTO locations VALUES (1000,'a','1','Roma',NULL,'IT'),(1200,'b','2','Tokyo','TP','JP'); COPY locations TO '/tmp/locations_fk.sql'; COPY countries TO '/tmp/countries_fk.sql'; COPY regions TO '/tmp/regions_fk.sql'; CREATE DATABASE t2_fkcopy; \c t2_fkcopy -- 仅建空 USTORE 结构(同上) -- ... create regions/countries/locations with storage_type=ustore ... -- 2) 父表为空时 COPY 子表(期望应违反外键) COPY locations FROM '/tmp/locations_fk.sql'; SELECT l.location_id, l.country_id FROM locations l LEFT JOIN countries c ON l.country_id=c.country_id WHERE c.country_id IS NULL; COPY countries FROM '/tmp/countries_fk.sql'; SELECT c.country_id, c.region_id FROM countries c LEFT JOIN regions r ON c.region_id=r.region_id WHERE r.region_id IS NULL; -- 3) 对照:同结构 ASTORE,同样反序 COPY(期望报错) -- ... create astore tables with same FK ... COPY locations FROM '/tmp/locations_fk.sql'; -- ASTORE 报 FK 错误预期输出
实际输出
日志信息
文档核查(opengauss/docs 本地最新):
本地复现环境:
pg_is_in_recovery()=f
完整 SQL/输出:issues/issue-to-submit/repro_1060640_ustore_copy_fk.sql 、result_1060640.txt
提单组织
测试团队