mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-23 19:39:53 +08:00
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
This commit is contained in:
parent
c181f2e2bc
commit
f6d4c9cf16
@ -41,8 +41,8 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
|
|||||||
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
|
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
|
||||||
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
|
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
|
||||||
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
|
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
|
||||||
FORCE_NOT_NULL ( <replaceable class="parameter">column_name</replaceable> [, ...] )
|
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
|
||||||
FORCE_NULL ( <replaceable class="parameter">column_name</replaceable> [, ...] )
|
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
|
||||||
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
|
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@ -350,6 +350,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
|
|||||||
In the default case where the null string is empty, this means that
|
In the default case where the null string is empty, this means that
|
||||||
empty values will be read as zero-length strings rather than nulls,
|
empty values will be read as zero-length strings rather than nulls,
|
||||||
even when they are not quoted.
|
even when they are not quoted.
|
||||||
|
If <literal>*</literal> is specified, the option will be applied to all columns.
|
||||||
This option is allowed only in <command>COPY FROM</command>, and only when
|
This option is allowed only in <command>COPY FROM</command>, and only when
|
||||||
using <literal>CSV</literal> format.
|
using <literal>CSV</literal> format.
|
||||||
</para>
|
</para>
|
||||||
@ -364,6 +365,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
|
|||||||
if it has been quoted, and if a match is found set the value to
|
if it has been quoted, and if a match is found set the value to
|
||||||
<literal>NULL</literal>. In the default case where the null string is empty,
|
<literal>NULL</literal>. In the default case where the null string is empty,
|
||||||
this converts a quoted empty string into NULL.
|
this converts a quoted empty string into NULL.
|
||||||
|
If <literal>*</literal> is specified, the option will be applied to all columns.
|
||||||
This option is allowed only in <command>COPY FROM</command>, and only when
|
This option is allowed only in <command>COPY FROM</command>, and only when
|
||||||
using <literal>CSV</literal> format.
|
using <literal>CSV</literal> format.
|
||||||
</para>
|
</para>
|
||||||
|
@ -512,9 +512,11 @@ ProcessCopyOptions(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "force_not_null") == 0)
|
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);
|
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);
|
opts_out->force_notnull = castNode(List, defel->arg);
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -525,9 +527,11 @@ ProcessCopyOptions(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "force_null") == 0)
|
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);
|
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);
|
opts_out->force_null = castNode(List, defel->arg);
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -1393,7 +1393,9 @@ BeginCopyFrom(ParseState *pstate,
|
|||||||
|
|
||||||
/* Convert FORCE_NOT_NULL name list to per-column flags, check validity */
|
/* Convert FORCE_NOT_NULL name list to per-column flags, check validity */
|
||||||
cstate->opts.force_notnull_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
|
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;
|
List *attnums;
|
||||||
ListCell *cur;
|
ListCell *cur;
|
||||||
@ -1416,7 +1418,9 @@ BeginCopyFrom(ParseState *pstate,
|
|||||||
|
|
||||||
/* Convert FORCE_NULL name list to per-column flags, check validity */
|
/* Convert FORCE_NULL name list to per-column flags, check validity */
|
||||||
cstate->opts.force_null_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
|
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;
|
List *attnums;
|
||||||
ListCell *cur;
|
ListCell *cur;
|
||||||
|
@ -582,10 +582,7 @@ BeginCopyTo(ParseState *pstate,
|
|||||||
cstate->opts.force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
|
cstate->opts.force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
|
||||||
if (cstate->opts.force_quote_all)
|
if (cstate->opts.force_quote_all)
|
||||||
{
|
{
|
||||||
int i;
|
MemSet(cstate->opts.force_quote_flags, true, num_phys_attrs * sizeof(bool));
|
||||||
|
|
||||||
for (i = 0; i < num_phys_attrs; i++)
|
|
||||||
cstate->opts.force_quote_flags[i] = true;
|
|
||||||
}
|
}
|
||||||
else if (cstate->opts.force_quote)
|
else if (cstate->opts.force_quote)
|
||||||
{
|
{
|
||||||
|
@ -3419,10 +3419,18 @@ copy_opt_item:
|
|||||||
{
|
{
|
||||||
$$ = makeDefElem("force_not_null", (Node *) $4, @1);
|
$$ = makeDefElem("force_not_null", (Node *) $4, @1);
|
||||||
}
|
}
|
||||||
|
| FORCE NOT NULL_P '*'
|
||||||
|
{
|
||||||
|
$$ = makeDefElem("force_not_null", (Node *) makeNode(A_Star), @1);
|
||||||
|
}
|
||||||
| FORCE NULL_P columnList
|
| FORCE NULL_P columnList
|
||||||
{
|
{
|
||||||
$$ = makeDefElem("force_null", (Node *) $3, @1);
|
$$ = makeDefElem("force_null", (Node *) $3, @1);
|
||||||
}
|
}
|
||||||
|
| FORCE NULL_P '*'
|
||||||
|
{
|
||||||
|
$$ = makeDefElem("force_null", (Node *) makeNode(A_Star), @1);
|
||||||
|
}
|
||||||
| ENCODING Sconst
|
| ENCODING Sconst
|
||||||
{
|
{
|
||||||
$$ = makeDefElem("encoding", (Node *) makeString($2), @1);
|
$$ = makeDefElem("encoding", (Node *) makeString($2), @1);
|
||||||
|
@ -56,8 +56,10 @@ typedef struct CopyFormatOptions
|
|||||||
bool force_quote_all; /* FORCE_QUOTE *? */
|
bool force_quote_all; /* FORCE_QUOTE *? */
|
||||||
bool *force_quote_flags; /* per-column CSV FQ flags */
|
bool *force_quote_flags; /* per-column CSV FQ flags */
|
||||||
List *force_notnull; /* list of column names */
|
List *force_notnull; /* list of column names */
|
||||||
|
bool force_notnull_all; /* FORCE_NOT_NULL *? */
|
||||||
bool *force_notnull_flags; /* per-column CSV FNN flags */
|
bool *force_notnull_flags; /* per-column CSV FNN flags */
|
||||||
List *force_null; /* list of column names */
|
List *force_null; /* list of column names */
|
||||||
|
bool force_null_all; /* FORCE_NULL *? */
|
||||||
bool *force_null_flags; /* per-column CSV FN flags */
|
bool *force_null_flags; /* per-column CSV FN flags */
|
||||||
bool convert_selectively; /* do selective binary conversion? */
|
bool convert_selectively; /* do selective binary conversion? */
|
||||||
List *convert_select; /* list of column names (can be NIL) */
|
List *convert_select; /* list of column names (can be NIL) */
|
||||||
|
@ -520,6 +520,50 @@ BEGIN;
|
|||||||
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
|
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
|
||||||
ERROR: FORCE_NULL column "b" not referenced by COPY
|
ERROR: FORCE_NULL column "b" not referenced by COPY
|
||||||
ROLLBACK;
|
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 ''
|
\pset null ''
|
||||||
-- test case with whole-row Var in a check constraint
|
-- test case with whole-row Var in a check constraint
|
||||||
create table check_con_tbl (f1 int);
|
create table check_con_tbl (f1 int);
|
||||||
|
@ -344,6 +344,36 @@ ROLLBACK;
|
|||||||
BEGIN;
|
BEGIN;
|
||||||
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
|
COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b));
|
||||||
ROLLBACK;
|
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 ''
|
\pset null ''
|
||||||
|
|
||||||
-- test case with whole-row Var in a check constraint
|
-- test case with whole-row Var in a check constraint
|
||||||
|
Loading…
Reference in New Issue
Block a user