Index: doc/src/sgml/ref/copy.sgml =================================================================== RCS file: /cvsroot/pgsql-server/doc/src/sgml/ref/copy.sgml,v retrieving revision 1.42 diff -c -c -r1.42 copy.sgml *** doc/src/sgml/ref/copy.sgml 15 Apr 2003 13:25:08 -0000 1.42 --- doc/src/sgml/ref/copy.sgml 18 Apr 2003 03:14:05 -0000 *************** *** 289,295 **** otherwise be taken as row or column delimiters. In particular, the following characters must be preceded by a backslash if they appear as part of a column value: backslash itself, ! newline, and the current delimiter character. --- 289,295 ---- otherwise be taken as row or column delimiters. In particular, the following characters must be preceded by a backslash if they appear as part of a column value: backslash itself, ! newline, carriage return, and the current delimiter character. *************** *** 355,370 **** It is strongly recommended that applications generating COPY data convert data newlines and carriage returns to the \n and \r sequences respectively. At present it is ! possible to represent a data carriage return without any special quoting, ! and to represent a data newline by a backslash and newline. However, ! these representations will not be accepted by default in future releases. ! Note that the end of each row is marked by a Unix-style newline ! (\n). Presently, COPY FROM will not behave as ! desired if given a file containing DOS- or Mac-style newlines. ! This is expected to change in future releases. --- 355,370 ---- It is strongly recommended that applications generating COPY data convert data newlines and carriage returns to the \n and \r sequences respectively. At present it is ! possible to represent a data carriage return by a backslash and carriage ! return, and to represent a data newline by a backslash and newline. ! However, these representations might not be accepted in future releases. ! COPY TO will terminate each row with a Unix-style ! newline (\n), or carriage return/newline ! ("\r\n") on MS Windows. COPY FROM can handle lines ! ending with newlines, carriage returns, or carriage return/newlines. *************** *** 393,399 **** 12-byte sequence PGBCOPY\n\377\r\n\0 --- note that the zero byte is a required part of the signature. (The signature is designed to allow easy identification of files that have been munged by a non-8-bit-clean ! transfer. This signature will be changed by newline-translation filters, dropped zero bytes, dropped high bits, or parity changes.) --- 393,399 ---- 12-byte sequence PGBCOPY\n\377\r\n\0 --- note that the zero byte is a required part of the signature. (The signature is designed to allow easy identification of files that have been munged by a non-8-bit-clean ! transfer. This signature will be changed by end-of-line-translation filters, dropped zero bytes, dropped high bits, or parity changes.) Index: src/backend/commands/copy.c =================================================================== RCS file: /cvsroot/pgsql-server/src/backend/commands/copy.c,v retrieving revision 1.191 diff -c -c -r1.191 copy.c *** src/backend/commands/copy.c 4 Apr 2003 20:42:11 -0000 1.191 --- src/backend/commands/copy.c 18 Apr 2003 03:14:51 -0000 *************** *** 49,54 **** --- 49,60 ---- #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7')) #define OCTVALUE(c) ((c) - '0') + #ifndef WIN32 + #define PGEOL "\n" + #else + #define PGEOL "\r\n" + #endif + /* * Represents the type of data returned by CopyReadAttribute() */ *************** *** 70,82 **** static void CopyAttributeOut(FILE *fp, char *string, char *delim); static List *CopyGetAttnums(Relation rel, List *attnamelist); ! static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0"; /* * Static communication variables ... pretty grotty, but COPY has * never been reentrant... */ int copy_lineno = 0; /* exported for use by elog() -- dz */ static bool fe_eof; /* --- 76,91 ---- static void CopyAttributeOut(FILE *fp, char *string, char *delim); static List *CopyGetAttnums(Relation rel, List *attnamelist); ! /* 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 * never been reentrant... */ int copy_lineno = 0; /* exported for use by elog() -- dz */ + bool has_crnl = false; + static bool fe_eof; /* *************** *** 504,510 **** else if (!is_from) { if (!binary) ! CopySendData("\\.\n", 3, fp); if (IsUnderPostmaster) pq_endcopyout(false); } --- 513,522 ---- else if (!is_from) { if (!binary) ! { ! CopySendString("\\.", fp); ! CopySendString(fp ? PGEOL : "\n", fp); ! } if (IsUnderPostmaster) pq_endcopyout(false); } *************** *** 589,595 **** int32 tmp; /* Signature */ ! CopySendData((char *) BinarySignature, 12, fp); /* Integer layout field */ tmp = 0x01020304; CopySendData(&tmp, sizeof(int32), fp); --- 601,607 ---- int32 tmp; /* Signature */ ! CopySendData((char *) BinarySignature, sizeof(BinarySignature), fp); /* Integer layout field */ tmp = 0x01020304; CopySendData(&tmp, sizeof(int32), fp); *************** *** 725,731 **** } if (!binary) ! CopySendChar('\n', fp); MemoryContextSwitchTo(oldcontext); } --- 737,743 ---- } if (!binary) ! CopySendString(fp ? PGEOL : "\n", fp); MemoryContextSwitchTo(oldcontext); } *************** *** 906,912 **** /* Signature */ CopyGetData(readSig, 12, fp); ! if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, 12) != 0) elog(ERROR, "COPY BINARY: file signature not recognized"); /* Integer layout field */ CopyGetData(&tmp, sizeof(int32), fp); --- 918,924 ---- /* Signature */ CopyGetData(readSig, 12, fp); ! if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, sizeof(BinarySignature)) != 0) elog(ERROR, "COPY BINARY: file signature not recognized"); /* Integer layout field */ CopyGetData(&tmp, sizeof(int32), fp); *************** *** 937,942 **** --- 949,955 ---- nulls = (char *) palloc(num_phys_attrs * sizeof(char)); copy_lineno = 0; + has_crnl = false; fe_eof = false; /* Make room for a PARAM_EXEC value for domain constraint checks */ *************** *** 1350,1357 **** --- 1363,1403 ---- *result = END_OF_FILE; goto copy_eof; } + if (c == '\r') + { + /* + * Do \r\n -> \n mapping only if first line has \r\n. + * + * This prevents us from silently discarding literal carriage + * return data values that just happen to be at the end of the line. + * Other literal carrige return and newline data values will just + * throw an error because the next line will have the incorrect number + * of data values. + */ + if (copy_lineno == 1 || has_crnl) + { + int c2 = CopyPeekChar(fp); + if (c2 == '\n') + { + CopyDonePeek(fp, c2, true); /* eat newline */ + has_crnl = true; + } + else + { + if (has_crnl) + elog(ERROR, "CopyReadAttribute: Literal carriage return data value\n" + "found in file containing carriage return/newline termination, use \\r"); + CopyDonePeek(fp, c2, false); + } + } + *result = END_OF_LINE; + break; + } if (c == '\n') { + if (has_crnl) + elog(ERROR, "CopyReadAttribute: Literal newline data value found in file\n" + "containing carriage return/newline termination, use \\n"); *result = END_OF_LINE; break; } *************** *** 1441,1451 **** c = '\v'; break; case '.': c = CopyGetChar(fp); ! if (c != '\n') ! elog(ERROR, "CopyReadAttribute: end of record marker corrupted"); *result = END_OF_FILE; goto copy_eof; } } appendStringInfoCharMacro(&attribute_buf, c); --- 1487,1506 ---- c = '\v'; break; case '.': + if (has_crnl) + { + c = CopyGetChar(fp); + if (c != '\r') + elog(ERROR, "Literal carriage return end-of-copy value detected in file containing carriage\n" + "return/newline termination, use \\r"); + } c = CopyGetChar(fp); ! if (c != '\n' && (c != '\r' || has_crnl)) ! elog(ERROR, "CopyReadAttribute: end of record marker corrupt"); *result = END_OF_FILE; goto copy_eof; + + /* Default, fall through with whatever character was just escaped. */ } } appendStringInfoCharMacro(&attribute_buf, c);