Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit be4b8a8

Browse files
committed
Don't use %s-with-precision format spec to truncate data being displayed
in a COPY error message. It seems that glibc gets indigestion if it is asked to truncate strings that contain invalid UTF-8 encoding sequences. vsnprintf will return -1 in such cases, leading to looping and eventual memory overflow in elog.c. Instead use our own, more robust pg_mbcliplen routine. I believe this problem accounts for several recent reports of unexpected 'out of memory' errors during COPY IN.
1 parent 6bdfde9 commit be4b8a8

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

src/backend/commands/copy.c

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.214 2003/11/29 19:51:47 pgsql Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.215 2004/01/18 02:15:29 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -139,6 +139,7 @@ static Datum CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo,
139139
Oid typelem, bool *isnull);
140140
static void CopyAttributeOut(char *string, char *delim);
141141
static List *CopyGetAttnums(Relation rel, List *attnamelist);
142+
static void limit_printout_length(StringInfo buf);
142143

143144
/* Internal communications functions */
144145
static void SendCopyBegin(bool binary, int natts);
@@ -1140,8 +1141,6 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
11401141
static void
11411142
copy_in_error_callback(void *arg)
11421143
{
1143-
#define MAX_COPY_DATA_DISPLAY 100
1144-
11451144
if (copy_binary)
11461145
{
11471146
/* can't usefully display the data */
@@ -1156,17 +1155,18 @@ copy_in_error_callback(void *arg)
11561155
if (copy_attname)
11571156
{
11581157
/* error is relevant to a particular column */
1159-
errcontext("COPY %s, line %d, column %s: \"%.*s%s\"",
1158+
limit_printout_length(&attribute_buf);
1159+
errcontext("COPY %s, line %d, column %s: \"%s\"",
11601160
copy_relname, copy_lineno, copy_attname,
1161-
MAX_COPY_DATA_DISPLAY, attribute_buf.data,
1162-
(attribute_buf.len > MAX_COPY_DATA_DISPLAY) ? "..." : "");
1161+
attribute_buf.data);
11631162
}
11641163
else
11651164
{
11661165
/* error is relevant to a particular line */
11671166
if (!line_buf_converted)
11681167
{
11691168
/* didn't convert the encoding yet... */
1169+
line_buf_converted = true;
11701170
if (client_encoding != server_encoding)
11711171
{
11721172
char *cvt;
@@ -1181,16 +1181,46 @@ copy_in_error_callback(void *arg)
11811181
appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
11821182
}
11831183
}
1184-
line_buf_converted = true;
11851184
}
1186-
errcontext("COPY %s, line %d: \"%.*s%s\"",
1185+
limit_printout_length(&line_buf);
1186+
errcontext("COPY %s, line %d: \"%s\"",
11871187
copy_relname, copy_lineno,
1188-
MAX_COPY_DATA_DISPLAY, line_buf.data,
1189-
(line_buf.len > MAX_COPY_DATA_DISPLAY) ? "..." : "");
1188+
line_buf.data);
11901189
}
11911190
}
11921191
}
11931192

1193+
/*
1194+
* Make sure we don't print an unreasonable amount of COPY data in a message.
1195+
*
1196+
* It would seem a lot easier to just use the sprintf "precision" limit to
1197+
* truncate the string. However, some versions of glibc have a bug/misfeature
1198+
* that vsnprintf will always fail (return -1) if it is asked to truncate
1199+
* a string that contains invalid byte sequences for the current encoding.
1200+
* So, do our own truncation. We assume we can alter the StringInfo buffer
1201+
* holding the input data.
1202+
*/
1203+
static void
1204+
limit_printout_length(StringInfo buf)
1205+
{
1206+
#define MAX_COPY_DATA_DISPLAY 100
1207+
1208+
int len;
1209+
1210+
/* Fast path if definitely okay */
1211+
if (buf->len <= MAX_COPY_DATA_DISPLAY)
1212+
return;
1213+
1214+
/* Apply encoding-dependent truncation */
1215+
len = pg_mbcliplen(buf->data, buf->len, MAX_COPY_DATA_DISPLAY);
1216+
if (buf->len <= len)
1217+
return; /* no need to truncate */
1218+
buf->len = len;
1219+
buf->data[len] = '\0';
1220+
1221+
/* Add "..." to show we truncated the input */
1222+
appendStringInfoString(buf, "...");
1223+
}
11941224

11951225
/*
11961226
* Copy FROM file to relation.
@@ -1875,7 +1905,15 @@ CopyReadLine(void)
18751905

18761906
/*
18771907
* Done reading the line. Convert it to server encoding.
1908+
*
1909+
* Note: set line_buf_converted to true *before* attempting conversion;
1910+
* this prevents infinite recursion during error reporting should
1911+
* pg_client_to_server() issue an error, due to copy_in_error_callback
1912+
* again attempting the same conversion. We'll end up issuing the message
1913+
* without conversion, which is bad but better than nothing ...
18781914
*/
1915+
line_buf_converted = true;
1916+
18791917
if (change_encoding)
18801918
{
18811919
cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
@@ -1889,8 +1927,6 @@ CopyReadLine(void)
18891927
}
18901928
}
18911929

1892-
line_buf_converted = true;
1893-
18941930
return result;
18951931
}
18961932

0 commit comments

Comments
 (0)