Skip to content

Commit

Permalink
Rename COPY option from SAVE_ERROR_TO to ON_ERROR
Browse files Browse the repository at this point in the history
The option names now are "stop" (default) and "ignore".  The future options
could be "file 'filename.log'" and "table 'tablename'".

Discussion: https://postgr.es/m/20240117.164859.2242646601795501168.horikyota.ntt%40gmail.com
Author: Jian He
Reviewed-by: Atsushi Torikoshi
  • Loading branch information
akorotkov committed Jan 19, 2024
1 parent dd0a0cf commit b725b7e
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 78 deletions.
20 changes: 10 additions & 10 deletions doc/src/sgml/ref/copy.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
SAVE_ERROR_TO '<replaceable class="parameter">location</replaceable>'
ON_ERROR '<replaceable class="parameter">error_action</replaceable>'
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
</synopsis>
</refsynopsisdiv>
Expand Down Expand Up @@ -375,20 +375,20 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>

<varlistentry>
<term><literal>SAVE_ERROR_TO</literal></term>
<term><literal>ON_ERROR</literal></term>
<listitem>
<para>
Specifies to save error information to <replaceable class="parameter">
location</replaceable> when there is malformed data in the input.
Currently, only <literal>error</literal> (default) and <literal>none</literal>
Specifies which <replaceable class="parameter">
error_action</replaceable> to perform when there is malformed data in the input.
Currently, only <literal>stop</literal> (default) and <literal>ignore</literal>
values are supported.
If the <literal>error</literal> value is specified,
If the <literal>stop</literal> value is specified,
<command>COPY</command> stops operation at the first error.
If the <literal>none</literal> value is specified,
If the <literal>ignore</literal> value is specified,
<command>COPY</command> skips malformed data and continues copying data.
The option is allowed only in <command>COPY FROM</command>.
The <literal>none</literal> value is allowed only when
not using <literal>binary</literal> format.
Only <literal>stop</literal> value is allowed when
using <literal>binary</literal> format.
</para>
</listitem>
</varlistentry>
Expand Down Expand Up @@ -577,7 +577,7 @@ COPY <replaceable class="parameter">count</replaceable>

<para>
<command>COPY</command> stops operation at the first error when
<literal>SAVE_ERROR_TO</literal> is not specified. This
<literal>ON_ERROR</literal> is not specified. This
should not lead to problems in the event of a <command>COPY
TO</command>, but the target table will already have received
earlier rows in a <command>COPY FROM</command>. These rows will not
Expand Down
38 changes: 19 additions & 19 deletions src/backend/commands/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,39 +395,39 @@ defGetCopyHeaderChoice(DefElem *def, bool is_from)
}

/*
* Extract a CopySaveErrorToChoice value from a DefElem.
* Extract a CopyOnErrorChoice value from a DefElem.
*/
static CopySaveErrorToChoice
defGetCopySaveErrorToChoice(DefElem *def, ParseState *pstate, bool is_from)
static CopyOnErrorChoice
defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
{
char *sval;

if (!is_from)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("COPY SAVE_ERROR_TO cannot be used with COPY TO"),
errmsg("COPY ON_ERROR cannot be used with COPY TO"),
parser_errposition(pstate, def->location)));

/*
* If no parameter value given, assume the default value.
*/
if (def->arg == NULL)
return COPY_SAVE_ERROR_TO_ERROR;
return COPY_ON_ERROR_STOP;

/*
* Allow "error", or "none" values.
* Allow "stop", or "ignore" values.
*/
sval = defGetString(def);
if (pg_strcasecmp(sval, "error") == 0)
return COPY_SAVE_ERROR_TO_ERROR;
if (pg_strcasecmp(sval, "none") == 0)
return COPY_SAVE_ERROR_TO_NONE;
if (pg_strcasecmp(sval, "stop") == 0)
return COPY_ON_ERROR_STOP;
if (pg_strcasecmp(sval, "ignore") == 0)
return COPY_ON_ERROR_IGNORE;

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("COPY save_error_to \"%s\" not recognized", sval),
errmsg("COPY ON_ERROR \"%s\" not recognized", sval),
parser_errposition(pstate, def->location)));
return COPY_SAVE_ERROR_TO_ERROR; /* keep compiler quiet */
return COPY_ON_ERROR_STOP; /* keep compiler quiet */
}

/*
Expand Down Expand Up @@ -455,7 +455,7 @@ ProcessCopyOptions(ParseState *pstate,
bool format_specified = false;
bool freeze_specified = false;
bool header_specified = false;
bool save_error_to_specified = false;
bool on_error_specified = false;
ListCell *option;

/* Support external use for option sanity checking */
Expand Down Expand Up @@ -608,12 +608,12 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
else if (strcmp(defel->defname, "save_error_to") == 0)
else if (strcmp(defel->defname, "on_error") == 0)
{
if (save_error_to_specified)
if (on_error_specified)
errorConflictingDefElem(defel, pstate);
save_error_to_specified = true;
opts_out->save_error_to = defGetCopySaveErrorToChoice(defel, pstate, is_from);
on_error_specified = true;
opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
}
else
ereport(ERROR,
Expand Down Expand Up @@ -642,10 +642,10 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot specify DEFAULT in BINARY mode")));

if (opts_out->binary && opts_out->save_error_to != COPY_SAVE_ERROR_TO_ERROR)
if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot specify SAVE_ERROR_TO in BINARY mode")));
errmsg("only ON_ERROR STOP is allowed in BINARY mode")));

