Improve context display for failures during COPY IN, as recently

discussed on pghackers.
This commit is contained in:
Tom Lane 2003-09-29 22:06:40 +00:00
parent 34c64955cd
commit 0e319c7ad7
5 changed files with 311 additions and 210 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.211 2003/09/25 06:57:58 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.212 2003/09/29 22:06:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -59,22 +59,21 @@
typedef enum CopyDest typedef enum CopyDest
{ {
COPY_FILE, /* to/from file */ COPY_FILE, /* to/from file */
COPY_OLD_FE, /* to/from frontend (old protocol) */ COPY_OLD_FE, /* to/from frontend (2.0 protocol) */
COPY_NEW_FE /* to/from frontend (new protocol) */ COPY_NEW_FE /* to/from frontend (3.0 protocol) */
} CopyDest; } CopyDest;
/* /*
* Represents the type of data returned by CopyReadAttribute() * State indicator showing what stopped CopyReadAttribute()
*/ */
typedef enum CopyReadResult typedef enum CopyReadResult
{ {
NORMAL_ATTR, NORMAL_ATTR,
END_OF_LINE, END_OF_LINE
END_OF_FILE
} CopyReadResult; } CopyReadResult;
/* /*
* Represents the end-of-line terminator of the input * Represents the end-of-line terminator type of the input
*/ */
typedef enum EolType typedef enum EolType
{ {
@ -85,17 +84,6 @@ typedef enum EolType
} EolType; } EolType;
/* non-export function prototypes */
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print);
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print);
static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
static Datum CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo,
Oid typelem, bool *isnull);
static void CopyAttributeOut(char *string, char *delim);
static List *CopyGetAttnums(Relation rel, List *attnamelist);
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* /*
@ -103,11 +91,18 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
* never been reentrant... * never been reentrant...
*/ */
static CopyDest copy_dest; static CopyDest copy_dest;
static FILE *copy_file; /* if copy_dest == COPY_FILE */ static FILE *copy_file; /* used if copy_dest == COPY_FILE */
static StringInfo copy_msgbuf; /* if copy_dest == COPY_NEW_FE */ static StringInfo copy_msgbuf; /* used if copy_dest == COPY_NEW_FE */
static bool fe_eof; /* true if detected end of copy data */ static bool fe_eof; /* true if detected end of copy data */
static EolType eol_type; /* EOL type of input */ static EolType eol_type; /* EOL type of input */
static int client_encoding; /* remote side's character encoding */
static int server_encoding; /* local encoding */
/* these are just for error messages, see copy_in_error_callback */
static bool copy_binary; /* is it a binary copy? */
static const char *copy_relname; /* table name for error messages */
static int copy_lineno; /* line number for error messages */ static int copy_lineno; /* line number for error messages */
static const char *copy_attname; /* current att for error messages */
/* /*
@ -117,16 +112,34 @@ static int copy_lineno; /* line number for error messages */
* grow to a suitable size, and then we will avoid palloc/pfree overhead * grow to a suitable size, and then we will avoid palloc/pfree overhead
* for subsequent attributes. Note that CopyReadAttribute returns a pointer * for subsequent attributes. Note that CopyReadAttribute returns a pointer
* to attribute_buf's data buffer! * to attribute_buf's data buffer!
* encoding, if needed, can be set once at the start of the copy operation.
*/ */
static StringInfoData attribute_buf; static StringInfoData attribute_buf;
static int client_encoding;
static int server_encoding;
/* /*
* Internal communications functions * Similarly, line_buf holds the whole input line being processed (its
* cursor field points to the next character to be read by CopyReadAttribute).
* The input cycle is first to read the whole line into line_buf, convert it
* to server encoding, and then extract individual attribute fields into
* attribute_buf. (We used to have CopyReadAttribute read the input source
* directly, but that caused a lot of encoding issues and unnecessary logic
* complexity).
*/ */
static StringInfoData line_buf;
static bool line_buf_converted;
/* non-export function prototypes */
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print);
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print);
static bool CopyReadLine(void);
static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
static Datum CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo,
Oid typelem, bool *isnull);
static void CopyAttributeOut(char *string, char *delim);
static List *CopyGetAttnums(Relation rel, List *attnamelist);
/* Internal communications functions */
static void SendCopyBegin(bool binary, int natts); static void SendCopyBegin(bool binary, int natts);
static void ReceiveCopyBegin(bool binary, int natts); static void ReceiveCopyBegin(bool binary, int natts);
static void SendCopyEnd(bool binary); static void SendCopyEnd(bool binary);
@ -145,6 +158,7 @@ static int32 CopyGetInt32(void);
static void CopySendInt16(int16 val); static void CopySendInt16(int16 val);
static int16 CopyGetInt16(void); static int16 CopyGetInt16(void);
/* /*
* Send copy start/stop messages for frontend copies. These have changed * Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns. * in past protocol redesigns.
@ -780,6 +794,8 @@ DoCopy(const CopyStmt *stmt)
* Set up variables to avoid per-attribute overhead. * Set up variables to avoid per-attribute overhead.
*/ */
initStringInfo(&attribute_buf); initStringInfo(&attribute_buf);
initStringInfo(&line_buf);
line_buf_converted = false;
client_encoding = pg_get_client_encoding(); client_encoding = pg_get_client_encoding();
server_encoding = GetDatabaseEncoding(); server_encoding = GetDatabaseEncoding();
@ -907,6 +923,7 @@ DoCopy(const CopyStmt *stmt)
else if (IsUnderPostmaster && !is_from) else if (IsUnderPostmaster && !is_from)
SendCopyEnd(binary); SendCopyEnd(binary);
pfree(attribute_buf.data); pfree(attribute_buf.data);
pfree(line_buf.data);
/* /*
* Close the relation. If reading, we can release the AccessShareLock * Close the relation. If reading, we can release the AccessShareLock
@ -1111,7 +1128,55 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
static void static void
copy_in_error_callback(void *arg) copy_in_error_callback(void *arg)
{ {
errcontext("COPY FROM, line %d", copy_lineno); #define MAX_COPY_DATA_DISPLAY 100
if (copy_binary)
{
/* can't usefully display the data */
if (copy_attname)
errcontext("COPY %s, line %d, column %s",
copy_relname, copy_lineno, copy_attname);
else
errcontext("COPY %s, line %d", copy_relname, copy_lineno);
}
else
{
if (copy_attname)
{
/* error is relevant to a particular column */
errcontext("COPY %s, line %d, column %s: \"%.*s%s\"",
copy_relname, copy_lineno, copy_attname,
MAX_COPY_DATA_DISPLAY, attribute_buf.data,
(attribute_buf.len > MAX_COPY_DATA_DISPLAY) ? "..." : "");
}
else
{
/* error is relevant to a particular line */
if (!line_buf_converted)
{
/* didn't convert the encoding yet... */
if (client_encoding != server_encoding)
{
char *cvt;
cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
line_buf.len);
if (cvt != line_buf.data)
{
/* transfer converted data back to line_buf */
line_buf.len = 0;
line_buf.data[0] = '\0';
appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
}
}
line_buf_converted = true;
}
errcontext("COPY %s, line %d: \"%.*s%s\"",
copy_relname, copy_lineno,
MAX_COPY_DATA_DISPLAY, line_buf.data,
(line_buf.len > MAX_COPY_DATA_DISPLAY) ? "..." : "");
}
}
} }
@ -1327,7 +1392,10 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* Initialize static variables */ /* Initialize static variables */
fe_eof = false; fe_eof = false;
eol_type = EOL_UNKNOWN; eol_type = EOL_UNKNOWN;
copy_binary = binary;
copy_relname = RelationGetRelationName(rel);
copy_lineno = 0; copy_lineno = 0;
copy_attname = NULL;
/* Set up callback to identify error line number */ /* Set up callback to identify error line number */
errcontext.callback = copy_in_error_callback; errcontext.callback = copy_in_error_callback;
@ -1359,29 +1427,36 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
CopyReadResult result = NORMAL_ATTR; CopyReadResult result = NORMAL_ATTR;
char *string; char *string;
/* Actually read the line into memory here */
done = CopyReadLine();
/*
* EOF at start of line means we're done. If we see EOF
* after some characters, we act as though it was newline
* followed by EOF, ie, process the line and then exit loop
* on next iteration.
*/
if (done && line_buf.len == 0)
break;
if (file_has_oids) if (file_has_oids)
{ {
string = CopyReadAttribute(delim, &result); string = CopyReadAttribute(delim, &result);
if (result == END_OF_FILE && *string == '\0')
{
/* EOF at start of line: all is well */
done = true;
break;
}
if (strcmp(string, null_print) == 0) if (strcmp(string, null_print) == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT), (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("null OID in COPY data"))); errmsg("null OID in COPY data")));
else else
{ {
copy_attname = "oid";
loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin, loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
CStringGetDatum(string))); CStringGetDatum(string)));
if (loaded_oid == InvalidOid) if (loaded_oid == InvalidOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT), (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid OID in COPY data"))); errmsg("invalid OID in COPY data")));
copy_attname = NULL;
} }
} }
@ -1394,7 +1469,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
int m = attnum - 1; int m = attnum - 1;
/* /*
* If prior attr on this line was ended by newline or EOF, * If prior attr on this line was ended by newline,
* complain. * complain.
*/ */
if (result != NORMAL_ATTR) if (result != NORMAL_ATTR)
@ -1405,68 +1480,33 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
string = CopyReadAttribute(delim, &result); string = CopyReadAttribute(delim, &result);
if (result == END_OF_FILE && *string == '\0' &&
cur == attnumlist && !file_has_oids)
{
/* EOF at start of line: all is well */
done = true;
break; /* out of per-attr loop */
}
if (strcmp(string, null_print) == 0) if (strcmp(string, null_print) == 0)
{ {
/* we read an SQL NULL, no need to do anything */ /* we read an SQL NULL, no need to do anything */
} }
else else
{ {
copy_attname = NameStr(attr[m]->attname);
values[m] = FunctionCall3(&in_functions[m], values[m] = FunctionCall3(&in_functions[m],
CStringGetDatum(string), CStringGetDatum(string),
ObjectIdGetDatum(elements[m]), ObjectIdGetDatum(elements[m]),
Int32GetDatum(attr[m]->atttypmod)); Int32GetDatum(attr[m]->atttypmod));
nulls[m] = ' '; nulls[m] = ' ';
copy_attname = NULL;
} }
} }
if (done)
break; /* out of per-row loop */
/* /*
* Complain if there are more fields on the input line. * Complain if there are more fields on the input line.
* *
* Special case: if we're reading a zero-column table, we won't * Special case: if we're reading a zero-column table, we won't
* yet have called CopyReadAttribute() at all; so do that and * yet have called CopyReadAttribute() at all; so no error if
* check we have an empty line. Fortunately we can keep that * line is empty.
* silly corner case out of the main line of execution.
*/ */
if (result == NORMAL_ATTR) if (result == NORMAL_ATTR && line_buf.len != 0)
{ ereport(ERROR,
if (attnumlist == NIL && !file_has_oids) (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
{ errmsg("extra data after last expected column")));
string = CopyReadAttribute(delim, &result);
if (result == NORMAL_ATTR || *string != '\0')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("extra data after last expected column")));
if (result == END_OF_FILE)
{
/* EOF at start of line: all is well */
done = true;
break;
}
}
else
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("extra data after last expected column")));
}
/*
* If we got some data on the line, but it was ended by EOF,
* process the line normally but set flag to exit the loop
* when we return to the top.
*/
if (result == END_OF_FILE)
done = true;
} }
else else
{ {
@ -1488,6 +1528,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
if (file_has_oids) if (file_has_oids)
{ {
copy_attname = "oid";
loaded_oid = loaded_oid =
DatumGetObjectId(CopyReadBinaryAttribute(0, DatumGetObjectId(CopyReadBinaryAttribute(0,
&oid_in_function, &oid_in_function,
@ -1497,6 +1538,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT), (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid OID in COPY data"))); errmsg("invalid OID in COPY data")));
copy_attname = NULL;
} }
i = 0; i = 0;
@ -1505,12 +1547,14 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
int attnum = lfirsti(cur); int attnum = lfirsti(cur);
int m = attnum - 1; int m = attnum - 1;
copy_attname = NameStr(attr[m]->attname);
i++; i++;
values[m] = CopyReadBinaryAttribute(i, values[m] = CopyReadBinaryAttribute(i,
&in_functions[m], &in_functions[m],
elements[m], elements[m],
&isnull); &isnull);
nulls[m] = isnull ? 'n' : ' '; nulls[m] = isnull ? 'n' : ' ';
copy_attname = NULL;
} }
} }
@ -1642,46 +1686,53 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* /*
* Read the value of a single attribute. * Read the next input line and stash it in line_buf, with conversion to
* server encoding.
* *
* *result is set to indicate what terminated the read: * Result is true if read was terminated by EOF, false if terminated
* NORMAL_ATTR: column delimiter * by newline.
* END_OF_LINE: newline
* END_OF_FILE: EOF indicator
* In all cases, the string read up to the terminator is returned.
*
* Note: This function does not care about SQL NULL values -- it
* is the caller's responsibility to check if the returned string
* matches what the user specified for the SQL NULL value.
*
* delim is the column delimiter string.
*/ */
static char * static bool
CopyReadAttribute(const char *delim, CopyReadResult *result) CopyReadLine(void)
{ {
bool result;
bool change_encoding = (client_encoding != server_encoding);
int c; int c;
int delimc = (unsigned char) delim[0];
int mblen; int mblen;
int j;
unsigned char s[2]; unsigned char s[2];
char *cvt; char *cvt;
int j;
s[1] = 0; s[1] = 0;
/* reset attribute_buf to empty */ /* reset line_buf to empty */
attribute_buf.len = 0; line_buf.len = 0;
attribute_buf.data[0] = '\0'; line_buf.data[0] = '\0';
line_buf.cursor = 0;
/* mark that encoding conversion hasn't occurred yet */
line_buf_converted = false;
/* set default status */ /* set default status */
*result = NORMAL_ATTR; result = false;
/*
* In this loop we only care for detecting newlines (\r and/or \n)
* and the end-of-copy marker (\.). For backwards compatibility
* we allow backslashes to escape newline characters. Backslashes
* other than the end marker get put into the line_buf, since
* CopyReadAttribute does its own escape processing. These four
* characters, and only these four, are assumed the same in frontend
* and backend encodings. We do not assume that second and later bytes
* of a frontend multibyte character couldn't look like ASCII characters.
*/
for (;;) for (;;)
{ {
c = CopyGetChar(); c = CopyGetChar();
if (c == EOF) if (c == EOF)
{ {
*result = END_OF_FILE; result = true;
goto copy_eof; break;
} }
if (c == '\r') if (c == '\r')
{ {
@ -1691,7 +1742,7 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
errmsg("literal carriage return found in data"), errmsg("literal carriage return found in data"),
errhint("Use \"\\r\" to represent carriage return."))); errhint("Use \"\\r\" to represent carriage return.")));
/* Check for \r\n on first line, _and_ handle \r\n. */ /* Check for \r\n on first line, _and_ handle \r\n. */
if (copy_lineno == 1 || eol_type == EOL_CRNL) if (eol_type == EOL_UNKNOWN || eol_type == EOL_CRNL)
{ {
int c2 = CopyPeekChar(); int c2 = CopyPeekChar();
@ -1717,7 +1768,6 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
eol_type = EOL_CR; eol_type = EOL_CR;
} }
} }
*result = END_OF_LINE;
break; break;
} }
if (c == '\n') if (c == '\n')
@ -1728,19 +1778,150 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
errmsg("literal newline found in data"), errmsg("literal newline found in data"),
errhint("Use \"\\n\" to represent newline."))); errhint("Use \"\\n\" to represent newline.")));
eol_type = EOL_NL; eol_type = EOL_NL;
*result = END_OF_LINE;
break; break;
} }
if (c == delimc)
break;
if (c == '\\') if (c == '\\')
{ {
c = CopyGetChar(); c = CopyGetChar();
if (c == EOF) if (c == EOF)
{ {
*result = END_OF_FILE; result = true;
goto copy_eof; break;
} }
if (c == '.')
{
if (eol_type == EOL_CRNL)
{
c = CopyGetChar();
if (c == '\n')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker does not match previous newline style")));
if (c != '\r')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker corrupt")));
}
c = CopyGetChar();
if (c != '\r' && c != '\n')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker corrupt")));
if ((eol_type == EOL_NL && c != '\n') ||
(eol_type == EOL_CRNL && c != '\n') ||
(eol_type == EOL_CR && c != '\r'))
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker does not match previous newline style")));
/*
* In protocol version 3, we should ignore anything
* after \. up to the protocol end of copy data. (XXX
* maybe better not to treat \. as special?)
*/
if (copy_dest == COPY_NEW_FE)
{
while (c != EOF)
c = CopyGetChar();
}
result = true; /* report EOF */
break;
}
/* not EOF mark, so emit \ and following char literally */
appendStringInfoCharMacro(&line_buf, '\\');
}
appendStringInfoCharMacro(&line_buf, c);
/*
* When client encoding != server, must be careful to read the
* extra bytes of a multibyte character exactly, since the encoding
* might not ensure they don't look like ASCII. When the encodings
* are the same, we need not do this, since no server encoding we
* use has ASCII-like following bytes.
*/
if (change_encoding)
{
s[0] = c;
mblen = pg_encoding_mblen(client_encoding, s);
for (j = 1; j < mblen; j++)
{
c = CopyGetChar();
if (c == EOF)
{
result = true;
break;
}
appendStringInfoCharMacro(&line_buf, c);
}
if (result)
break; /* out of outer loop */
}
} /* end of outer loop */
/*
* Done reading the line. Convert it to server encoding.
*/
if (change_encoding)
{
cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
line_buf.len);
if (cvt != line_buf.data)
{
/* transfer converted data back to line_buf */
line_buf.len = 0;
line_buf.data[0] = '\0';
appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
}
}
line_buf_converted = true;
return result;
}
/*
* Read the value of a single attribute, performing de-escaping as needed.
*
* *result is set to indicate what terminated the read:
* NORMAL_ATTR: column delimiter
* END_OF_LINE: end of line
* In either case, the string read up to the terminator is returned.
*
* Note: This function does not care about SQL NULL values -- it
* is the caller's responsibility to check if the returned string
* matches what the user specified for the SQL NULL value.
*
* delim is the column delimiter string.
*/
static char *
CopyReadAttribute(const char *delim, CopyReadResult *result)
{
char c;
char delimc = delim[0];
/* reset attribute_buf to empty */
attribute_buf.len = 0;
attribute_buf.data[0] = '\0';
/* set default status */
*result = END_OF_LINE;
for (;;)
{
if (line_buf.cursor >= line_buf.len)
break;
c = line_buf.data[line_buf.cursor++];
if (c == delimc)
{
*result = NORMAL_ATTR;
break;
}
if (c == '\\')
{
if (line_buf.cursor >= line_buf.len)
break;
c = line_buf.data[line_buf.cursor++];
switch (c) switch (c)
{ {
case '0': case '0':
@ -1755,36 +1936,24 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
int val; int val;
val = OCTVALUE(c); val = OCTVALUE(c);
c = CopyPeekChar(); if (line_buf.cursor < line_buf.len)
if (ISOCTAL(c))
{ {
val = (val << 3) + OCTVALUE(c); c = line_buf.data[line_buf.cursor];
CopyDonePeek(c, true /* pick up */ );
c = CopyPeekChar();
if (ISOCTAL(c)) if (ISOCTAL(c))
{ {
line_buf.cursor++;
val = (val << 3) + OCTVALUE(c); val = (val << 3) + OCTVALUE(c);
CopyDonePeek(c, true /* pick up */ ); if (line_buf.cursor < line_buf.len)
}
else
{
if (c == EOF)
{ {
*result = END_OF_FILE; c = line_buf.data[line_buf.cursor];
goto copy_eof; if (ISOCTAL(c))
{
line_buf.cursor++;
val = (val << 3) + OCTVALUE(c);
}
} }
CopyDonePeek(c, false /* put back */ );
} }
} }
else
{
if (c == EOF)
{
*result = END_OF_FILE;
goto copy_eof;
}
CopyDonePeek(c, false /* put back */ );
}
c = val & 0377; c = val & 0377;
} }
break; break;
@ -1816,79 +1985,12 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
case 'v': case 'v':
c = '\v'; c = '\v';
break; break;
case '.': /*
if (eol_type == EOL_CRNL) * in all other cases, take the char after '\' literally
{ */
c = CopyGetChar();
if (c == '\n')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker does not match previous newline style")));
if (c != '\r')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker corrupt")));
}
c = CopyGetChar();
if (c != '\r' && c != '\n')
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker corrupt")));
if ((eol_type == EOL_NL && c != '\n') ||
(eol_type == EOL_CRNL && c != '\n') ||
(eol_type == EOL_CR && c != '\r'))
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("end-of-copy marker does not match previous newline style")));
/*
* In protocol version 3, we should ignore anything
* after \. up to the protocol end of copy data. (XXX
* maybe better not to treat \. as special?)
*/
if (copy_dest == COPY_NEW_FE)
{
while (c != EOF)
c = CopyGetChar();
}
*result = END_OF_FILE;
goto copy_eof;
} }
} }
appendStringInfoCharMacro(&attribute_buf, c); appendStringInfoCharMacro(&attribute_buf, c);
/* XXX shouldn't this be done even when encoding is the same? */
if (client_encoding != server_encoding)
{
/* get additional bytes of the char, if any */
s[0] = c;
mblen = pg_encoding_mblen(client_encoding, s);
for (j = 1; j < mblen; j++)
{
c = CopyGetChar();
if (c == EOF)
{
*result = END_OF_FILE;
goto copy_eof;
}
appendStringInfoCharMacro(&attribute_buf, c);
}
}
}
copy_eof:
if (client_encoding != server_encoding)
{
cvt = (char *) pg_client_to_server((unsigned char *) attribute_buf.data,
attribute_buf.len);
if (cvt != attribute_buf.data)
{
/* transfer converted data back to attribute_buf */
attribute_buf.len = 0;
attribute_buf.data[0] = '\0';
appendBinaryStringInfo(&attribute_buf, cvt, strlen(cvt));
}
} }
return attribute_buf.data; return attribute_buf.data;
@ -1917,7 +2019,7 @@ CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo, Oid typelem,
if (fld_size < 0) if (fld_size < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT), (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid size for field %d", column_no))); errmsg("invalid field size")));
/* reset attribute_buf to empty, and load raw data in it */ /* reset attribute_buf to empty, and load raw data in it */
attribute_buf.len = 0; attribute_buf.len = 0;
@ -1944,8 +2046,7 @@ CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo, Oid typelem,
if (attribute_buf.cursor != attribute_buf.len) if (attribute_buf.cursor != attribute_buf.len)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("incorrect binary data format in field %d", errmsg("incorrect binary data format")));
column_no)));
*isnull = false; *isnull = false;
return result; return result;

