Skip to content

Commit f6d4c9c

Browse files
committed
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
1 parent c181f2e commit f6d4c9c

File tree

8 files changed

+103
-12
lines changed

8 files changed

+103
-12
lines changed

doc/src/sgml/ref/copy.sgml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
4141
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
4242
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
4343
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
44-
FORCE_NOT_NULL ( <replaceable class="parameter">column_name</replaceable> [, ...] )
45-
FORCE_NULL ( <replaceable class="parameter">column_name</replaceable> [, ...] )
44+
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
45+
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
4646
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
4747
</synopsis>
4848
</refsynopsisdiv>
@@ -350,6 +350,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
350350
In the default case where the null string is empty, this means that
351351
empty values will be read as zero-length strings rather than nulls,
352352
even when they are not quoted.
353+
If <literal>*</literal> is specified, the option will be applied to all columns.
353354
This option is allowed only in <command>COPY FROM</command>, and only when
354355
using <literal>CSV</literal> format.
355356
</para>
@@ -364,6 +365,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
364365
if it has been quoted, and if a match is found set the value to
365366
<literal>NULL</literal>. In the default case where the null string is empty,
366367
this converts a quoted empty string into NULL.
368+
If <literal>*</literal> is specified, the option will be applied to all columns.
367369
This option is allowed only in <command>COPY FROM</command>, and only when
368370
using <literal>CSV</literal> format.
369371
</para>

