v2-0001-COPY-JSON-use-trailing-commas-in-FORCE_ARRAY-outp.patch
application/octet-stream
Filename: v2-0001-COPY-JSON-use-trailing-commas-in-FORCE_ARRAY-outp.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0001
Subject: COPY JSON: use trailing commas in FORCE_ARRAY output
| File | + | − |
|---|---|---|
| src/backend/commands/copyto.c | 24 | 5 |
| src/test/regress/expected/copy.out | 12 | 12 |
From 4f2151348d89f9417d161729b5b717d32804d702 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 6 May 2026 14:14:03 +0800
Subject: [PATCH v2] 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
---
src/backend/commands/copyto.c | 29 ++++++++++++++++++++++++-----
src/test/regress/expected/copy.out | 24 ++++++++++++------------
2 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c
index 85d15353647..e98a15dcd64 100644
--- a/src/backend/commands/copyto.c
+++ b/src/backend/commands/copyto.c
@@ -156,6 +156,7 @@ static void CopySendData(CopyToState cstate, const void *databuf, int datasize);
static void CopySendString(CopyToState cstate, const char *str);
static void CopySendChar(CopyToState cstate, char c);
static void CopySendEndOfRow(CopyToState cstate);
+static void CopySendTextLikeLineTerminator(CopyToState cstate);
static void CopySendTextLikeEndOfRow(CopyToState cstate);
static void CopySendInt32(CopyToState cstate, int32 val);
static void CopySendInt16(CopyToState cstate, int16 val);
@@ -349,6 +350,8 @@ CopyToJsonEnd(CopyToState cstate)
{
if (cstate->opts.force_array)
{
+ if (cstate->json_row_delim_needed)
+ CopySendTextLikeLineTerminator(cstate);
CopySendChar(cstate, ']');
CopySendTextLikeEndOfRow(cstate);
}
@@ -418,7 +421,11 @@ CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot)
if (cstate->opts.force_array)
{
if (cstate->json_row_delim_needed)
+ {
CopySendChar(cstate, ',');
+ CopySendTextLikeLineTerminator(cstate);
+ CopySendChar(cstate, ' ');
+ }
else
{
/* first row needs no delimiter */
@@ -429,7 +436,10 @@ CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot)
CopySendData(cstate, cstate->json_buf->data, cstate->json_buf->len);
- CopySendTextLikeEndOfRow(cstate);
+ if (cstate->opts.force_array)
+ CopySendEndOfRow(cstate);
+ else
+ CopySendTextLikeEndOfRow(cstate);
}
/*
@@ -641,11 +651,10 @@ CopySendEndOfRow(CopyToState cstate)
}
/*
- * Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
- * line termination and do common appropriate things for the end of row.
+ * Append the platform-appropriate line termination for text-like output.
*/
-static inline void
-CopySendTextLikeEndOfRow(CopyToState cstate)
+static void
+CopySendTextLikeLineTerminator(CopyToState cstate)
{
switch (cstate->copy_dest)
{
@@ -664,6 +673,16 @@ CopySendTextLikeEndOfRow(CopyToState cstate)
default:
break;
}
+}
+
+/*
+ * Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
+ * line termination and do common appropriate things for the end of row.
+ */
+static inline void
+CopySendTextLikeEndOfRow(CopyToState cstate)
+{
+ CopySendTextLikeLineTerminator(cstate);
/* Now take the actions related to the end of a row */
CopySendEndOfRow(cstate);
diff --git a/src/test/regress/expected/copy.out b/src/test/regress/expected/copy.out
index 1714faab39c..3da23de8551 100644
--- a/src/test/regress/expected/copy.out
+++ b/src/test/regress/expected/copy.out
@@ -85,13 +85,13 @@ copy (values (1), (2)) TO stdout with (format json);
{"column1":2}
copy (select 1 union all select 2) to stdout with (format json, force_array true);
[
- {"?column?":1}
-,{"?column?":2}
+ {"?column?":1},
+ {"?column?":2}
]
copy (values (1), (2)) TO stdout with (format json, force_array true);
[
- {"column1":1}
-,{"column1":2}
+ {"column1":1},
+ {"column1":2}
]
copy copytest to stdout json;
{"style":"DOS","test":"abc\r\ndef","filler":1}
@@ -150,17 +150,17 @@ ERROR: COPY FORCE_ARRAY can only be used with JSON mode
-- force_array variants
copy copytest to stdout (format json, force_array);
[
- {"style":"DOS","test":"abc\r\ndef","filler":1}
-,{"style":"Unix","test":"abc\ndef","filler":2}
-,{"style":"Mac","test":"abc\rdef","filler":3}
-,{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb","filler":4}
+ {"style":"DOS","test":"abc\r\ndef","filler":1},
+ {"style":"Unix","test":"abc\ndef","filler":2},
+ {"style":"Mac","test":"abc\rdef","filler":3},
+ {"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb","filler":4}
]
copy copytest(style, test) to stdout (format json, force_array true);
[
- {"style":"DOS","test":"abc\r\ndef"}
-,{"style":"Unix","test":"abc\ndef"}
-,{"style":"Mac","test":"abc\rdef"}
-,{"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb"}
+ {"style":"DOS","test":"abc\r\ndef"},
+ {"style":"Unix","test":"abc\ndef"},
+ {"style":"Mac","test":"abc\rdef"},
+ {"style":"esc\\ape","test":"a\\r\\\r\\\n\\nb"}
]
copy copytest to stdout (format json, force_array false);
{"style":"DOS","test":"abc\r\ndef","filler":1}
--
2.50.1 (Apple Git-155)