v1-0001-Fix-ON_ERROR-SET_NULL-domain-check-with-reordered.patch
application/octet-stream
Filename: v1-0001-Fix-ON_ERROR-SET_NULL-domain-check-with-reordered.patch
Type: application/octet-stream
Part: 0
Message:
Fix bug of COPY (on_error set_null)
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: Fix ON_ERROR SET_NULL domain check with reordered COPY column lists
| File | + | − |
|---|---|---|
| src/backend/commands/copyfrom.c | 10 | 12 |
| src/test/regress/expected/copy2.out | 7 | 0 |
| src/test/regress/sql/copy2.sql | 7 | 0 |
From 28ff0866e8c1eb0dc756d0f4bb9d5f6ac9d1519a Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Fri, 15 May 2026 09:00:28 +0800
Subject: [PATCH v1] Fix ON_ERROR SET_NULL domain check with reordered COPY
column lists
BeginCopyFrom() initialized domain_with_constraint using positions in the
COPY column list, but CopyFromTextLikeOneRow() checks it using physical
attribute numbers. With a COPY column list whose order differs from the
table's physical column order, this could miss a domain constraint or read
the wrong flag.
Allocate the array for all physical attributes and store each flag using
attnum - 1, matching the later lookup.
While here, also change a few palloc to palloc_array.
Author: Chao Li <lic@highgo.com>
---
src/backend/commands/copyfrom.c | 22 ++++++++++------------
src/test/regress/expected/copy2.out | 7 +++++++
src/test/regress/sql/copy2.sql | 7 +++++++
3 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 64ac3063c61..eddc46f09e1 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -1636,8 +1636,6 @@ BeginCopyFrom(ParseState *pstate,
if (cstate->opts.on_error == COPY_ON_ERROR_SET_NULL)
{
- int attr_count = list_length(cstate->attnumlist);
-
/*
* When data type conversion fails and ON_ERROR is SET_NULL, we need
* ensure that the input column allow null values. ExecConstraints()
@@ -1646,20 +1644,20 @@ BeginCopyFrom(ParseState *pstate,
* check must be performed during the initial string-to-datum
* conversion (see CopyFromTextLikeOneRow()).
*/
- cstate->domain_with_constraint = palloc0_array(bool, attr_count);
+ cstate->domain_with_constraint = palloc0_array(bool, num_phys_attrs);
foreach_int(attno, cstate->attnumlist)
{
- int i = foreach_current_index(attno);
+ int m = attno - 1;
- Form_pg_attribute att = TupleDescAttr(tupDesc, attno - 1);
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
- cstate->domain_with_constraint[i] = DomainHasConstraints(att->atttypid, NULL);
+ cstate->domain_with_constraint[m] = DomainHasConstraints(att->atttypid, NULL);
}
}
/* Convert FORCE_NULL name list to per-column flags, check validity */
- cstate->opts.force_null_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
+ cstate->opts.force_null_flags = palloc0_array(bool, num_phys_attrs);
if (cstate->opts.force_null_all)
MemSet(cstate->opts.force_null_flags, true, num_phys_attrs * sizeof(bool));
else if (cstate->opts.force_null)
@@ -1777,10 +1775,10 @@ BeginCopyFrom(ParseState *pstate,
* the input function), and info about defaults and constraints. (Which
* input function we use depends on text/binary format choice.)
*/
- in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
- typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
- defmap = (int *) palloc(num_phys_attrs * sizeof(int));
- defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
+ in_functions = palloc_array(FmgrInfo, num_phys_attrs);
+ typioparams = palloc_array(Oid, num_phys_attrs);
+ defmap = palloc_array(int, num_phys_attrs);
+ defexprs = palloc_array(ExprState *, num_phys_attrs);
for (int attnum = 1; attnum <= num_phys_attrs; attnum++)
{
@@ -1846,7 +1844,7 @@ BeginCopyFrom(ParseState *pstate,
}
}
- cstate->defaults = (bool *) palloc0(tupDesc->natts * sizeof(bool));
+ cstate->defaults = palloc0_array(bool, tupDesc->natts);
/* initialize progress */
pgstat_progress_start_command(PROGRESS_COMMAND_COPY,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 7600e5239d2..5ec2ad9f156 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -800,6 +800,7 @@ NOTICE: 6 rows were skipped due to data type incompatibility
CREATE DOMAIN d_int_not_null AS integer NOT NULL CHECK (value > 0);
CREATE DOMAIN d_int_positive_maybe_null AS integer CHECK (value > 0);
CREATE TABLE t_on_error_null (a d_int_not_null, b d_int_positive_maybe_null, c integer);
+CREATE TABLE t_on_error_null_col_order (a integer, b d_int_not_null, c integer);
\pset null NULL
COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
ERROR: domain d_int_not_null does not allow null values
@@ -813,6 +814,11 @@ COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
ERROR: domain d_int_not_null does not allow null values
DETAIL: ON_ERROR SET_NULL cannot be applied because column "a" (domain d_int_not_null) does not accept null values.
CONTEXT: COPY t_on_error_null, line 1, column a: "-1"
+-- fail, COPY column list order is not physical attribute order.
+COPY t_on_error_null_col_order (b, c) FROM STDIN WITH (on_error set_null);
+ERROR: domain d_int_not_null does not allow null values
+DETAIL: ON_ERROR SET_NULL cannot be applied because column "b" (domain d_int_not_null) does not accept null values.
+CONTEXT: COPY t_on_error_null_col_order, line 1, column b: "ss"
-- fail, less data.
COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null);
ERROR: missing data for column "c"
@@ -899,6 +905,7 @@ DROP VIEW instead_of_insert_tbl_view;
DROP VIEW instead_of_insert_tbl_view_2;
DROP FUNCTION fun_instead_of_insert_tbl();
DROP TABLE check_ign_err;
+DROP TABLE t_on_error_null_col_order;
DROP TABLE t_on_error_null;
DROP DOMAIN d_int_not_null;
DROP DOMAIN d_int_positive_maybe_null;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index e0810109473..3be2fdb9d42 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -549,6 +549,7 @@ a {2} 2
CREATE DOMAIN d_int_not_null AS integer NOT NULL CHECK (value > 0);
CREATE DOMAIN d_int_positive_maybe_null AS integer CHECK (value > 0);
CREATE TABLE t_on_error_null (a d_int_not_null, b d_int_positive_maybe_null, c integer);
+CREATE TABLE t_on_error_null_col_order (a integer, b d_int_not_null, c integer);
\pset null NULL
COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
@@ -563,6 +564,11 @@ COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
-1 11 13
\.
+-- fail, COPY column list order is not physical attribute order.
+COPY t_on_error_null_col_order (b, c) FROM STDIN WITH (on_error set_null);
+ss 1
+\.
+
-- fail, less data.
COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null);
1,1
@@ -651,6 +657,7 @@ DROP VIEW instead_of_insert_tbl_view;
DROP VIEW instead_of_insert_tbl_view_2;
DROP FUNCTION fun_instead_of_insert_tbl();
DROP TABLE check_ign_err;
+DROP TABLE t_on_error_null_col_order;
DROP TABLE t_on_error_null;
DROP DOMAIN d_int_not_null;
DROP DOMAIN d_int_positive_maybe_null;
--
2.50.1 (Apple Git-155)