diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0e604b7..c129979 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1121,6 +1121,10 @@ ProcessCopyOptions(CopyState cstate, } else if (strcmp(defel->defname, "encoding") == 0) { + if (cstate->binary) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot specify ENCODING in BINARY mode"))); if (cstate->file_encoding >= 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -1153,10 +1157,18 @@ ProcessCopyOptions(CopyState cstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot specify NULL in BINARY mode"))); + if (cstate->binary && cstate->escape) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot specify ESCAPE in BINARY mode"))); + /* Set defaults for omitted options */ if (!cstate->delim) cstate->delim = cstate->csv_mode ? "," : "\t"; + if (!cstate->csv_mode && !cstate->escape) + cstate->escape = "\\"; + if (!cstate->null_print) cstate->null_print = cstate->csv_mode ? "" : "\\N"; cstate->null_print_len = strlen(cstate->null_print); @@ -1227,16 +1239,15 @@ ProcessCopyOptions(CopyState cstate, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY delimiter and quote must be different"))); - /* Check escape */ - if (!cstate->csv_mode && cstate->escape != NULL) - ereport(ERROR, + if (cstate->csv_mode && strlen(cstate->escape) != 1) + ereport(ERROR,de (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY escape available only in CSV mode"))); + errmsg("COPY escape must be a single one-byte character in CSV mode"))); - if (cstate->csv_mode && strlen(cstate->escape) != 1) + if (strlen(cstate->escape) > 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY escape must be a single one-byte character"))); + errmsg("COPY escape must be empty or a single one-byte charactor"))); /* Check force_quote */ if (!cstate->csv_mode && (cstate->force_quote || cstate->force_quote_all)) @@ -3596,6 +3607,7 @@ static int CopyReadAttributesText(CopyState cstate) { char delimc = cstate->delim[0]; + char escapec = cstate->escape[0]; int fieldno; char *output_ptr; char *cur_ptr; @@ -3677,7 +3689,7 @@ CopyReadAttributesText(CopyState cstate) found_delim = true; break; } - if (c == '\\') + if (c == escapec) { if (cur_ptr >= line_end_ptr) break; @@ -4055,6 +4067,7 @@ CopyAttributeOutText(CopyState cstate, char *string) char *start; char c; char delimc = cstate->delim[0]; + char escapec = cstate->escape[0]; if (cstate->need_transcoding) ptr = pg_server_to_any(string, strlen(string), cstate->file_encoding); @@ -4119,14 +4132,14 @@ CopyAttributeOutText(CopyState cstate, char *string) } /* if we get here, we need to convert the control char */ DUMPSOFAR(); - CopySendChar(cstate, '\\'); + CopySendChar(cstate, escapec); CopySendChar(cstate, c); start = ++ptr; /* do not include char in next run */ } - else if (c == '\\' || c == delimc) + else if (c == escapec || c == delimc) { DUMPSOFAR(); - CopySendChar(cstate, '\\'); + CopySendChar(cstate, escapec); start = ptr++; /* we include char in next run */ } else if (IS_HIGHBIT_SET(c)) @@ -4179,14 +4192,14 @@ CopyAttributeOutText(CopyState cstate, char *string) } /* if we get here, we need to convert the control char */ DUMPSOFAR(); - CopySendChar(cstate, '\\'); + CopySendChar(cstate, escapec); CopySendChar(cstate, c); start = ++ptr; /* do not include char in next run */ } - else if (c == '\\' || c == delimc) + else if (c == escapec || c == delimc) { DUMPSOFAR(); - CopySendChar(cstate, '\\'); + CopySendChar(cstate, escapec); start = ptr++; /* we include char in next run */ } else diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source index cb13606..be6dc9e 100644 --- a/src/test/regress/input/copy.source +++ b/src/test/regress/input/copy.source @@ -133,3 +133,19 @@ this is just a line full of junk that would error out if parsed \. copy copytest3 to stdout csv header; + +CREATE TEMP TABLE t (d json); +COPY t from stdin; +{"key": "value"} +\. +COPY t from stdin; +{"key": "val\"ue"} +\. +COPY t from stdin with escape ''; +{"key": "val\"ue"} +\. +COPY t from stdin with escape ''; +{"key": "value"} +\. +DROP TABLE t; + diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source index b7e372d..ced39eb 100644 --- a/src/test/regress/output/copy.source +++ b/src/test/regress/output/copy.source @@ -95,3 +95,13 @@ copy copytest3 to stdout csv header; c1,"col with , comma","col with "" quote" 1,a,1 2,b,2 +CREATE TEMP TABLE t (d json); +COPY t from stdin; +COPY t from stdin; +ERROR: invalid input syntax for type json +DETAIL: Token "ue" is invalid. +CONTEXT: JSON data, line 1: {"key": "val"ue... +COPY t, line 1, column d: "{"key": "val"ue"}" +COPY t from stdin with escape ''; +COPY t from stdin with escape ''; +DROP TABLE t;