Skip to content

Commit c3d0e54

Browse files
author
Commitfest Bot
committed
[CF 4817] v5 - on_error table, saving error info to a table
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/4817 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxGsswQm1gHxM7gAb1j_UPOymtnOrAjeS5i+a5N00xM-Wg@mail.gmail.com Author(s): jian he
2 parents bbccf7e + 57452a9 commit c3d0e54

File tree

9 files changed

+582
-13
lines changed

9 files changed

+582
-13
lines changed

doc/src/sgml/ref/copy.sgml

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
4444
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
4545
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
4646
ON_ERROR <replaceable class="parameter">error_action</replaceable>
47+
TABLE <replaceable class="parameter">error_saving_table</replaceable>
4748
REJECT_LIMIT <replaceable class="parameter">maxerror</replaceable>
4849
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
4950
LOG_VERBOSITY <replaceable class="parameter">verbosity</replaceable>
@@ -395,15 +396,25 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
395396
input value into its data type.
396397
An <replaceable class="parameter">error_action</replaceable> value of
397398
<literal>stop</literal> means fail the command, while
398-
<literal>ignore</literal> means discard the input row and continue with the next one.
399+
<literal>ignore</literal> means discard the input row and continue with the next one,
400+
<literal>table</literal> means save error details to <replaceable class="parameter">error_saving_table</replaceable>
401+
and continue with the next one.
399402
The default is <literal>stop</literal>.
400403
</para>
401404
<para>
402-
The <literal>ignore</literal> option is applicable only for <command>COPY FROM</command>
405+
The <literal>ignore</literal> and <literal>table</literal> option are applicable only for <command>COPY FROM</command>
403406
when the <literal>FORMAT</literal> is <literal>text</literal> or <literal>csv</literal>.
404407
</para>
405408
<para>
406-
A <literal>NOTICE</literal> message containing the ignored row count is
409+
If <literal>ON_ERROR</literal>=<literal>table</literal>,
410+
a <literal>NOTICE</literal> message containing the row count that is saved to
411+
<replaceable class="parameter">error_saving_table</replaceable> is
412+
emitted at the end of the <command>COPY FROM</command> if at least one
413+
row was saved.
414+
</para>
415+
416+
<para>
417+
If <literal>ON_ERROR</literal>=<literal>ignore</literal>, a <literal>NOTICE</literal> message containing the ignored row count is
407418
emitted at the end of the <command>COPY FROM</command> if at least one
408419
row was discarded. When <literal>LOG_VERBOSITY</literal> option is set to
409420
<literal>verbose</literal>, a <literal>NOTICE</literal> message
@@ -463,6 +474,108 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
463474
</listitem>
464475
</varlistentry>
465476