/* Set defaults for omitted options */
if (!opts_out->delim)
Expand Down
20 changes: 10 additions & 10 deletions src/backend/commands/copyfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ CopyFrom(CopyFromState cstate)
Assert(cstate->rel);
Assert(list_length(cstate->range_table) == 1);

if (cstate->opts.save_error_to != COPY_SAVE_ERROR_TO_ERROR)
if (cstate->opts.on_error != COPY_ON_ERROR_STOP)
Assert(cstate->escontext);

/*
Expand Down Expand Up @@ -996,14 +996,14 @@ CopyFrom(CopyFromState cstate)
if (!NextCopyFrom(cstate, econtext, myslot->tts_values, myslot->tts_isnull))
break;

if (cstate->opts.save_error_to != COPY_SAVE_ERROR_TO_ERROR &&
if (cstate->opts.on_error != COPY_ON_ERROR_STOP &&
cstate->escontext->error_occurred)
{
/*
* Soft error occured, skip this tuple and save error information
* according to SAVE_ERROR_TO.
* Soft error occured, skip this tuple and deal with error
* information according to ON_ERROR.
*/
if (cstate->opts.save_error_to == COPY_SAVE_ERROR_TO_NONE)
if (cstate->opts.on_error == COPY_ON_ERROR_IGNORE)

/*
* Just make ErrorSaveContext ready for the next NextCopyFrom.
Expand Down Expand Up @@ -1307,7 +1307,7 @@ CopyFrom(CopyFromState cstate)
/* Done, clean up */
error_context_stack = errcallback.previous;

if (cstate->opts.save_error_to != COPY_SAVE_ERROR_TO_ERROR &&
if (cstate->opts.on_error != COPY_ON_ERROR_STOP &&
cstate->num_errors > 0)
ereport(NOTICE,
errmsg_plural("%llu row was skipped due to data type incompatibility",
Expand Down Expand Up @@ -1450,18 +1450,18 @@ BeginCopyFrom(ParseState *pstate,
}
}

/* Set up soft error handler for SAVE_ERROR_TO */
if (cstate->opts.save_error_to != COPY_SAVE_ERROR_TO_ERROR)
/* Set up soft error handler for ON_ERROR */
if (cstate->opts.on_error != COPY_ON_ERROR_STOP)
{
cstate->escontext = makeNode(ErrorSaveContext);
cstate->escontext->type = T_ErrorSaveContext;
cstate->escontext->error_occurred = false;

/*
* Currently we only support COPY_SAVE_ERROR_TO_NONE. We'll add other
* Currently we only support COPY_ON_ERROR_IGNORE. We'll add other
* options later
*/
if (cstate->opts.save_error_to == COPY_SAVE_ERROR_TO_NONE)
if (cstate->opts.on_error == COPY_ON_ERROR_IGNORE)
cstate->escontext->details_wanted = false;
}
else
Expand Down
6 changes: 5 additions & 1 deletion src/backend/commands/copyfromparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,11 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,

values[m] = ExecEvalExpr(defexprs[m], econtext, &nulls[m]);
}
/* If SAVE_ERROR_TO is specified, skip rows with soft errors */

/*
* If ON_ERROR is specified with IGNORE, skip rows with soft
* errors
*/
else if (!InputFunctionCallSafe(&in_functions[m],
string,
typioparams[m],
Expand Down
8 changes: 4 additions & 4 deletions src/bin/psql/tab-complete.c
Original file line number Diff line number Diff line change
Expand Up @@ -2899,15 +2899,15 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
"HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING", "DEFAULT",
"SAVE_ERROR_TO");
"ON_ERROR");

/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "FORMAT"))
COMPLETE_WITH("binary", "csv", "text");

/* Complete COPY <sth> FROM filename WITH (SAVE_ERROR_TO */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "SAVE_ERROR_TO"))
COMPLETE_WITH("error", "none");
/* Complete COPY <sth> FROM filename WITH (ON_ERROR */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "ON_ERROR"))
COMPLETE_WITH("stop", "ignore");

/* Complete COPY <sth> FROM <sth> WITH (<options>) */
else if (Matches("COPY|\\copy", MatchAny, "FROM", MatchAny, "WITH", MatchAny))
Expand Down
10 changes: 5 additions & 5 deletions src/include/commands/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ typedef enum CopyHeaderChoice
* Represents where to save input processing errors. More values to be added
* in the future.
*/
typedef enum CopySaveErrorToChoice
typedef enum CopyOnErrorChoice
{
COPY_SAVE_ERROR_TO_ERROR = 0, /* immediately throw errors */
COPY_SAVE_ERROR_TO_NONE, /* ignore errors */
} CopySaveErrorToChoice;
COPY_ON_ERROR_STOP = 0, /* immediately throw errors, default */
COPY_ON_ERROR_IGNORE, /* ignore errors */
} CopyOnErrorChoice;

