Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
MasaoFujii committed Sep 24, 2024
1 parent 62ddf7e commit 4550bd8
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 0 deletions.
20 changes: 20 additions & 0 deletions doc/src/sgml/ref/copy.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
ON_ERROR <replaceable class="parameter">error_action</replaceable>
REJECT_LIMIT { <replaceable class="parameter">integer</replaceable> }
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
LOG_VERBOSITY <replaceable class="parameter">verbosity</replaceable>
</synopsis>
Expand Down Expand Up @@ -411,6 +412,25 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</listitem>
</varlistentry>

<varlistentry>
<term><literal>REJECT_LIMIT</literal></term>
<listitem>
<para>
When a positive integer value is specified, <command>COPY</command> limits
the maximum tolerable number of errors while converting a column's input
value into its data type.
If input data caused more errors than the specified value, entire
<command>COPY</command> fails.
Otherwise, <command>COPY</command> discards the input row and continues
with the next one.
This option must be used with <literal>ON_ERROR</literal> to be set to
<literal>ignore</literal>.
Just setting <literal>ON_ERROR</literal> to <literal>ignore</literal>
tolerates unlimited number of errors.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term><literal>ENCODING</literal></term>
<listitem>
Expand Down
41 changes: 41 additions & 0 deletions src/backend/commands/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,30 @@ defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
return COPY_ON_ERROR_STOP; /* keep compiler quiet */
}

/*
* Extract REJECT_LIMIT value from a DefElem.
*/
static int64
defGetCopyRejectLimitOptions(DefElem *def)
{
int64 reject_limit;

if (nodeTag(def->arg) == T_Integer)
{
reject_limit = defGetInt64(def);
if (reject_limit <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("number for REJECT_LIMIT must be greater than zero")));
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("value for REJECT_LIMIT must be positive integer")));

return reject_limit;
}

/*
* Extract a CopyLogVerbosityChoice value from a DefElem.
*/
Expand Down Expand Up @@ -470,6 +494,7 @@ ProcessCopyOptions(ParseState *pstate,
bool header_specified = false;
bool on_error_specified = false;
bool log_verbosity_specified = false;
bool reject_limit_specified = false;
ListCell *option;

/* Support external use for option sanity checking */
Expand Down Expand Up @@ -636,6 +661,13 @@ ProcessCopyOptions(ParseState *pstate,
log_verbosity_specified = true;
opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
}
else if (strcmp(defel->defname, "reject_limit") == 0)
{
if (reject_limit_specified)
errorConflictingDefElem(defel, pstate);
reject_limit_specified = true;
opts_out->reject_limit = defGetCopyRejectLimitOptions(defel);
}
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
Expand Down Expand Up @@ -669,6 +701,15 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("only ON_ERROR STOP is allowed in BINARY mode")));

if (opts_out->reject_limit && !opts_out->on_error)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
/*- translator: first and second %s are the names of COPY
* option, e.g. ON_ERROR, thrid is the value of the COPY option,
* e.g. IGNORE */
errmsg("COPY %s requires %s to be set to %s",
"REJECT_LIMIT", "ON_ERROR", "IGNORE")));

/* Set defaults for omitted options */
if (!opts_out->delim)
opts_out->delim = opts_out->csv_mode ? "," : "\t";
Expand Down
5 changes: 5 additions & 0 deletions src/backend/commands/copyfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,11 @@ CopyFrom(CopyFromState cstate)
pgstat_progress_update_param(PROGRESS_COPY_TUPLES_SKIPPED,
++skipped);

if (cstate->opts.reject_limit && skipped > cstate->opts.reject_limit)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("skipped more than REJECT_LIMIT rows: \"%lld\",",
(long long) cstate->opts.reject_limit)));
continue;
}

Expand Down
1 change: 1 addition & 0 deletions src/include/commands/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ typedef struct CopyFormatOptions
bool convert_selectively; /* do selective binary conversion? */
CopyOnErrorChoice on_error; /* what to do when error happened */
CopyLogVerbosityChoice log_verbosity; /* verbosity of logged messages */
int64 reject_limit; /* maximum tolerable number of errors */
List *convert_select; /* list of column names (can be NIL) */
} CopyFormatOptions;

Expand Down
10 changes: 10 additions & 0 deletions src/test/regress/expected/copy2.out
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ COPY x to stdout (log_verbosity unsupported);
ERROR: COPY LOG_VERBOSITY "unsupported" not recognized
LINE 1: COPY x to stdout (log_verbosity unsupported);
^
COPY x from stdin with (reject_limit 1);
ERROR: COPY REJECT_LIMIT requires ON_ERROR to be set to IGNORE
COPY x from stdin with (on_error ignore, reject_limit 0);
ERROR: number for REJECT_LIMIT must be greater than zero
-- too many columns in column list: should fail
COPY x (a, b, c, d, e, d, c) from stdin;
ERROR: column "d" specified more than once
Expand Down Expand Up @@ -789,6 +793,12 @@ CONTEXT: COPY check_ign_err, line 1: "1 {1}"
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"
-- tests for reject_limit option
COPY check_ign_err FROM STDIN WITH (on_error ignore, reject_limit 3);
ERROR: skipped more than REJECT_LIMIT rows: "3",
CONTEXT: COPY check_ign_err, line 5, column n: ""
COPY check_ign_err FROM STDIN WITH (on_error ignore, reject_limit 4);
NOTICE: 4 rows were skipped due to data type incompatibility
-- clean up
DROP TABLE forcetest;
DROP TABLE vistest;
Expand Down
21 changes: 21 additions & 0 deletions src/test/regress/sql/copy2.sql
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ COPY x to stdout (format TEXT, force_null(a));
COPY x to stdin (format CSV, force_null(a));
COPY x to stdin (format BINARY, on_error unsupported);
COPY x to stdout (log_verbosity unsupported);
COPY x from stdin with (reject_limit 1);
COPY x from stdin with (on_error ignore, reject_limit 0);

-- too many columns in column list: should fail
COPY x (a, b, c, d, e, d, c) from stdin;
Expand Down Expand Up @@ -557,6 +559,25 @@ COPY check_ign_err FROM STDIN WITH (on_error ignore);
1 {1} 3 abc
\.

-- tests for reject_limit option
COPY check_ign_err FROM STDIN WITH (on_error ignore, reject_limit 3);
6 {6} 6
a {7} 7
8 {8} 8888888888
9 {a, 9} 9

10 {10} 10
\.

COPY check_ign_err FROM STDIN WITH (on_error ignore, reject_limit 4);
6 {6} 6
a {7} 7
8 {8} 8888888888
9 {a, 9} 9

10 {10} 10
\.

-- clean up
DROP TABLE forcetest;
DROP TABLE vistest;
Expand Down

0 comments on commit 4550bd8

Please sign in to comment.