Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5139b02

Browse files
author
Commitfest Bot
committed
[CF 4716] v16 - COPY TO json
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/4716 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxGmSw5GUOquAT+q7B0k+xweU3NwcNc53fLjHQjsMeanaw@mail.gmail.com Author(s): Joe Conway
2 parents c3eda50 + 72f578b commit 5139b02

File tree

13 files changed

+378
-53
lines changed

13 files changed

+378
-53
lines changed

doc/src/sgml/ref/copy.sgml

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
4343
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
4444
FORCE_NOT_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
4545
FORCE_NULL { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
46+
FORCE_ARRAY [ <replaceable class="parameter">boolean</replaceable> ]
4647
ON_ERROR <replaceable class="parameter">error_action</replaceable>
4748
REJECT_LIMIT <replaceable class="parameter">maxerror</replaceable>
4849
ENCODING '<replaceable class="parameter">encoding_name</replaceable>'
@@ -219,10 +220,15 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
219220
Selects the data format to be read or written:
220221
<literal>text</literal>,
221222
<literal>csv</literal> (Comma Separated Values),
223+
<literal>json</literal> (JavaScript Object Notation),
222224
or <literal>binary</literal>.
223225
The default is <literal>text</literal>.
224226
See <xref linkend="sql-copy-file-formats"/> below for details.
225227
</para>
228+
<para>
229+
The <literal>json</literal> option is allowed only in
230+
<command>COPY TO</command>.
231+
</para>
226232
</listitem>
227233
</varlistentry>
228234

@@ -257,7 +263,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
257263
(line) of the file. The default is a tab character in text format,
258264
a comma in <literal>CSV</literal> format.
259265
This must be a single one-byte character.
260-
This option is not allowed when using <literal>binary</literal> format.
266+
This option is not allowed when using <literal>binary</literal> or <literal>json</literal> format.
261267
</para>
262268
</listitem>
263269
</varlistentry>
@@ -271,7 +277,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
271277
string in <literal>CSV</literal> format. You might prefer an
272278
empty string even in text format for cases where you don't want to
273279
distinguish nulls from empty strings.
274-
This option is not allowed when using <literal>binary</literal> format.
280+
This option is not allowed when using <literal>binary</literal> or <literal>json</literal> format.
275281
</para>
276282

277283
<note>
@@ -294,7 +300,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
294300
is found in the input file, the default value of the corresponding column
295301
will be used.
296302
This option is allowed only in <command>COPY FROM</command>, and only when
297-
not using <literal>binary</literal> format.
303+
not using <literal>binary</literal> or <literal>json</literal> format.
298304
</para>
299305
</listitem>
300306
</varlistentry>
@@ -310,7 +316,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
310316
If this option is set to <literal>MATCH</literal>, the number and names
311317
of the columns in the header line must match the actual column names of
312318
the table, in order; otherwise an error is raised.
313-
This option is not allowed when using <literal>binary</literal> format.
319+
This option is not allowed when using <literal>binary</literal> or <literal>json</literal> format.
314320
The <literal>MATCH</literal> option is only valid for <command>COPY
315321
FROM</command> commands.
316322
</para>
@@ -387,6 +393,19 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
387393
</listitem>
388394
</varlistentry>
389395

396+
<varlistentry>
397+
<term><literal>FORCE_ARRAY</literal></term>
398+
<listitem>
399+
<para>
400+
Force output of square brackets as array decorations at the beginning
401+
and end of output, and commas between the rows. It is allowed only in
402+
<command>COPY TO</command>, and only when using
403+
<literal>JSON</literal> format. The default is
404+
<literal>false</literal>.
405+
</para>
406+
</listitem>
407+
</varlistentry>
408+
390409
<varlistentry>
391410
<term><literal>ON_ERROR</literal></term>
392411
<listitem>

src/backend/commands/copy.c

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ ProcessCopyOptions(ParseState *pstate,
504504
bool on_error_specified = false;
505505
bool log_verbosity_specified = false;
506506
bool reject_limit_specified = false;
507+
bool force_array_specified = false;
507508
ListCell *option;
508509

509510
/* Support external use for option sanity checking */
@@ -525,11 +526,13 @@ ProcessCopyOptions(ParseState *pstate,
525526
errorConflictingDefElem(defel, pstate);
526527
format_specified = true;
527528
if (strcmp(fmt, "text") == 0)
528-
/* default format */ ;
529+
opts_out->format = COPY_FORMAT_TEXT;
529530
else if (strcmp(fmt, "csv") == 0)
530-
opts_out->csv_mode = true;
531+
opts_out->format = COPY_FORMAT_CSV;
531532
else if (strcmp(fmt, "binary") == 0)
532-
opts_out->binary = true;
533+
opts_out->format = COPY_FORMAT_BINARY;
534+
else if (strcmp(fmt, "json") == 0)
535+
opts_out->format = COPY_FORMAT_JSON;
533536
else
534537
ereport(ERROR,
535538
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -656,6 +659,13 @@ ProcessCopyOptions(ParseState *pstate,
656659
defel->defname),
657660
parser_errposition(pstate, defel->location)));
658661
}
662+
else if (strcmp(defel->defname, "force_array") == 0)
663+
{
664+
if (force_array_specified)
665+
errorConflictingDefElem(defel, pstate);
666+
force_array_specified = true;
667+
opts_out->force_array = defGetBoolean(defel);
668+
}
659669
else if (strcmp(defel->defname, "on_error") == 0)
660670
{
661671
if (on_error_specified)
@@ -689,31 +699,47 @@ ProcessCopyOptions(ParseState *pstate,
689699
* Check for incompatible options (must do these three before inserting
690700
* defaults)
691701
*/
692-
if (opts_out->binary && opts_out->delim)
702+
if (opts_out->format == COPY_FORMAT_BINARY && opts_out->delim)
693703
ereport(ERROR,
694704
(errcode(ERRCODE_SYNTAX_ERROR),
695705
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
696706
errmsg("cannot specify %s in BINARY mode", "DELIMITER")));
697707

698-
if (opts_out->binary && opts_out->null_print)
708+
if (opts_out->format == COPY_FORMAT_JSON && opts_out->delim)
709+
ereport(ERROR,
710+
errcode(ERRCODE_SYNTAX_ERROR),
711+
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
712+
errmsg("cannot specify %s in JSON mode", "DELIMITER"));
713+
714+
if (opts_out->format == COPY_FORMAT_BINARY && opts_out->null_print)
699715
ereport(ERROR,
700716
(errcode(ERRCODE_SYNTAX_ERROR),
701717
errmsg("cannot specify %s in BINARY mode", "NULL")));
702718

703-
if (opts_out->binary && opts_out->default_print)
719+
if (opts_out->format == COPY_FORMAT_JSON && opts_out->null_print)
720+
ereport(ERROR,
721+
errcode(ERRCODE_SYNTAX_ERROR),
722+
errmsg("cannot specify %s in JSON mode", "NULL"));
723+
724+
if (opts_out->format == COPY_FORMAT_BINARY && opts_out->default_print)
704725
ereport(ERROR,
705726
(errcode(ERRCODE_SYNTAX_ERROR),
706727
errmsg("cannot specify %s in BINARY mode", "DEFAULT")));
707728

729+
if (opts_out->format == COPY_FORMAT_JSON && opts_out->default_print)
730+
ereport(ERROR,
731+
errcode(ERRCODE_SYNTAX_ERROR),
732+
errmsg("cannot specify %s in JSON mode", "DEFAULT"));
733+
708734
/* Set defaults for omitted options */
709735
if (!opts_out->delim)
710-
opts_out->delim = opts_out->csv_mode ? "," : "\t";
736+
opts_out->delim = opts_out->format == COPY_FORMAT_CSV ? "," : "\t";
711737

712738
if (!opts_out->null_print)
713-
opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
739+
opts_out->null_print = opts_out->format == COPY_FORMAT_CSV ? "" : "\\N";
714740
opts_out->null_print_len = strlen(opts_out->null_print);
715741

716-
if (opts_out->csv_mode)
742+
if (opts_out->format == COPY_FORMAT_CSV)
717743
{
718744
if (!opts_out->quote)
719745
opts_out->quote = "\"";
@@ -761,51 +787,56 @@ ProcessCopyOptions(ParseState *pstate,
761787
* future-proofing. Likewise we disallow all digits though only octal
762788
* digits are actually dangerous.
763789
*/
764-
if (!opts_out->csv_mode &&
790+
if (opts_out->format != COPY_FORMAT_CSV &&
765791
strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
766792
opts_out->delim[0]) != NULL)
767793
ereport(ERROR,
768794
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
769795
errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
770796

771797
/* Check header */
772-
if (opts_out->binary && opts_out->header_line)
798+
if (opts_out->format == COPY_FORMAT_BINARY && opts_out->header_line)
773799
ereport(ERROR,
774800
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
775801
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
776802
errmsg("cannot specify %s in BINARY mode", "HEADER")));
777803

804+
if (opts_out->format == COPY_FORMAT_JSON && opts_out->header_line)
805+
ereport(ERROR,
806+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
807+
errmsg("cannot specify %s in JSON mode", "HEADER"));
808+
778809
/* Check quote */
779-
if (!opts_out->csv_mode && opts_out->quote != NULL)
810+
if (opts_out->format != COPY_FORMAT_CSV && opts_out->quote != NULL)
780811
ereport(ERROR,
781812
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
782813
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
783814
errmsg("COPY %s requires CSV mode", "QUOTE")));
784815

785-
if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
816+
if (opts_out->format == COPY_FORMAT_CSV && strlen(opts_out->quote) != 1)
786817
ereport(ERROR,
787818
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
788819
errmsg("COPY quote must be a single one-byte character")));
789820

790-
if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
821+
if (opts_out->format == COPY_FORMAT_CSV && opts_out->delim[0] == opts_out->quote[0])
791822
ereport(ERROR,
792823
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
793824
errmsg("COPY delimiter and quote must be different")));
794825

795826
/* Check escape */
796-
if (!opts_out->csv_mode && opts_out->escape != NULL)
827+
if (opts_out->format != COPY_FORMAT_CSV && opts_out->escape != NULL)
797828
ereport(ERROR,
798829
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
799830
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
800831
errmsg("COPY %s requires CSV mode", "ESCAPE")));
801832

802-
if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
833+
if (opts_out->format == COPY_FORMAT_CSV && strlen(opts_out->escape) != 1)
803834
ereport(ERROR,
804835
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
805836
errmsg("COPY escape must be a single one-byte character")));
806837

