Re: Fix DEFAULT marker handling in COPY FROM ... HEADER MATCH

From: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
To: Ayoub Kazar <ayoub(dot)kazar(at)data-bene(dot)io>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: Fix DEFAULT marker handling in COPY FROM ... HEADER MATCH
Date: 2026-11-04 06:54:43
Message-ID: B8D1E065-575B-4C50-862E-FA156B8B3910@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

> On Nov 4, 2026, at 08:21, Ayoub Kazar <ayoub(dot)kazar(at)data-bene(dot)io> wrote:
>
> Hello,
>
> I noticed an issue when combining the HEADER MATCH option (added in v15) with the DEFAULT option in COPY FROM (added in v16).
>
> If any column name in the header line matches the configured DEFAULT marker,
> two problematic behaviors occur depending on whether the column has a default:
>
> -When the column has a default value, the input value of the first data row
> is discarded and replaced with the column's default expression. All
> subsequent data rows are loaded normally:
>
> CREATE TABLE sales (id int, "default" int DEFAULT 0, note text);
>
> COPY sales FROM STDIN WITH (FORMAT csv, HEADER MATCH, DEFAULT 'default');
> id,default,note
> 1,5,first
> 2,7,second
> \.
>
> SELECT * FROM sales ORDER BY id;
> id | default | note
> ----+---------+--------
> 1 | 0 | first -- Expected 5 here ?
> 2 | 7 | second
> (2 rows)
>
> -When the column does not have a default value, COPY aborts during header
> parsing, but not for the correct reason i guess:
>
> CREATE TABLE t (d int, b text);
> COPY t FROM STDIN WITH (FORMAT csv, HEADER MATCH, DEFAULT 'd');
> d,b
> 5,x
> \.
> ERROR: unexpected default marker in COPY data
> DETAIL: Column "d" has no default value.
> CONTEXT: COPY t, line 1: "d,b"
>
> This also happens with foreign tables via file_fdw when used with
> OPTIONS (header 'match', default '...').
>
>
> Because header parsing and the first row of data are parsed within the same
> first call to NextCopyFrom(), and cstate->defaults is initialized to false
> only at the top of NextCopyFrom(), the flag set while reading the header
> line survived into the first data row. CopyFromTextLikeOneRow() then sees
> cstate->defaults[m] set to true and evaluate the default expression instead of
> the supplied field.
>
> The attached fix temporarily disables default marker recognition while parsing
> the header line when HEADER MATCH. I also added two tests for this.
>
>
> This affects REL_16_STABLE through master.
>
> Thoughts?
>
>
> Regards,
> Ayoub Kazar<0001-Fix-DEFAULT-marker-handling-in-COPY-FROM-.-HEADER-MATCH.patch>

Nice catch. I reproduced the problem and confirmed that the v1 fix works.

The root cause is that CopyReadAttributesCSV() and CopyReadAttributesText() use the same logic to parse header lines and data lines, so a column name may be incorrectly interpreted as a DEFAULT marker. From this perspective, I think adding an is_header parameter to these two functions to distinguish header parsing from data parsing would be a more general solution.

With your finding, I realized that NULL markers have the same problem:
```
evantest=# create table t (id int, a int);
CREATE TABLE
evantest=# copy t from stdin with (format csv, header match, null 'a');
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> id,a
>> 1,2
>> \.
ERROR: column name mismatch in header line field 2: got null value ("a"), expected "a"
CONTEXT: COPY t, line 1: "id,a"
```

Here, a is a valid column name in the header line and should not be rejected simply because it matches the NULL marker.

Adding an is_header parameter allows us to disable both NULL and DEFAULT marker recognition when parsing header lines, addressing both issues with the same approach. It also avoids temporarily modifying the COPY options.

So, I’m proposing v2 with this approach. I kept the regression tests you added in v1 and added tests for NULL markers and the text format. Would you please review v2?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

Attachment Content-Type Size
v2-0001-Fix-DEFAULT-and-NULL-marker-handling-in-COPY-FROM.patch application/octet-stream 11.8 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Chao Li 2026-11-04 07:05:52 Re: [PATCH v1] psql: Do not complete HEADER MATCH for COPY TO
Previous Message Nikolay Samokhvalov 2026-11-04 06:43:58 Re: Add wait events for server logging destination writes