diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index dcf6e6a2f48..2aeb38a6e5f 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -6265,10 +6265,13 @@ FROM pg_stat_get_backend_idset() AS backendid; tuples_skipped bigint - Number of tuples skipped because they contain malformed data. - This counter only advances when - ignore is specified to the ON_ERROR - option. + Number of tuples that contained malformed data. When + ON_ERROR is set to ignore, + this counts rows that were skipped. When set to + set_null, this counts rows where at least one + column was set to null due to a conversion error. + This counter only advances when ON_ERROR + is set to ignore or set_null. diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 96ba23e961c..0a75800b8ab 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -1052,6 +1052,11 @@ CopyFromTextLikeOneRow(CopyFromState cstate, ExprContext *econtext, cstate->num_errors++; else if (cstate->opts.on_error == COPY_ON_ERROR_SET_NULL) { + /* + * Reset error state so the subsequent InputFunctionCallSafe + * call (for domain constraint check) can properly report + * whether it succeeded or failed. + */ cstate->escontext->error_occurred = false; Assert(cstate->domain_with_constraint != NULL); @@ -1075,13 +1080,17 @@ CopyFromTextLikeOneRow(CopyFromState cstate, ExprContext *econtext, else if (string == NULL) ereport(ERROR, errcode(ERRCODE_NOT_NULL_VIOLATION), - errmsg("domain %s does not allow null values", format_type_be(typioparams[m])), + errmsg("null value in column \"%s\" violates not-null constraint of domain %s", + cstate->cur_attname, format_type_be(typioparams[m])), errdatatype(typioparams[m])); else ereport(ERROR, - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("invalid input value for domain %s: \"%s\"", - format_type_be(typioparams[m]), string)); + errcode(ERRCODE_NOT_NULL_VIOLATION), + errmsg("cannot set null value for column \"%s\" with domain %s", + cstate->cur_attname, format_type_be(typioparams[m])), + errdetail("Column \"%s\" does not accept null values, so ON_ERROR SET_NULL cannot be applied.", + cstate->cur_attname), + errdatatype(typioparams[m])); /* * We count only the number of rows (not fields) where diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index 72034796aca..d9cc7bf5f48 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -799,13 +799,15 @@ CREATE DOMAIN d_int_positive_maybe_null AS integer CHECK(value > 0); CREATE TABLE t_on_error_null (a d_int_not_null, b d_int_positive_maybe_null, c integer); \pset null NULL COPY t_on_error_null FROM STDIN WITH (on_error set_null); --fail -ERROR: domain d_int_not_null does not allow null values +ERROR: null value in column "a" violates not-null constraint of domain d_int_not_null CONTEXT: COPY t_on_error_null, line 1, column a: null input COPY t_on_error_null FROM STDIN WITH (on_error set_null); --fail -ERROR: invalid input value for domain d_int_not_null: "ss" +ERROR: cannot set null value for column "a" with domain d_int_not_null +DETAIL: Column "a" does not accept null values, so ON_ERROR SET_NULL cannot be applied. CONTEXT: COPY t_on_error_null, line 1, column a: "ss" COPY t_on_error_null FROM STDIN WITH (on_error set_null); --fail -ERROR: invalid input value for domain d_int_not_null: "-1" +ERROR: cannot set null value for column "a" with domain d_int_not_null +DETAIL: Column "a" does not accept null values, so ON_ERROR SET_NULL cannot be applied. CONTEXT: COPY t_on_error_null, line 1, column a: "-1" --fail, less data. COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null);