Commit c3b7d2d5 authored by Bruce Momjian's avatar Bruce Momjian

Allow \r and \r\n termination for COPY files.

Output \r\n termination on Win32.

Disallow literal carriage return as a data value,
backslash-carriage-return and \r still allowed.

Doc changes already committed.
parent 9c48cae3
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.193 2003/04/19 19:55:37 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.194 2003/04/19 20:36:03 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,6 +50,13 @@ ...@@ -50,6 +50,13 @@
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7')) #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0') #define OCTVALUE(c) ((c) - '0')
/* Default line termination */
#ifndef WIN32
#define PGEOL "\n"
#else
#define PGEOL "\r\n"
#endif
/* /*
* Represents the different source/dest cases we need to worry about at * Represents the different source/dest cases we need to worry about at
* the bottom level * the bottom level
...@@ -71,9 +78,21 @@ typedef enum CopyReadResult ...@@ -71,9 +78,21 @@ typedef enum CopyReadResult
END_OF_FILE END_OF_FILE
} CopyReadResult; } CopyReadResult;
/*
* Represents the end-of-line terminator of the input
*/
typedef enum EolType
{
EOL_UNKNOWN,
EOL_NL,
EOL_CR,
EOL_CRNL
} EolType;
/* non-export function prototypes */ /* non-export function prototypes */
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print); bool pipe, char *delim, char *null_print);
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print); char *delim, char *null_print);
static Oid GetInputFunction(Oid type); static Oid GetInputFunction(Oid type);
...@@ -82,7 +101,8 @@ static char *CopyReadAttribute(const char *delim, CopyReadResult *result); ...@@ -82,7 +101,8 @@ static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
static void CopyAttributeOut(char *string, char *delim); static void CopyAttributeOut(char *string, char *delim);
static List *CopyGetAttnums(Relation rel, List *attnamelist); static List *CopyGetAttnums(Relation rel, List *attnamelist);
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0"; /* The trailing null is part of the signature */
static const char BinarySignature[] = "PGBCOPY\n\377\r\n";
/* /*
* Static communication variables ... pretty grotty, but COPY has * Static communication variables ... pretty grotty, but COPY has
...@@ -94,6 +114,7 @@ static CopyDest copy_dest; ...@@ -94,6 +114,7 @@ static CopyDest copy_dest;
static FILE *copy_file; /* if copy_dest == COPY_FILE */ static FILE *copy_file; /* if copy_dest == COPY_FILE */
static StringInfo copy_msgbuf; /* if copy_dest == COPY_NEW_FE */ static StringInfo copy_msgbuf; /* 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;
/* /*
* These static variables are used to avoid incurring overhead for each * These static variables are used to avoid incurring overhead for each
...@@ -181,7 +202,10 @@ static void ...@@ -181,7 +202,10 @@ static void
SendCopyEnd(bool binary, bool pipe) SendCopyEnd(bool binary, bool pipe)
{ {
if (!binary) if (!binary)
CopySendData("\\.\n", 3); {
CopySendString("\\.");
CopySendString(!pipe ? PGEOL : "\n");
}
pq_endcopyout(false); pq_endcopyout(false);
} }
...@@ -674,7 +698,7 @@ DoCopy(const CopyStmt *stmt) ...@@ -674,7 +698,7 @@ DoCopy(const CopyStmt *stmt)
elog(ERROR, "COPY: %s is a directory", filename); elog(ERROR, "COPY: %s is a directory", filename);
} }
} }
CopyTo(rel, attnumlist, binary, oids, delim, null_print); CopyTo(rel, attnumlist, binary, oids, pipe, delim, null_print);
} }
if (!pipe) if (!pipe)
...@@ -697,7 +721,7 @@ DoCopy(const CopyStmt *stmt) ...@@ -697,7 +721,7 @@ DoCopy(const CopyStmt *stmt)
* Copy from relation TO file. * Copy from relation TO file.
*/ */
static void static void
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
char *delim, char *null_print) char *delim, char *null_print)
{ {
HeapTuple tuple; HeapTuple tuple;
...@@ -762,7 +786,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -762,7 +786,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
int32 tmp; int32 tmp;
/* Signature */ /* Signature */
CopySendData((char *) BinarySignature, 12); CopySendData((char *) BinarySignature, sizeof(BinarySignature));
/* Integer layout field */ /* Integer layout field */
tmp = 0x01020304; tmp = 0x01020304;
CopySendData(&tmp, sizeof(int32)); CopySendData(&tmp, sizeof(int32));
...@@ -895,7 +919,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -895,7 +919,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
} }
if (!binary) if (!binary)
CopySendChar('\n'); CopySendString(!pipe ? PGEOL : "\n");
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
...@@ -1076,7 +1100,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1076,7 +1100,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* Signature */ /* Signature */
CopyGetData(readSig, 12); CopyGetData(readSig, 12);
if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0) if (CopyGetEof() || memcmp(readSig, BinarySignature,
sizeof(BinarySignature)) != 0)
elog(ERROR, "COPY BINARY: file signature not recognized"); elog(ERROR, "COPY BINARY: file signature not recognized");
/* Integer layout field */ /* Integer layout field */
CopyGetData(&tmp, sizeof(int32)); CopyGetData(&tmp, sizeof(int32));
...@@ -1108,6 +1133,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1108,6 +1133,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* Initialize static variables */ /* Initialize static variables */
copy_lineno = 0; copy_lineno = 0;
eol_type = EOL_UNKNOWN;
fe_eof = false; fe_eof = false;
/* Make room for a PARAM_EXEC value for domain constraint checks */ /* Make room for a PARAM_EXEC value for domain constraint checks */
...@@ -1520,8 +1546,44 @@ CopyReadAttribute(const char *delim, CopyReadResult *result) ...@@ -1520,8 +1546,44 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
*result = END_OF_FILE; *result = END_OF_FILE;
goto copy_eof; goto copy_eof;
} }
if (c == '\r')
{
if (eol_type == EOL_NL)
elog(ERROR, "CopyReadAttribute: Literal carriage return data value\n"
"found in input that has newline termination; use \\r");
/* Check for \r\n on first line, _and_ handle \r\n. */
if (copy_lineno == 1 || eol_type == EOL_CRNL)
{
int c2 = CopyPeekChar();
if (c2 == '\n')
{
CopyDonePeek(c2, true); /* eat newline */
eol_type = EOL_CRNL;
}
else
{
/* found \r, but no \n */
if (eol_type == EOL_CRNL)
elog(ERROR, "CopyReadAttribute: Literal carriage return data value\n"
"found in input that has carriage return/newline termination; use \\r");
/* if we got here, it is the first line and we didn't get \n, so put it back */
CopyDonePeek(c2, false);
eol_type = EOL_CR;
}
}
*result = END_OF_LINE;
break;
}
if (c == '\n') if (c == '\n')
{ {
if (eol_type == EOL_CRNL)
elog(ERROR, "CopyReadAttribute: Literal newline data value found in input\n"
"that has carriage return/newline termination; use \\n");
if (eol_type == EOL_CR)
elog(ERROR, "CopyReadAttribute: Literal newline data value found in input\n"
"that has carriage return termination; use \\n");
eol_type = EOL_NL;
*result = END_OF_LINE; *result = END_OF_LINE;
break; break;
} }
...@@ -1611,9 +1673,20 @@ CopyReadAttribute(const char *delim, CopyReadResult *result) ...@@ -1611,9 +1673,20 @@ CopyReadAttribute(const char *delim, CopyReadResult *result)
c = '\v'; c = '\v';
break; break;
case '.': case '.':
if (eol_type == EOL_CRNL)
{
c = CopyGetChar();
if (c == '\n')
elog(ERROR, "CopyReadAttribute: end-of-copy termination does not match previous input");
if (c != '\r')
elog(ERROR, "CopyReadAttribute: end-of-copy marker corrupt");
}
c = CopyGetChar(); c = CopyGetChar();
if (c != '\n') if (c != '\r' && c != '\n')
elog(ERROR, "CopyReadAttribute: end of record marker corrupted"); elog(ERROR, "CopyReadAttribute: end-of-copy marker corrupt");
if (((eol_type == EOL_NL || eol_type == EOL_CRNL) && c != '\n') ||
(eol_type == EOL_CR && c != '\r'))
elog(ERROR, "CopyReadAttribute: end-of-copy termination does not match previous input");
/* /*
* In protocol version 3, we should ignore anything after * In protocol version 3, we should ignore anything after
* \. up to the protocol end of copy data. (XXX maybe * \. up to the protocol end of copy data. (XXX maybe
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment