diff options
author | Andrew Dunstan <andrew@dunslane.net> | 2023-09-30 12:34:41 -0400 |
---|---|---|
committer | Andrew Dunstan <andrew@dunslane.net> | 2023-09-30 12:34:41 -0400 |
commit | f6d4c9cf162b70f2837fb6c2a83e80a3f3410695 (patch) | |
tree | 23d62216e9b51f67ea5aff1bad95f5434b2bcd1c /src | |
parent | c181f2e2bcecc2704c6461a0543894a38d7143df (diff) | |
download | postgresql-f6d4c9cf162b70f2837fb6c2a83e80a3f3410695.tar.gz postgresql-f6d4c9cf162b70f2837fb6c2a83e80a3f3410695.zip |
Provide FORCE_NULL * and FORCE_NOT_NULL * options for COPY FROM
These options already exist, but you need to specify a column list for
them, which can be cumbersome. We already have the possibility of all
columns for FORCE QUOTE, so this is simply extending that facility to
FORCE_NULL and FORCE_NOT_NULL.
Author: Zhang Mingli
Reviewed-By: Richard Guo, Kyatoro Horiguchi, Michael Paquier.
Discussion: https://postgr.es/m/CACJufxEnVqzOFtqhexF2+AwOKFrV8zHOY3y=p+gPK6eB14pn_w@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/copy.c | 12 | ||||
-rw-r--r-- | src/backend/commands/copyfrom.c | 8 | ||||
-rw-r--r-- | src/backend/commands/copyto.c | 5 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 8 | ||||
-rw-r--r-- | src/include/commands/copy.h | 2 | ||||
-rw-r--r-- | src/test/regress/expected/copy2.out | 44 | ||||
-rw-r--r-- | src/test/regress/sql/copy2.sql | 30 |
7 files changed, 99 insertions, 10 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f14fae33083..c5d7d78645a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -512,9 +512,11 @@ ProcessCopyOptions(ParseState *pstate, } else if (strcmp(defel->defname, "force_not_null") == 0) { - if (opts_out->force_notnull) + if (opts_out->force_notnull || opts_out->force_notnull_all) errorConflictingDefElem(defel, pstate); - if (defel->arg && IsA(defel->arg, List)) + if (defel->arg && IsA(defel->arg, A_Star)) + opts_out->force_notnull_all = true; + else if (defel->arg && IsA(defel->arg, List)) opts_out->force_notnull = castNode(List, defel->arg); else ereport(ERROR, @@ -525,9 +527,11 @@ ProcessCopyOptions(ParseState *pstate, } else if (strcmp(defel->defname, "force_null") == 0) { - if (opts_out->force_null) + if (opts_out->force_null || opts_out->force_null_all) errorConflictingDefElem(defel, pstate); - if (defel->arg && IsA(defel->arg, List)) + if (defel->arg && IsA(defel->arg, A_Star)) + opts_out->force_null_all = true; + else if (defel->arg && IsA(defel->arg, List)) opts_out->force_null = castNode(List, defel->arg); else ereport(ERROR, diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 70871ed8190..2d1567d0e42 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1393,7 +1393,9 @@ BeginCopyFrom(ParseState *pstate, /* Convert FORCE_NOT_NULL name list to per-column flags, check validity */ cstate->opts.force_notnull_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); - if (cstate->opts.force_notnull) + if (cstate->opts.force_notnull_all) + MemSet(cstate->opts.force_notnull_flags, true, num_phys_attrs * sizeof(bool)); + else if (cstate->opts.force_notnull) { List *attnums; ListCell *cur; @@ -1416,7 +1418,9 @@ BeginCopyFrom(ParseState *pstate, /* Convert FORCE_NULL name list to per-column flags, check validity */ cstate->opts.force_null_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); - if (cstate->opts.force_null) + if (cstate->opts.force_null_all) + MemSet(cstate->opts.force_null_flags, true, num_phys_attrs * sizeof(bool)); + else if (cstate->opts.force_null) { List *attnums; ListCell *cur; diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index eaa3172793a..0378f0ade09 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -582,10 +582,7 @@ BeginCopyTo(ParseState *pstate, cstate->opts.force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); if (cstate->opts.force_quote_all) { - int i; - - for (i = 0; i < num_phys_attrs; i++) - cstate->opts.force_quote_flags[i] = true; + MemSet(cstate->opts.force_quote_flags, true, num_phys_attrs * sizeof(bool)); } else if (cstate->opts.force_quote) { diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7d2032885ed..fec9b2604fe 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3419,10 +3419,18 @@ copy_opt_item: { $$ = makeDefElem("force_not_null", (Node *) $4, @1); } + | FORCE NOT NULL_P '*' + { + $$ = makeDefElem("force_not_null", (Node *) makeNode(A_Star), @1); + } | FORCE NULL_P columnList { $$ = makeDefElem("force_null", (Node *) $3, @1); } + | FORCE NULL_P '*' + { + $$ = makeDefElem("force_null", (Node *) makeNode(A_Star), @1); + } | ENCODING Sconst { $$ = makeDefElem("encoding", (Node *) makeString($2), @1); diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 33175868f65..f2cca0b90b4 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -56,8 +56,10 @@ typedef struct CopyFormatOptions bool force_quote_all; /* FORCE_QUOTE *? */ bool *force_quote_flags; /* per-column CSV FQ flags */ List *force_notnull; /* list of column names */ + bool force_notnull_all; /* FORCE_NOT_NULL *? */ bool *force_notnull_flags; /* per-column CSV FNN flags */ List *force_null; /* list of column names */ + bool force_null_all; /* FORCE_NULL *? */ 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) */ diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index faf1a4d1b0c..95ec7363afc 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -520,6 +520,50 @@ BEGIN; COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b)); ERROR: FORCE_NULL column "b" not referenced by COPY ROLLBACK; +-- should succeed with no effect ("b" remains an empty string, "c" remains NULL) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *); +COMMIT; +SELECT b, c FROM forcetest WHERE a = 4; + b | c +---+------ + | NULL +(1 row) + +-- should succeed with effect ("b" remains an empty string) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *); +COMMIT; +SELECT b, c FROM forcetest WHERE a = 5; + b | c +---+--- + | +(1 row) + +-- should succeed with effect ("c" remains NULL) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *); +COMMIT; +SELECT b, c FROM forcetest WHERE a = 6; + b | c +---+------ + b | NULL +(1 row) + +-- should fail with "conflicting or redundant options" error +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_NULL(b)); +ERROR: conflicting or redundant options +LINE 1: ...c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_... + ^ +ROLLBACK; +-- should fail with "conflicting or redundant options" error +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b)); +ERROR: conflicting or redundant options +LINE 1: ... b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL... + ^ +ROLLBACK; \pset null '' -- test case with whole-row Var in a check constraint create table check_con_tbl (f1 int); diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index d759635068c..a5486f60867 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -344,6 +344,36 @@ ROLLBACK; BEGIN; COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b)); ROLLBACK; +-- should succeed with no effect ("b" remains an empty string, "c" remains NULL) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *); +4,,"" +\. +COMMIT; +SELECT b, c FROM forcetest WHERE a = 4; +-- should succeed with effect ("b" remains an empty string) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *); +5,,"" +\. +COMMIT; +SELECT b, c FROM forcetest WHERE a = 5; +-- should succeed with effect ("c" remains NULL) +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *); +6,"b","" +\. +COMMIT; +SELECT b, c FROM forcetest WHERE a = 6; +-- should fail with "conflicting or redundant options" error +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_NULL(b)); +ROLLBACK; +-- should fail with "conflicting or redundant options" error +BEGIN; +COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b)); +ROLLBACK; + \pset null '' -- test case with whole-row Var in a check constraint |