Skip to content

Commit e591425

Browse files
Chao Li (Evan)hackorum
authored andcommitted
COPY JSON: use trailing commas in FORCE_ARRAY output
Change COPY TO ... FORMAT JSON, FORCE_ARRAY output to place commas at the end of each array element line, instead of at the beginning of the next line. Previously, output looked like this: ``` [ {"id":1} ,{"id":2} ] ``` This is valid JSON, but it is an unusual formatting style and can be surprising to readers. Make it emit the more conventional form instead: ``` [ {"id":1}, {"id":2} ] ``` Implement this without buffering the whole result by adjusting how JSON rows are terminated and how the separator is emitted between rows. Update the regression test output accordingly. Author: Chao Li <lic@highgo.com> Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com> Reviewed-by: Daniel Gustafsson <daniel@yesql.se> Reviewed-by: Alex Guo <guo.alex.hengchen@gmail.com> Discussion: https://postgr.es/m/DFAC4097-2559-4DED-B7D5-EB53B02E9DA3@gmail.com
1 parent 127ce77 commit e591425

2 files changed

Lines changed: 36 additions & 17 deletions

File tree

src/backend/commands/copyto.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ static void CopySendData(CopyToState cstate, const void *databuf, int datasize);
156156
static void CopySendString(CopyToState cstate, const char *str);
157157
static void CopySendChar(CopyToState cstate, char c);
158158
static void CopySendEndOfRow(CopyToState cstate);
159+
static void CopySendTextLikeLineTerminator(CopyToState cstate);
159160
static void CopySendTextLikeEndOfRow(CopyToState cstate);
160161
static void CopySendInt32(CopyToState cstate, int32 val);
161162
static void CopySendInt16(CopyToState cstate, int16 val);
@@ -349,6 +350,8 @@ CopyToJsonEnd(CopyToState cstate)
349350
{
350351
if (cstate->opts.force_array)
351352
{
353+
if (cstate->json_row_delim_needed)
354+
CopySendTextLikeLineTerminator(cstate);
352355
CopySendChar(cstate, ']');
353356
CopySendTextLikeEndOfRow(cstate);
354357
}
@@ -418,7 +421,11 @@ CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot)
418421
if (cstate->opts.force_array)
419422
{
420423
if (cstate->json_row_delim_needed)
424+
{
421425
CopySendChar(cstate, ',');
426+
CopySendTextLikeLineTerminator(cstate);
427+
CopySendChar(cstate, ' ');
428+
}
422429
else
423430
{
424431
/* first row needs no delimiter */
@@ -447,7 +454,10 @@ CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot)
447454
else
448455
CopySendData(cstate, cstate->json_buf->data, cstate->json_buf->len);
449456

450-
CopySendTextLikeEndOfRow(cstate);
457+
if (cstate->opts.force_array)
458+
CopySendEndOfRow(cstate);
459+
else
460+
CopySendTextLikeEndOfRow(cstate);
451461
}
452462

453463
/*
@@ -659,11 +669,10 @@ CopySendEndOfRow(CopyToState cstate)
659669
}
660670

661671
/*
662-
* Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
663-
* line termination and do common appropriate things for the end of row.
672+
* Append the platform-appropriate line termination for text-like output.
664673
*/
665-
static inline void
666-
CopySendTextLikeEndOfRow(CopyToState cstate)
674+
static void
675+
CopySendTextLikeLineTerminator(CopyToState cstate)
667676
{
668677
switch (cstate->copy_dest)
669678
{
@@ -682,6 +691,16 @@ CopySendTextLikeEndOfRow(CopyToState cstate)
682691
default:
683692
break;
684693
}
694+
}
695+
696+
/*
697+
* Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
698+
* line termination and do common appropriate things for the end of row.
699+
*/
700+
static inline void
701+
CopySendTextLikeEndOfRow(CopyToState cstate)
702+
{
703+
CopySendTextLikeLineTerminator(cstate);
685704

686705
/* Now take the actions related to the end of a row */
687706
CopySendEndOfRow(cstate);

src/test/regress/expected/copy.out

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ copy (values (1), (2)) TO stdout with (format json);
9494
{"column1":2}
9595
copy (select 1 union all select 2) to stdout with (format json, force_array true);
9696
[
97-
{"?column?":1}
98-
,{"?column?":2}
97+
{"?column?":1},
98+
{"?column?":2}
9999
]
100100
copy (values (1), (2)) TO stdout with (format json, force_array true);
101101
[
102-
{"column1":1}
103-
,{"column1":2}
102+
{"column1":1},
103+
{"column1":2}
104104
]
105105
copy copytest to stdout json;
106106
{"style":"DOS","test":"abc\r\ndef","filler":1}
@@ -159,17 +159,17 @@ ERROR: COPY FORCE_ARRAY can only be used with JSON mode
159159
-- force_array variants
160160
copy copytest to stdout (format json, force_array);
161161
[
162-
{"style":"DOS","test":"abc\r\ndef","filler":1}
163-
,{"style":"Unix","test":"abc\ndef","filler":2}
164-
,{"style":"Mac","test":"abc\rdef","filler":3}
165-
,{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb","filler":4}
162+
{"style":"DOS","test":"abc\r\ndef","filler":1},
163+
{"style":"Unix","test":"abc\ndef","filler":2},
164+
{"style":"Mac","test":"abc\rdef","filler":3},
165+
{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb","filler":4}
166166
]
167167
copy copytest(style, test) to stdout (format json, force_array true);
168168
[
169-
{"style":"DOS","test":"abc\r\ndef"}
170-
,{"style":"Unix","test":"abc\ndef"}
171-
,{"style":"Mac","test":"abc\rdef"}
172-
,{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb"}
169+
{"style":"DOS","test":"abc\r\ndef"},
170+
{"style":"Unix","test":"abc\ndef"},
171+
{"style":"Mac","test":"abc\rdef"},
172+
{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb"}
173173
]
174174
copy copytest to stdout (format json, force_array false);
175175
{"style":"DOS","test":"abc\r\ndef","filler":1}

0 commit comments

Comments
 (0)