0005-Introduce-NULL_ON_ERROR-option-for-COPY-FROM.patch
text/x-patch
Filename: 0005-Introduce-NULL_ON_ERROR-option-for-COPY-FROM.patch
Type: text/x-patch
Part: 4
Message:
Re: SQL/JSON features for v15
Patch
Format: format-patch
Series: patch 0005
Subject: Introduce NULL_ON_ERROR option for COPY FROM
| File | + | − |
|---|---|---|
| src/backend/commands/copy.c | 20 | 0 |
| src/backend/commands/copyfrom.c | 33 | 0 |
| src/backend/commands/copyfromparse.c | 16 | 5 |
| src/include/commands/copy.h | 2 | 0 |
| src/test/regress/expected/copy.out | 15 | 0 |
| src/test/regress/sql/copy.sql | 13 | 0 |
From 1fc1b4231b079078c303be75864c4ef71d0742a1 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 31 Aug 2022 22:29:47 +0300
Subject: [PATCH 5/5] Introduce NULL_ON_ERROR option for COPY FROM
---
src/backend/commands/copy.c | 20 +++++++++++++++++
src/backend/commands/copyfrom.c | 33 ++++++++++++++++++++++++++++
src/backend/commands/copyfromparse.c | 21 +++++++++++++-----
src/include/commands/copy.h | 2 ++
src/test/regress/expected/copy.out | 15 +++++++++++++
src/test/regress/sql/copy.sql | 13 +++++++++++
6 files changed, 99 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 49924e476af..ce7e875f655 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -551,6 +551,19 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "null_on_error") == 0)
+ {
+ if (opts_out->null_on_error)
+ errorConflictingDefElem(defel, pstate);
+ if (defel->arg && IsA(defel->arg, List))
+ opts_out->null_on_error = castNode(List, defel->arg);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument to option \"%s\" must be a list of column names",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -701,6 +714,13 @@ ProcessCopyOptions(ParseState *pstate,
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CSV quote character must not appear in the NULL specification")));
+
+ /* Check null_on_error */
+ if (opts_out->null_on_error != NIL && !is_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("COPY null on error only available using COPY FROM")));
+
}
/*
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index e8bb168aea8..99b39d8d7c2 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -47,6 +47,7 @@
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/portal.h"
@@ -1316,6 +1317,29 @@ BeginCopyFrom(ParseState *pstate,
}
}
+ /* Convert NULL_ON_ERROR name list to per-column flags, check validity */
+ cstate->opts.null_on_error_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
+ if (cstate->opts.null_on_error)
+ {
+ List *attnums;
+ ListCell *cur;
+
+ attnums = CopyGetAttnums(tupDesc, cstate->rel, cstate->opts.null_on_error);
+
+ foreach(cur, attnums)
+ {
+ int attnum = lfirst_int(cur);
+ Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1);
+
+ if (!list_member_int(cstate->attnumlist, attnum))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("NULL_ON_ERROR column \"%s\" not referenced by COPY",
+ NameStr(attr->attname))));
+ cstate->opts.null_on_error_flags[attnum - 1] = true;
+ }
+ }
+
/* Use client encoding when ENCODING option is not specified. */
if (cstate->opts.file_encoding < 0)
cstate->file_encoding = pg_get_client_encoding();
@@ -1417,6 +1441,15 @@ BeginCopyFrom(ParseState *pstate,
&in_func_oid, &typioparams[attnum - 1]);
fmgr_info(in_func_oid, &in_functions[attnum - 1]);
+ /* check whether input function supports returning errors */
+ if (cstate->opts.null_on_error_flags[attnum - 1] &&
+ !func_is_safe(in_func_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("input function for datatype \"%s\" does not support error handling",
+ format_type_be(att->atttypid))));
+
+
/* Get default info if needed */
if (!list_member_int(cstate->attnumlist, attnum) && !att->attgenerated)
{
diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c
index 097414ef12d..04a36de444c 100644
--- a/src/backend/commands/copyfromparse.c
+++ b/src/backend/commands/copyfromparse.c
@@ -897,6 +897,8 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
int attnum = lfirst_int(cur);
int m = attnum - 1;
Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+ ErrorData *edata = NULL;
+ ErrorData **p_edata;
if (fieldno >= fldct)
ereport(ERROR,
@@ -938,11 +940,20 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
cstate->cur_attname = NameStr(att->attname);
cstate->cur_attval = string;
- values[m] = InputFunctionCall(&in_functions[m],
- string,
- typioparams[m],
- att->atttypmod);
- if (string != NULL)
+
+ p_edata = cstate->opts.null_on_error_flags[m] ? &edata : NULL;
+
+ values[m] = InputFunctionCallOptError(&in_functions[m],
+ string,
+ typioparams[m],
+ att->atttypmod,
+ p_edata);
+ if (edata)
+ {
+ FreeErrorData(edata);
+ nulls[m] = true;
+ }
+ else if (string != NULL)
nulls[m] = false;
cstate->cur_attname = NULL;
cstate->cur_attval = NULL;
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index cb0096aeb67..359fb4cc9e0 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -59,6 +59,8 @@ typedef struct CopyFormatOptions
bool *force_null_flags; /* per-column CSV FN flags */
bool convert_selectively; /* do selective binary conversion? */
List *convert_select; /* list of column names (can be NIL) */
+ List *null_on_error; /* list of column names */
+ bool *null_on_error_flags; /* per-column NOE flags */
} CopyFormatOptions;
/* These are private in commands/copy[from|to].c */
diff --git a/src/test/regress/expected/copy.out b/src/test/regress/expected/copy.out
index 3fad1c52d1f..82839d2f67a 100644
--- a/src/test/regress/expected/copy.out
+++ b/src/test/regress/expected/copy.out
@@ -240,3 +240,18 @@ SELECT * FROM header_copytest ORDER BY a;
(5 rows)
drop table header_copytest;
+create table null_on_error_copytest(i int, f float4);
+copy null_on_error_copytest from stdin with (null_on_error (i, f));
+ERROR: input function for datatype "integer" does not support error handling
+copy null_on_error_copytest from stdin with (null_on_error (f));
+copy null_on_error_copytest from stdin with (null_on_error (f));
+ERROR: invalid input syntax for type integer: "err"
+CONTEXT: COPY null_on_error_copytest, line 2, column i: "err"
+select * from null_on_error_copytest;
+ i | f
+---+-----
+ 1 | 2.3
+ 2 |
+(2 rows)
+
+drop table null_on_error_copytest;
diff --git a/src/test/regress/sql/copy.sql b/src/test/regress/sql/copy.sql
index 285022e07c6..914e29d5ba1 100644
--- a/src/test/regress/sql/copy.sql
+++ b/src/test/regress/sql/copy.sql
@@ -268,3 +268,16 @@ a c b
SELECT * FROM header_copytest ORDER BY a;
drop table header_copytest;
+
+create table null_on_error_copytest(i int, f float4);
+copy null_on_error_copytest from stdin with (null_on_error (i, f));
+copy null_on_error_copytest from stdin with (null_on_error (f));
+1 2.3
+2 err
+\.
+copy null_on_error_copytest from stdin with (null_on_error (f));
+1 2.3
+err 4.5
+\.
+select * from null_on_error_copytest;
+drop table null_on_error_copytest;
--
2.25.1