View File

@ -998,7 +998,7 @@ copy test("........pg.dropped.1........") to stdout;
ERROR: column "........pg.dropped.1........" of relation "test" does not exist ERROR: column "........pg.dropped.1........" of relation "test" does not exist
copy test from stdin; copy test from stdin;
ERROR: extra data after last expected column ERROR: extra data after last expected column
CONTEXT: COPY FROM, line 1 CONTEXT: COPY test, line 1: "10 11 12"
select * from test; select * from test;
b | c b | c
---+--- ---+---

View File

@ -35,17 +35,17 @@ ERROR: column "d" specified more than once
-- missing data: should fail -- missing data: should fail
COPY x from stdin; COPY x from stdin;
ERROR: invalid input syntax for integer: "" ERROR: invalid input syntax for integer: ""
CONTEXT: COPY FROM, line 1 CONTEXT: COPY x, line 1, column a: ""
COPY x from stdin; COPY x from stdin;
ERROR: missing data for column "e" ERROR: missing data for column "e"
CONTEXT: COPY FROM, line 1 CONTEXT: COPY x, line 1: "2000 230 23 23"
COPY x from stdin; COPY x from stdin;
ERROR: missing data for column "e" ERROR: missing data for column "e"
CONTEXT: COPY FROM, line 1 CONTEXT: COPY x, line 1: "2001 231 \N \N"
-- extra data: should fail -- extra data: should fail
COPY x from stdin; COPY x from stdin;
ERROR: extra data after last expected column ERROR: extra data after last expected column
CONTEXT: COPY FROM, line 1 CONTEXT: COPY x, line 1: "2002 232 40 50 60 70 80"
-- various COPY options: delimiters, oids, NULL string -- various COPY options: delimiters, oids, NULL string
COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x'; COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
-- check results of copy in -- check results of copy in