807838
/* Check force_quote */
808-
if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
839+
if (opts_out->format != COPY_FORMAT_CSV && (opts_out->force_quote || opts_out->force_quote_all))
809840
ereport(ERROR,
810841
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
811842
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
@@ -819,8 +850,8 @@ ProcessCopyOptions(ParseState *pstate,
819850
"COPY FROM")));
820851

821852
/* Check force_notnull */
822-
if (!opts_out->csv_mode && (opts_out->force_notnull != NIL ||
823-
opts_out->force_notnull_all))
853+
if (opts_out->format != COPY_FORMAT_CSV && (opts_out->force_notnull != NIL ||
854+
opts_out->force_notnull_all))
824855
ereport(ERROR,
825856
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
826857
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
@@ -835,8 +866,8 @@ ProcessCopyOptions(ParseState *pstate,
835866
"COPY TO")));
836867

837868
/* Check force_null */
838-
if (!opts_out->csv_mode && (opts_out->force_null != NIL ||
839-
opts_out->force_null_all))
869+
if (opts_out->format != COPY_FORMAT_CSV && (opts_out->force_null != NIL ||
870+
opts_out->force_null_all))
840871
ereport(ERROR,
841872
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
842873
/*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
@@ -860,7 +891,7 @@ ProcessCopyOptions(ParseState *pstate,
860891
"NULL")));
861892

862893
/* Don't allow the CSV quote char to appear in the null string. */
863-
if (opts_out->csv_mode &&
894+
if (opts_out->format == COPY_FORMAT_CSV &&
864895
strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
865896
ereport(ERROR,
866897
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -877,6 +908,17 @@ ProcessCopyOptions(ParseState *pstate,
877908
errmsg("COPY %s cannot be used with %s", "FREEZE",
878909
"COPY TO")));
879910

911+
/* Check json format */
912+
if (opts_out->format == COPY_FORMAT_JSON && is_from)
913+
ereport(ERROR,
914+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
915+
errmsg("COPY json mode cannot be used with %s", "COPY FROM"));
916+
917+
if (opts_out->format != COPY_FORMAT_JSON && opts_out->force_array)
918+
ereport(ERROR,
919+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
920+
errmsg("COPY %s can only used with JSON mode", "FORCE_ARRAY"));
921+
880922
if (opts_out->default_print)
881923
{
882924
if (!is_from)
@@ -896,7 +938,7 @@ ProcessCopyOptions(ParseState *pstate,
896938
"DEFAULT")));
897939

898940
/* Don't allow the CSV quote char to appear in the default string. */
899-
if (opts_out->csv_mode &&
941+
if (opts_out->format == COPY_FORMAT_CSV &&
900942
strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
901943
ereport(ERROR,
902944
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -913,7 +955,7 @@ ProcessCopyOptions(ParseState *pstate,
913955
errmsg("NULL specification and DEFAULT specification cannot be the same")));
914956
}
915957
/* Check on_error */
916-
if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
958+
if (opts_out->format == COPY_FORMAT_BINARY && opts_out->on_error != COPY_ON_ERROR_STOP)
917959
ereport(ERROR,
918960
(errcode(ERRCODE_SYNTAX_ERROR),
919961
errmsg("only ON_ERROR STOP is allowed in BINARY mode")));

src/backend/commands/copyfrom.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ static const CopyFromRoutine CopyFromRoutineBinary = {
155155
static const CopyFromRoutine *
156156
CopyFromGetRoutine(const CopyFormatOptions *opts)
157157
{
158-
if (opts->csv_mode)
158+
if (opts->format == COPY_FORMAT_CSV)
159159
return &CopyFromRoutineCSV;
160-
else if (opts->binary)
160+
else if (opts->format == COPY_FORMAT_BINARY)
161161
return &CopyFromRoutineBinary;
162162

163163
/* default is text */
@@ -261,7 +261,7 @@ CopyFromErrorCallback(void *arg)
261261
cstate->cur_relname);
262262
return;
263263
}
264-
if (cstate->opts.binary)
264+
if (cstate->opts.format == COPY_FORMAT_BINARY)
265265
{
266266
/* can't usefully display the data */
267267
if (cstate->cur_attname)

src/backend/commands/copyfromparse.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ ReceiveCopyBegin(CopyFromState cstate)
171171
{
172172
StringInfoData buf;
173173
int natts = list_length(cstate->attnumlist);
174-
int16 format = (cstate->opts.binary ? 1 : 0);
174+
int16 format = (cstate->opts.format == COPY_FORMAT_BINARY ? 1 : 0);
175175
int i;
176176

177177
pq_beginmessage(&buf, PqMsg_CopyInResponse);
@@ -747,7 +747,7 @@ bool
747747
NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields)
748748
{
749749
return NextCopyFromRawFieldsInternal(cstate, fields, nfields,
750-
cstate->opts.csv_mode);
750+
cstate->opts.format == COPY_FORMAT_CSV);
751751
}
752752

753753
/*
@@ -774,7 +774,7 @@ NextCopyFromRawFieldsInternal(CopyFromState cstate, char ***fields, int *nfields
774774
bool done;
775775

776776
/* only available for text or csv input */
777-
Assert(!cstate->opts.binary);
777+
Assert(!(cstate->opts.format == COPY_FORMAT_BINARY));
778778

779779
/* on input check that the header line is correct if needed */
780780
if (cstate->cur_lineno == 0 && cstate->opts.header_line)

0 commit comments

Comments
 (0)