Skip to content

Commit da8a4c1

Browse files
committed
Reject a copy EOF marker that has data ahead of it on the same line.
We have always documented that a copy EOF marker (\.) must appear by itself on a line, and that is how psql interprets the rule. However, the backend's actual COPY FROM logic only insists that there not be data between the \. and the following newline. Any data ahead of the \. is parsed as a final line of input. It's hard to interpret this as anything but an ancient mistake that we've faithfully carried forward. Continuing to allow it is not cost-free, since it could mask client-side bugs that unnecessarily backslash-escape periods (and thereby risk accidentally creating an EOF marker). So, let's remove that provision and throw error if the EOF marker isn't alone on its line, matching what the documentation has said right along. Adjust the relevant error messages to be clearer, too. Discussion: https://postgr.es/m/ed659f37-a9dd-42a7-82b9-0da562cc4006@manitou-mail.org
1 parent 983a588 commit da8a4c1

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

src/backend/commands/copyfromparse.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,7 @@ CopyReadLineText(CopyFromState cstate)
14031403
else if (c2 != '\r')
14041404
ereport(ERROR,
14051405
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
1406-
errmsg("end-of-copy marker corrupt")));
1406+
errmsg("end-of-copy marker is not alone on its line")));
14071407
}
14081408

14091409
/* Get the next character */
@@ -1414,25 +1414,27 @@ CopyReadLineText(CopyFromState cstate)
14141414
if (c2 != '\r' && c2 != '\n')
14151415
ereport(ERROR,
14161416
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
1417-
errmsg("end-of-copy marker corrupt")));
1417+
errmsg("end-of-copy marker is not alone on its line")));
14181418

14191419
if ((cstate->eol_type == EOL_NL && c2 != '\n') ||
14201420
(cstate->eol_type == EOL_CRNL && c2 != '\n') ||
14211421
(cstate->eol_type == EOL_CR && c2 != '\r'))
1422-
{
14231422
ereport(ERROR,
14241423
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
14251424
errmsg("end-of-copy marker does not match previous newline style")));
1426-
}
14271425

14281426
/*
1429-
* Transfer only the data before the \. into line_buf, then
1430-
* discard the data and the \. sequence.
1427+
* If there is any data on this line before the \., complain.
1428+
*/
1429+
if (cstate->line_buf.len > 0 ||
1430+
prev_raw_ptr > cstate->input_buf_index)
1431+
ereport(ERROR,
1432+
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
1433+
errmsg("end-of-copy marker is not alone on its line")));
1434+
1435+
/*
1436+
* Discard the \. and newline, then report EOF.
14311437
*/
1432-
if (prev_raw_ptr > cstate->input_buf_index)
1433-
appendBinaryStringInfo(&cstate->line_buf,
1434-
cstate->input_buf + cstate->input_buf_index,
1435-
prev_raw_ptr - cstate->input_buf_index);
14361438
cstate->input_buf_index = input_buf_ptr;
14371439
result = true; /* report EOF */
14381440
break;

src/test/regress/expected/copy.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ select test from copytest2 order by test collate "C";
5050
line2
5151
(3 rows)
5252

53+
-- in text mode, \. must be alone on its line
54+
truncate copytest2;
55+
copy copytest2(test) from stdin;
56+
ERROR: end-of-copy marker is not alone on its line
57+
CONTEXT: COPY copytest2, line 3
58+
copy copytest2(test) from stdin;
59+
ERROR: end-of-copy marker is not alone on its line
60+
CONTEXT: COPY copytest2, line 3
61+
select test from copytest2;
62+
test
63+
------
64+
(0 rows)
65+
5366
-- test header line feature
5467
create temp table copytest3 (
5568
c1 int,

src/test/regress/sql/copy.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ truncate copytest2;
5050
copy copytest2(test) from :'filename' csv;
5151
select test from copytest2 order by test collate "C";
5252

53+
-- in text mode, \. must be alone on its line
54+
truncate copytest2;
55+
copy copytest2(test) from stdin;
56+
line1
57+
line2
58+
foo\.
59+
line3
60+
\.
61+
copy copytest2(test) from stdin;
62+
line4
63+
line5
64+
\.foo
65+
line6
66+
\.
67+
select test from copytest2;
68+
5369

5470
-- test header line feature
5571

0 commit comments

Comments
 (0)