477+
<varlistentry>
478+
<term><literal>TABLE</literal></term>
479+
<listitem>
480+
<para>
481+
Save error context details to the table <replaceable class="parameter">error_saving_table</replaceable>.
482+
This option is allowed only in <command>COPY FROM</command> and
483+
<literal>ON_ERROR</literal> is specified with <literal>TABLE</literal>.
484+
It also require user have <literal>INSERT</literal> privileges on all columns
485+
in the <replaceable class="parameter">error_saving_table</replaceable>.
486+
</para>
487+
488+
<para>
489+
If table <replaceable class="parameter">error_saving_table</replaceable> does meet the following definition
490+
(column ordinal position should be the same as the below), an error will be raised.
491+
492+
<informaltable>
493+
<tgroup cols="3">
494+
<thead>
495+
<row>
496+
<entry>Column name</entry>
497+
<entry>Data type</entry>
498+
<entry>Description</entry>
499+
</row>
500+
</thead>
501+
502+
<tbody>
503+
<row>
504+
<entry> <literal>userid</literal> </entry>
505+
<entry><type>oid</type></entry>
506+
<entry>The user generated the error.
507+
Reference <link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.<structfield>oid</structfield>,
508+
however there is no hard dependency with catalog <literal>pg_authid</literal>.
509+
If the corresponding row on <literal>pg_authid</literal> is deleted, this value becomes stale.
510+
</entry>
511+
</row>
512+
513+
<row>
514+
<entry> <literal>copy_tbl</literal> </entry>
515+
<entry><type>oid</type></entry>
516+
<entry>The <command>COPY FROM</command> operation destination table oid.
517+
Reference <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>,
518+
however there is no hard dependency with catalog <literal>pg_class</literal>.
519+
If the corresponding row on <literal>pg_class</literal> is deleted, this value becomes stale.
520+
</entry>
521+
</row>
522+
523+
<row>
524+
<entry> <literal>filename</literal> </entry>
525+
<entry><type>text</type></entry>
526+
<entry>The path name of the <command>COPY FROM</command> input</entry>
527+
</row>
528+
529+
<row>
530+
<entry> <literal>lineno</literal> </entry>
531+
<entry><type>bigint</type></entry>
532+
<entry>Line number where the error occurred, counting from 1</entry>
533+
</row>
534+
535+
<row>
536+
<entry> <literal>line</literal> </entry>
537+
<entry><type>text</type></entry>
538+
<entry>Raw content of the error occurred line</entry>
539+
</row>
540+
541+
<row>
542+
<entry> <literal>colname</literal> </entry>
543+
<entry><type>text</type></entry>
544+
<entry>Field where the error occurred</entry>
545+
</row>
546+
547+
<row>
548+
<entry> <literal>raw_field_value</literal> </entry>
549+
<entry><type>text</type></entry>
550+
<entry>Raw content of the error occurred field</entry>
551+
</row>
552+
553+
<row>
554+
<entry> <literal>err_message </literal> </entry>
555+
<entry><type>text</type></entry>
556+
<entry>The error message</entry>
557+
</row>
558+
559+
<row>
560+
<entry> <literal>err_detail</literal> </entry>
561+
<entry><type>text</type></entry>
562+
<entry>Detailed error message </entry>
563+
</row>
564+
565+
<row>
566+
<entry> <literal>errorcode </literal> </entry>
567+
<entry><type>text</type></entry>
568+
<entry>The error code </entry>
569+
</row>
570+
571+
</tbody>
572+
</tgroup>
573+
</informaltable>
574+
575+
</para>
576+
</listitem>
577+
</varlistentry>
578+
466579
<varlistentry>
467580
<term><literal>WHERE</literal></term>
468581
<listitem>

src/backend/commands/copy.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
410410
if (pg_strcasecmp(sval, "ignore") == 0)
411411
return COPY_ON_ERROR_IGNORE;
412412

413+
if (pg_strcasecmp(sval, "table") == 0)
414+
return COPY_ON_ERROR_TABLE;
413415
ereport(ERROR,
414416
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
415417
/*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
@@ -502,6 +504,7 @@ ProcessCopyOptions(ParseState *pstate,
502504
bool freeze_specified = false;
503505
bool header_specified = false;
504506
bool on_error_specified = false;
507+
bool on_error_tbl_specified = false;
505508
bool log_verbosity_specified = false;
506509
bool reject_limit_specified = false;
507510
ListCell *option;
@@ -677,6 +680,13 @@ ProcessCopyOptions(ParseState *pstate,
677680
reject_limit_specified = true;
678681
opts_out->reject_limit = defGetCopyRejectLimitOption(defel);
679682
}
683+
else if (strcmp(defel->defname, "table") == 0)
684+
{
685+
if (on_error_tbl_specified)
686+
errorConflictingDefElem(defel, pstate);
687+
on_error_tbl_specified = true;
688+
opts_out->on_error_tbl = defGetString(defel);
689+
}
680690
else
681691
ereport(ERROR,
682692
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -685,6 +695,25 @@ ProcessCopyOptions(ParseState *pstate,
685695
parser_errposition(pstate, defel->location)));
686696
}
687697

698+
if (opts_out->on_error == COPY_ON_ERROR_TABLE)
699+
{
700+
if (opts_out->on_error_tbl == NULL)
701+
ereport(ERROR,
702+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
703+
errmsg("cannot specify %s option value to \"%s\" when %s is not specified", "ON_ERROR", "TABLE", "TABLE"),
704+
errhint("You may need also specify \"%s\" option.", "TABLE"));
705+
706+
if (opts_out->reject_limit)
707+
ereport(ERROR,
708+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
709+
errmsg("cannot specify %s option when %s option is specified as \"%s\"", "REJECT_LIMIT", "ON_ERROR", "TABLE"));
710+
}
711+
712+
if (opts_out->on_error != COPY_ON_ERROR_TABLE && opts_out->on_error_tbl != NULL)
713+
ereport(ERROR,
714+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
715+
errmsg("COPY %s option can only be used when %s option is specified as \"%s\"", "TABLE", "ON_ERROR", "TABLE"));
716+
688717
/*
689718
* Check for incompatible options (must do these three before inserting
690719
* defaults)

0 commit comments

Comments
 (0)