Fix COPY FROM ON_ERROR SET_NULL with selective column list

When using COPY FROM ... ON_ERROR SET_NULL with a selective column list, the
domain_with_constraint array was incorrectly allocated based on the length of
the target column list. While the array was populated sequentially,
CopyFromTextLikeOneRow attempted to access it using the physical attribute
index (attnum - 1). This mismatch caused out-of-bounds reads when targeting
high-numbered columns, allowing NULL values to bypass NOT NULL domain checks
and be silently inserted.

Fix by allocating the array to match the total number of physical attributes
(num_phys_attrs) and indexing via attnum - 1, bringing it into alignment with
other per-column arrays in BeginCopyFrom.

Author: SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAHg+QDdej0c0gWJi2FnbirzhgzyZNPiTwC1P5B_-dSNCzq-91A@mail.gmail.com
This commit is contained in:
Fujii Masao 2026-05-19 10:11:41 +09:00
parent 801b9962e7
commit 1164a82272
3 changed files with 10 additions and 6 deletions

View file

@ -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,15 +1644,13 @@ 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);
Form_pg_attribute att = TupleDescAttr(tupDesc, attno - 1);
cstate->domain_with_constraint[i] = DomainHasConstraints(att->atttypid, NULL);
cstate->domain_with_constraint[attno - 1] = DomainHasConstraints(att->atttypid, NULL);
}
}

View file

@ -805,6 +805,10 @@ 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: null input
COPY t_on_error_null(c, a) 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: null input
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.

View file

@ -555,6 +555,10 @@ COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
\N 11 13
\.
COPY t_on_error_null(c, a) FROM STDIN WITH (on_error set_null); -- fail
11 \N
\.
COPY t_on_error_null FROM STDIN WITH (on_error set_null); -- fail
ss 11 14
\.