src/backend/commands/copy.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,11 @@ ProcessCopyOptions(ParseState *pstate,
512512
}
513513
else if (strcmp(defel->defname, "force_not_null") == 0)
514514
{
515-
if (opts_out->force_notnull)
515+
if (opts_out->force_notnull || opts_out->force_notnull_all)
516516
errorConflictingDefElem(defel, pstate);
517-
if (defel->arg && IsA(defel->arg, List))
517+
if (defel->arg && IsA(defel->arg, A_Star))
518+
opts_out->force_notnull_all = true;
519+
else if (defel->arg && IsA(defel->arg, List))
518520
opts_out->force_notnull = castNode(List, defel->arg);
519521
else
520522
ereport(ERROR,
@@ -525,9 +527,11 @@ ProcessCopyOptions(ParseState *pstate,
525527
}
526528
else if (strcmp(defel->defname, "force_null") == 0)
527529
{
528-
if (opts_out->force_null)
530+
if (opts_out->force_null || opts_out->force_null_all)
529531
errorConflictingDefElem(defel, pstate);
530-
if (defel->arg && IsA(defel->arg, List))
532+
if (defel->arg && IsA(defel->arg, A_Star))
533+
opts_out->force_null_all = true;
534+
else if (defel->arg && IsA(defel->arg, List))
531535
opts_out->force_null = castNode(List, defel->arg);
532536
else
533537
ereport(ERROR,

src/backend/commands/copyfrom.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,9 @@ BeginCopyFrom(ParseState *pstate,
13931393

13941394
/* Convert FORCE_NOT_NULL name list to per-column flags, check validity */
13951395
cstate->opts.force_notnull_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
1396-
if (cstate->opts.force_notnull)
1396+
if (cstate->opts.force_notnull_all)
1397+
MemSet(cstate->opts.force_notnull_flags, true, num_phys_attrs * sizeof(bool));
1398+
else if (cstate->opts.force_notnull)
13971399
{
13981400
List *attnums;
13991401
ListCell *cur;
@@ -1416,7 +1418,9 @@ BeginCopyFrom(ParseState *pstate,
14161418

14171419
/* Convert FORCE_NULL name list to per-column flags, check validity */
14181420
cstate->opts.force_null_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
1419-
if (cstate->opts.force_null)
1421+
if (cstate->opts.force_null_all)
1422+
MemSet(cstate->opts.force_null_flags, true, num_phys_attrs * sizeof(bool));
1423+
else if (cstate->opts.force_null)
14201424
{
14211425
List *attnums;
14221426
ListCell *cur;

src/backend/commands/copyto.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,10 +582,7 @@ BeginCopyTo(ParseState *pstate,
582582
cstate->opts.force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
583583
if (cstate->opts.force_quote_all)
584584
{
585-
int i;
586-
587-
for (i = 0; i < num_phys_attrs; i++)
588-
cstate->opts.force_quote_flags[i] = true;
585+
MemSet(cstate->opts.force_quote_flags, true, num_phys_attrs * sizeof(bool));
589586
}
590587
else if (cstate->opts.force_quote)
591588
{

src/backend/parser/gram.y

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3419,10 +3419,18 @@ copy_opt_item:
34193419
{
34203420
$$ = makeDefElem("force_not_null", (Node *) $4, @1);
34213421
}
3422+
| FORCE NOT NULL_P '*'
3423+
{
3424+
$$ = makeDefElem("force_not_null", (Node *) makeNode(A_Star), @1);
3425+
}
34223426
| FORCE NULL_P columnList
34233427
{
34243428
$$ = makeDefElem("force_null", (Node *) $3, @1);
34253429
}
3430+
| FORCE NULL_P '*'
3431+
{
3432+
$$ = makeDefElem("force_null", (Node *) makeNode(A_Star), @1);
3433+
}
34263434
| ENCODING Sconst
34273435
{
34283436
$$ = makeDefElem("encoding", (Node *) makeString($2), @1);

src/include/commands/copy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ typedef struct CopyFormatOptions
5656
bool force_quote_all; /* FORCE_QUOTE *? */
5757
bool *force_quote_flags; /* per-column CSV FQ flags */
5858
List *force_notnull; /* list of column names */
59+
bool force_notnull_all; /* FORCE_NOT_NULL *? */
5960
bool *force_notnull_flags; /* per-column CSV FNN flags */
6061
List *force_null; /* list of column names */
62+
bool force_null_all; /* FORCE_NULL *? */
6163
bool *force_null_flags; /* per-column CSV FN flags */
6264
bool convert_selectively; /* do selective binary conversion? */
6365
List *convert_select; /* list of column names (can be NIL) */

src/test/regress/expected/copy2.out

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,50 @@ BEGIN;
520520
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
521521
ERROR: FORCE_NULL column "b" not referenced by COPY
522522
ROLLBACK;
523+
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
524+
BEGIN;
525+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *);
526+
COMMIT;
527+
SELECT b, c FROM forcetest WHERE a = 4;
528+
b | c
529+
---+------
530+
| NULL
531+
(1 row)
532+
533+
-- should succeed with effect ("b" remains an empty string)
534+
BEGIN;
535+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *);
536+
COMMIT;
537+
SELECT b, c FROM forcetest WHERE a = 5;
538+
b | c
539+
---+---
540+
|
541+
(1 row)
542+
543+
-- should succeed with effect ("c" remains NULL)
544+
BEGIN;
545+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *);
546+
COMMIT;
547+
SELECT b, c FROM forcetest WHERE a = 6;
548+
b | c
549+
---+------
550+
b | NULL
551+
(1 row)
552+
553+
-- should fail with "conflicting or redundant options" error
554+
BEGIN;
555+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_NULL(b));
556+
ERROR: conflicting or redundant options
557+
LINE 1: ...c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_...
558+
^
559+
ROLLBACK;
560+
-- should fail with "conflicting or redundant options" error
561+
BEGIN;
562+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b));
563+
ERROR: conflicting or redundant options
564+
LINE 1: ... b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL...
565+
^
566+
ROLLBACK;
523567
\pset null ''
524568
-- test case with whole-row Var in a check constraint
525569
create table check_con_tbl (f1 int);

src/test/regress/sql/copy2.sql

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,36 @@ ROLLBACK;
344344
BEGIN;
345345
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
346346
ROLLBACK;
347+
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
348+
BEGIN;
349+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *);
350+
4,,""
351+
\.
352+
COMMIT;
353+
SELECT b, c FROM forcetest WHERE a = 4;
354+
-- should succeed with effect ("b" remains an empty string)
355+
BEGIN;
356+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *);
357+
5,,""
358+
\.
359+
COMMIT;
360+
SELECT b, c FROM forcetest WHERE a = 5;
361+
-- should succeed with effect ("c" remains NULL)
362+
BEGIN;
363+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *);
364+
6,"b",""
365+
\.
366+
COMMIT;
367+
SELECT b, c FROM forcetest WHERE a = 6;
368+
-- should fail with "conflicting or redundant options" error
369+
BEGIN;
370+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_NULL(b));
371+
ROLLBACK;
372+
-- should fail with "conflicting or redundant options" error
373+
BEGIN;
374+
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b));
375+
ROLLBACK;
376+
347377
\pset null ''
348378

349379
-- test case with whole-row Var in a check constraint

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy