Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wKWuG-0015gb-11 for pgsql-hackers@arkaria.postgresql.org; Wed, 06 May 2026 07:42:56 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wKWuE-00FVYo-2u for pgsql-hackers@arkaria.postgresql.org; Wed, 06 May 2026 07:42:54 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wKWuE-00FVYg-1h for pgsql-hackers@lists.postgresql.org; Wed, 06 May 2026 07:42:54 +0000 Received: from mail-yx1-xb12a.google.com ([2607:f8b0:4864:20::b12a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wKWuC-00000000mgf-1Vc5 for pgsql-hackers@lists.postgresql.org; Wed, 06 May 2026 07:42:54 +0000 Received: by mail-yx1-xb12a.google.com with SMTP id 956f58d0204a3-65c09c1d000so535149d50.1 for ; Wed, 06 May 2026 00:42:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1778053370; cv=none; d=google.com; s=arc-20240605; b=OG94Am5fVbvilkc7LTmwt7BIP7rRr+NCy+8yJLZp9cGHf5mToWuMKLEvP4HvhMJiDt 3EKFeSJyv9QjJ2q+Ti/4+RCDZN94feqXa25UEnNnufXO60ZtSUW1VQqKeeguykm+fJB1 vTtym9dqMcPgcu2u3rsfd1NjIWUQdm9sEGC+jrxGiTb/q7DklUVw7nHFChNrMdUcUa0M sizTC7yVnzf9xVtYJYsSTp6aMIq2jV/Nt+mgM8OEMEixO3PCnuSSflvZELHU3j0bTzXD pP7dtYCANM8X5P391gNckWjFZbsH7W5k01P6+7kOrrSOcj0BJWuBBwg7UvCIbIonMMNf aODA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=3RdaZSSwPB4aHRBiPQjLD2oz7ZU8vJJV6nqXiquHJ/Y=; fh=difWPyZhf+FMfWnUHiBJsrLSKQJL23D1+3HP+YAZNos=; b=dvi2OqaKBRYhsPjxknD5tD4qFxxedYeonNXTKJaDEDIahqQB4s/Whwg4oQPQlEFfAT tSrzzfyhN9OYSvrxO8dOT5rT75JVIdQbuzvu/iWv9SG+qRAJ3huULKrTM2MddBSGEvA7 CwQn//e+rFq67EajmHLbzWWmf2JQqw1uMcNEzTn4jk5fC5Om6+xr72LsUIW36eZ4IN6x Xgsj4nU8s04RRg83nZEnRUcSLKGTzNhaM2B1MZjgWuG+keKOSee+4oHfV++6cXeOBczX yP2O+8A2NpwM8dkTIoFuNXgufq9wWhi4Cti7P2gCJTYABYtr3gRramInkWRFUGTtCbZ3 llrw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778053370; x=1778658170; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=3RdaZSSwPB4aHRBiPQjLD2oz7ZU8vJJV6nqXiquHJ/Y=; b=rOrfkzvkwOMbgi45CdcT7cfbL/ls1YJO2tNqTjYfu0MV2R7g8vNv9+gysAd4RJ60ho Or+q55dailcxFJ2uGHa6ppRrMcfrxLpLuLTrj6Zx5lKeGs/Hes2B3anLgZP0PyQh5DHi 5QyCL22j9keYBHBMx+vM0NGuhYjT7x/GJT3cyM6NHj5f45b67ulnVs9GYPAtF/VD5oW/ 8t03dpoIrQWga9vYGBEOI+fcPp4aSnvwbjZbibDrF1+XQMrziA9U2G6rQqivRLiwaM9T 1fqZQMmoUe4Nj7ZYdU/OWh+Kz5WIysfANwAjUJofR6n7Gchfw5bNTlrKmSospU1hhAhr ii5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778053370; x=1778658170; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=3RdaZSSwPB4aHRBiPQjLD2oz7ZU8vJJV6nqXiquHJ/Y=; b=qCypaZyzRsHdoTSBnbpRt4rLwrBS+y4da0b/dHs5PJHXQhRsfViDsDVjX4Wb7WZ0o+ KHPcA1OvZVvGC3WftwGPbvZR6GknRKS5ToPqpBWaumHrq8wHfzYFJ2r6OvpDaLK9NKF0 fkGMxBDcvA23teWkhIK5K050MMAhY51GlSAplx4ZWxkLVzuOEA4+yMXIgiwWVfadHyLO C78xkU7cN29vpgsn/7JzpLgIFSHhI6cLlguZnIbb+S+MgbXk4XfwHSn4M2JjQYqVu4vV dHAbZzwCGwo13Va9vE+KKpAuCgQ+8QVWckVqVsMilx1t/Y9CpfVDqxJhGphPKSGYyrzh bt6w== X-Gm-Message-State: AOJu0YxV2dh6xriHLlOhdnG257qKBZCeL60CA+yrxWxdubzjnh2oBT8Q af045CeQBGdpxUHO/aEgRZ5tBF/Xk4PpVpMxfJAPCLw7w40vGSep2s+3/c4+O2MaWO0nre3UNsx FwVf6ZJsKZi7EXdGQeWZZ9PZiOgSiWt0= X-Gm-Gg: AeBDieurVHu3fMq8KCoXTQAfprMpfe5JvNnvRO7K7VYpgqK2aVeOIjUS4WpDj8UXyfq AeLlyQ5P63H46CGvhPOkTak6DbOpFxYASkCLkzFZDWDwdisgg6to+r4TIcSMojoLqI5Cv79Y/Fh kd0HbltSImFZOkIeMipRM8Zk3040aGHVKq2jU/r96/PwZ8ujf5KB8oeYjB3lzNkXW77k8HQg30N YbtA53P+eqU6bffPrs5iyMeEZSLVZ2i8Omd/W8XQLegPf4cpoNYp9vHu7kUfjDGCtBPz8NQ4lM+ a24kRdVI94sr2+C3 X-Received: by 2002:a05:690c:388:b0:7b2:cfae:8a0a with SMTP id 00721157ae682-7bdf6022205mr19587297b3.0.1778053370348; Wed, 06 May 2026 00:42:50 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Ayush Tiwari Date: Wed, 6 May 2026 13:12:38 +0530 X-Gm-Features: AVHnY4Lx5YUW5OupHe8wTnxs6BoWTJJzYBQnQdWAEIf2aoZ18nlp9PmLKscDN00 Message-ID: Subject: Re: COPY JSON: use trailing commas in FORCE_ARRAY output To: Chao Li Cc: PostgreSQL Hackers Content-Type: multipart/alternative; boundary="000000000000880a1e0651214f6e" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000880a1e0651214f6e Content-Type: text/plain; charset="UTF-8" Hi, On Wed, 6 May 2026 at 12:11, Chao Li wrote: > Hi, > > Another issue I found with COPY TO (FORMAT json, FORCE_ARRAY) is that it > places the delimiter comma at the beginning of the next line, like this: > ``` > evantest=# copy test_json_copy to stdout with (format json, force_array); > [ > > {"id":1,"name":"Alice","is_active":true,"tags":["dev","db"],"created_at":"2026-05-01"} > > ,{"id":2,"name":"Bob","is_active":false,"tags":["manager"],"created_at":"2026-05-02"} > ,{"id":3,"name":"Charlie","is_active":null,"tags":null,"created_at":null} > ,{"id":4,"name":"Special Case: > \"Quotes\"","is_active":true,"tags":["a","b"],"created_at":"2026-05-04"} > ] > ``` > > I was surprised by the comma placement. It is valid JSON, but it looks > quite uncommon. > > For comparison, the existing json_agg() places commas at the end of the > line: > ``` > evantest=# select json_agg(t) from (select id, name from test_json_copy) t; > json_agg > ---------------------------------------------- > [{"id":1,"name":"Alice"}, + > {"id":2,"name":"Bob"}, + > {"id":3,"name":"Charlie"}, + > {"id":4,"name":"Special Case: \"Quotes\""}] > (1 row) > ``` > > If this feature had already been released, I would not think it worth > changing just for formatting. But since "FORMAT json" is a new PG19 feature > and has not been released yet, I think it is better to make the output to > follow the more common style. > > This patch changes the output to place the comma at the end of the > previous line instead. The fix only adjusts how commas and newlines are > emitted. It does not buffer the whole result, so it should not have any > performance impact. > The patch looks good to me. I applied and the focused 'copy' regression test too passed. I agree that since FORMAT json is new for PG19, it is reasonable to adjust the formatting before release. The implementation still streams the output and only changes where the separator/newline are emitted. Regards, Ayush --000000000000880a1e0651214f6e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,


O= n Wed, 6 May 2026 at 12:11, Chao Li <li.evan.chao@gmail.com> wrote:
Hi,

Another issue I found with COPY TO (FORMAT json, FORCE_ARRAY) is that it pl= aces the delimiter comma at the beginning of the next line, like this:
```
evantest=3D# copy test_json_copy to stdout with (format json, force_array);=
[
{"id":1,"name":"Alice","is_active":= true,"tags":["dev","db"],"created_at&quo= t;:"2026-05-01"}
,{"id":2,"name":"Bob","is_active":f= alse,"tags":["manager"],"created_at":"20= 26-05-02"}
,{"id":3,"name":"Charlie","is_active&quo= t;:null,"tags":null,"created_at":null}
,{"id":4,"name":"Special Case: \"Quotes\"= ;","is_active":true,"tags":["a","b&= quot;],"created_at":"2026-05-04"}
]
```

I was surprised by the comma placement. It is valid JSON, but it looks quit= e uncommon.

For comparison, the existing json_agg() places commas at the end of the lin= e:
```
evantest=3D# select json_agg(t) from (select id, name from test_json_copy) = t;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0json_a= gg
----------------------------------------------
=C2=A0[{"id":1,"name":"Alice"},=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0+
=C2=A0 {"id":2,"name":"Bob"},=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0+
=C2=A0 {"id":3,"name":"Charlie"},=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0+
=C2=A0 {"id":4,"name":"Special Case: \"Quotes= \""}]
(1 row)
```

If this feature had already been released, I would not think it worth chang= ing just for formatting. But since "FORMAT json" is a new PG19 fe= ature and has not been released yet, I think it is better to make the outpu= t to follow the more common style.

This patch changes the output to place the comma at the end of the previous= line instead. The fix only adjusts how commas and newlines are emitted. It= does not buffer the whole result, so it should not have any performance im= pact.

The patch looks good to me.=C2=A0 I ap= plied and=C2=A0the=C2=A0focused 'copy'
regression test too passed.

I agree that s= ince FORMAT json is new for PG19, it is reasonable to adjust
the formatt= ing before release.=C2=A0 The implementation still streams the output
and only changes where the separator/newline are emitted.

=
Regards,
Ayush
--000000000000880a1e0651214f6e--