View File

@ -40,7 +40,7 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate
-- Test copy -- Test copy
COPY basictest (testvarchar) FROM stdin; -- fail COPY basictest (testvarchar) FROM stdin; -- fail
ERROR: value too long for type character varying(5) ERROR: value too long for type character varying(5)
CONTEXT: COPY FROM, line 1 CONTEXT: COPY basictest, line 1: "notsoshorttext"
COPY basictest (testvarchar) FROM stdin; COPY basictest (testvarchar) FROM stdin;
select * from basictest; select * from basictest;
testint4 | testtext | testvarchar | testnumeric testint4 | testtext | testvarchar | testnumeric
@ -127,11 +127,11 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy -- Test copy
COPY nulltest FROM stdin; --fail COPY nulltest FROM stdin; --fail
ERROR: domain dcheck does not allow null values ERROR: domain dcheck does not allow null values
CONTEXT: COPY FROM, line 1 CONTEXT: COPY nulltest, line 1: "a b \N d \N"
-- Last row is bad -- Last row is bad
COPY nulltest FROM stdin; COPY nulltest FROM stdin;
ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5" ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5"
CONTEXT: COPY FROM, line 3 CONTEXT: COPY nulltest, line 3: "a b c \N a"
select * from nulltest; select * from nulltest;
col1 | col2 | col3 | col4 | col5 col1 | col2 | col3 | col4 | col5
------+------+------+------+------ ------+------+------+------+------

View File

@ -274,7 +274,7 @@ SELECT '' AS two, * FROM COPY_TBL;
COPY COPY_TBL FROM '@abs_srcdir@/data/constrf.data'; COPY COPY_TBL FROM '@abs_srcdir@/data/constrf.data';
ERROR: new row for relation "copy_tbl" violates check constraint "copy_con" ERROR: new row for relation "copy_tbl" violates check constraint "copy_con"
CONTEXT: COPY FROM, line 2 CONTEXT: COPY copy_tbl, line 2: "7 check failed 6"
SELECT * FROM COPY_TBL; SELECT * FROM COPY_TBL;
x | y | z x | y | z
---+---------------+--- ---+---------------+---