/*
* A struct to hold COPY options, in a parsed form. All of these are related
Expand Down Expand Up @@ -72,7 +72,7 @@ typedef struct CopyFormatOptions
bool force_null_all; /* FORCE_NULL *? */
bool *force_null_flags; /* per-column CSV FN flags */
bool convert_selectively; /* do selective binary conversion? */
CopySaveErrorToChoice save_error_to; /* where to save error information */
CopyOnErrorChoice on_error; /* what to do when error happened */
List *convert_select; /* list of column names (can be NIL) */
} CopyFormatOptions;

Expand Down
36 changes: 18 additions & 18 deletions src/test/regress/expected/copy2.out
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,21 @@ COPY x from stdin (encoding 'sql_ascii', encoding 'sql_ascii');
ERROR: conflicting or redundant options
LINE 1: COPY x from stdin (encoding 'sql_ascii', encoding 'sql_ascii...
^
COPY x from stdin (save_error_to none,save_error_to none);
COPY x from stdin (on_error ignore, on_error ignore);
ERROR: conflicting or redundant options
LINE 1: COPY x from stdin (save_error_to none,save_error_to none);
^
LINE 1: COPY x from stdin (on_error ignore, on_error ignore);
^
-- incorrect options
COPY x to stdin (format BINARY, delimiter ',');
ERROR: cannot specify DELIMITER in BINARY mode
COPY x to stdin (format BINARY, null 'x');
ERROR: cannot specify NULL in BINARY mode
COPY x from stdin (format BINARY, save_error_to none);
ERROR: cannot specify SAVE_ERROR_TO in BINARY mode
COPY x to stdin (save_error_to none);
ERROR: COPY SAVE_ERROR_TO cannot be used with COPY TO
LINE 1: COPY x to stdin (save_error_to none);
^
COPY x from stdin (format BINARY, on_error ignore);
ERROR: only ON_ERROR STOP is allowed in BINARY mode
COPY x from stdin (on_error unsupported);
ERROR: COPY ON_ERROR "unsupported" not recognized
LINE 1: COPY x from stdin (on_error unsupported);
^
COPY x to stdin (format TEXT, force_quote(a));
ERROR: COPY FORCE_QUOTE requires CSV mode
COPY x from stdin (format CSV, force_quote(a));
Expand All @@ -104,9 +104,9 @@ COPY x to stdout (format TEXT, force_null(a));
ERROR: COPY FORCE_NULL requires CSV mode
COPY x to stdin (format CSV, force_null(a));
ERROR: COPY FORCE_NULL cannot be used with COPY TO
COPY x to stdin (format BINARY, save_error_to unsupported);
ERROR: COPY SAVE_ERROR_TO cannot be used with COPY TO
LINE 1: COPY x to stdin (format BINARY, save_error_to unsupported);
COPY x to stdin (format BINARY, on_error unsupported);
ERROR: COPY ON_ERROR cannot be used with COPY TO
LINE 1: COPY x to stdin (format BINARY, on_error unsupported);
^
-- too many columns in column list: should fail
COPY x (a, b, c, d, e, d, c) from stdin;
Expand Down Expand Up @@ -724,12 +724,12 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)

COMMIT;
-- tests for SAVE_ERROR_TO option
-- tests for on_error option
CREATE TABLE check_ign_err (n int, m int[], k int);
COPY check_ign_err FROM STDIN WITH (save_error_to error);
COPY check_ign_err FROM STDIN WITH (on_error stop);
ERROR: invalid input syntax for type integer: "a"
CONTEXT: COPY check_ign_err, line 2, column n: "a"
COPY check_ign_err FROM STDIN WITH (save_error_to none);
COPY check_ign_err FROM STDIN WITH (on_error ignore);
NOTICE: 4 rows were skipped due to data type incompatibility
SELECT * FROM check_ign_err;
n | m | k
Expand All @@ -740,15 +740,15 @@ SELECT * FROM check_ign_err;

-- test datatype error that can't be handled as soft: should fail
CREATE TABLE hard_err(foo widget);
COPY hard_err FROM STDIN WITH (save_error_to none);
COPY hard_err FROM STDIN WITH (on_error ignore);
ERROR: invalid input syntax for type widget: "1"
CONTEXT: COPY hard_err, line 1, column foo: "1"
-- test missing data: should fail
COPY check_ign_err FROM STDIN WITH (save_error_to none);
COPY check_ign_err FROM STDIN WITH (on_error ignore);
ERROR: missing data for column "k"
CONTEXT: COPY check_ign_err, line 1: "1 {1}"
-- test extra data: should fail
COPY check_ign_err FROM STDIN WITH (save_error_to none);
COPY check_ign_err FROM STDIN WITH (on_error ignore);
ERROR: extra data after last expected column
CONTEXT: COPY check_ign_err, line 1: "1 {1} 3 abc"
-- clean up
Expand Down
Loading

0 comments on commit b725b7e

Please sign in to comment.