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 1w32et-000qBj-3A for pgsql-hackers@arkaria.postgresql.org; Thu, 19 Mar 2026 01:58:48 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w32er-00Fr4U-1N for pgsql-hackers@arkaria.postgresql.org; Thu, 19 Mar 2026 01:58:45 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w32er-00Fr4M-0C for pgsql-hackers@lists.postgresql.org; Thu, 19 Mar 2026 01:58:45 +0000 Received: from mail-vk1-xa2b.google.com ([2607:f8b0:4864:20::a2b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w32en-00000000SbD-1UTo for pgsql-hackers@postgresql.org; Thu, 19 Mar 2026 01:58:43 +0000 Received: by mail-vk1-xa2b.google.com with SMTP id 71dfb90a1353d-5673804da95so139750e0c.0 for ; Wed, 18 Mar 2026 18:58:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773885522; cv=none; d=google.com; s=arc-20240605; b=HiCx4E7184YcYpR+M+QZreMBVN5w1huwxQD8qqZG/192sajxbGSGAxWFAX7rRAUlx9 GBfrwR7OiuiajI6sQfaxI0u0OCSODOqX+0RwgaBSiqTKOaeIKeK4U0unT3uMVYPDBJb3 DBteH8lzwWw1jBusRqHDqOAo161xFSIgtcVWzYiSNkeSZIfdsyJvvgNW2i6kBD0rgoqO JwWqqcsr3dBKvIjyGM4wm9QIINvGB0WX4Uf4Lo3SomMIzNH9kbMMtlk5vvOsAiSgjMFR 0yc4J459ftd5mw4Ksu2c/iZXeNVVqWPt1xTaIxozC5DmlJxOgsCc9ufn6NEyxCZOu3g+ 6GLw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=2kCMeL60Pf2rm3ycHEL4LG4fDj9rb/bdcM8F4N/XOA0=; fh=fs2I+V1I6g4NRUBHbx+gtUI5Gd2jf+a+uG1a/QXbDww=; b=HqW7NoWXi+ztdDvOaIct+QTSpWWZM97e+5UOQ0SWlEzo/1yFAxOnTX25dKvT24XskF 9aE6IMmgVAJjYgW0UGrB6vAsgt3oIDU3V7g4XfmPi2sNCHEuJkrvhmn+n3Q80miruuAH 9FyrvjyHFe/pUZ4yOzKUQOTp9CycLJ0veymG9bSuWcXeYtkxz3s5x1TloXQPW8LsDE0V un4KsZqTUt7CBSd507uzjYx7xdZXPNCbcI7eohVBtaambwBDlq8pfntj0/89Y51lw7yL 0HTzZsOJskTNUNZ6x8berupIXZCl4uNW0w6TOE8qCLX5XAvTRxebi+4AsoMBum1/pU0P Db1g==; darn=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=20230601; t=1773885522; x=1774490322; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=2kCMeL60Pf2rm3ycHEL4LG4fDj9rb/bdcM8F4N/XOA0=; b=aWOpBzxf+hPmCVY+fIVTmSXttFgBMDpav0NNTR7tKq85Zza2qEcE9EFox20w+cTWTg +C4kNmi+PYqk/kzf9Rx/aTzZLpqW+wJw8QbfmaCdb3+TxZo4gMGMP5Vb4iJZl0kxV2ak 5YIlaNUHHEwdumIvNvns5Y+PoBXxEQVJIRPX66PNBKfhKPhwdMTYUJ/7Fve5oIhqNtB8 Tdej17UqpfC+SVSNxyQYsB5gZJRglQFn7TIxF/vXni+sfY3NhSPnEOqXxZVYn+BYPz8z ny/78iE2dBzmNmPUnu0cxCZPBanXVtdorEgGBqgeB0QRV6s3BuDVKX7eLptTmZsvZL+V VD4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773885522; x=1774490322; h=content-transfer-encoding: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=2kCMeL60Pf2rm3ycHEL4LG4fDj9rb/bdcM8F4N/XOA0=; b=QSf4Ahii5S/kZr6Jfpt3PCAORdqZkp741LN3lBtrx+abQ+zCzivXcyOCq8RP3mmsUB tZVXIVsCZN3kV+D2zsZqmlpfUBl+DTv5AHqU8Uf1NH9bLr6TyynSd4bBCke6/ukwjeF0 NcibyWmTA7AAtekC6qgwyaP3TaXKNkW8EA85ipM3XhLnY1LnXT+XzsR66geQTNIs5orX a5PitnDvRQ8BORCVZIThFi5ud9eI5cEjj7BZsJa67LtnkisYJciR4zzJN4f8gkISbT7s 9dJ3lq5uPPpvfB/Ze47z1wwJkqTh4p2bRbkSXUCQzKe+ICuY8cFXX9cq45U2sLFCbq8x rJCw== X-Forwarded-Encrypted: i=1; AJvYcCXe2CJJExe71RmV3l2yEfh2gZLkCGh1YtZAPKNxuhqiiiMTjSHWO1te0+ixsb9BteyV4fkLEUKUjremxnrH@postgresql.org X-Gm-Message-State: AOJu0YxICB1L5BP2GjL8kzdzae0dGdkrGFRoiI11A3lAykELy3MpGatw 538dL3rElx+P3BKTIXrk9mwvvHDhmtixOCQCU61R+ub4LJfHeQKxurYE4ZlULdZbRtAFs5x8bjJ NIYzV2cTYdQE0G0pIcGqOqFPT16DfwF4= X-Gm-Gg: ATEYQzznwj4Xa7DFN4hBtIkqB8S5ey9ypyX9wZiE5Inum5D+IEpLO6ewmgaQUckqxFc BXqfhbKY2RlZElACjANi3nHmTwipRZpc6WzU/rUQ5MLtpeFzV1ll7du3co9GaeIMhyaH6KqTwrq 3IiM8bxhsvmkwIyFyrdTEk0LTLyGCumROdloFRnSMQW9eNAa+T28HBGZ3M/GcSwI7Ign6UYhip9 3sG+/wnI3LmCSDuxUJXB3MVhGZ89k87AqNHSLfiqWQ1s58Qekn/OMQ0DcPD6sfSD8UtGPHTpaYi U37yqWYXL61homBsOgsO790sMZGacQGRZ4PBTbxGK3oYsBkmy5hpZhUU3e+IHtLTXaPAonNtd7M 62uQi5pQRET1sxmNj0qT/YoiKC9OLiCFKE7gE82gor3ZOPl4i4SYSPGw49CjHA9OF4WeEdxpSWK W8nGWCJrY5tm9r0W+jeODp X-Received: by 2002:a05:6102:c53:b0:5fd:efb0:8563 with SMTP id ada2fe7eead31-6027d330b1emr2131359137.26.1773885521658; Wed, 18 Mar 2026 18:58:41 -0700 (PDT) MIME-Version: 1.0 References: <964a3377-d957-4a06-ad02-8228aefa65fc@dunslane.net> In-Reply-To: From: jian he Date: Thu, 19 Mar 2026 09:58:05 +0800 X-Gm-Features: AaiRm51gl7QRBjnI5GFCCzmzx5xB1wJWTfuWRaErU5bwV0rFnhNpV8fQurBb7AY Message-ID: Subject: Re: Emitting JSON to file using COPY TO To: Daniel Verite Cc: Andrew Dunstan , Masahiko Sawada , Joe Conway , Junwang Zhao , Florents Tselai , "Andrey M. Borodin" , Dean Rasheed , Davin Shearer , PostgreSQL development Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Wed, Mar 18, 2026 at 10:37=E2=80=AFPM Daniel Verite wrote: > > Currently there's no difference in output between the null > json value and the SQL null. > > postgres=3D# create table tbl (j jsonb); > postgres=3D# insert into tbl values('null'); > postgres=3D# insert into tbl values(null); > postgres=3D# copy tbl to stdout with (format json); > {"j":null} > {"j":null} > > Does it have to be that way or are there valid distinct outputs > that we could use to avoid this ambiguity? > This is an existing (quite old) behavior of composite_to_json->datum_to_json_internal, IMHO. ``` if (is_null) { appendBinaryStringInfo(result, "null", strlen("null")); return; } ``` produce the same results as ``` case JSONTYPE_JSON: /* JSON and JSONB output will already be escaped */ outputstr =3D OidOutputFunctionCall(outfuncoid, val); appendStringInfoString(result, outputstr); pfree(outputstr); break; ``` Therefore I intended to document it as below: JSON Format When the json format is used, data is exported with one JSON object per line, where each line corresponds to a single record. The json format has no standard way to distinguish between an SQL NULL and a JSON null literal. In the examples that follow, the following table containing JSON data will be used: CREATE TABLE my_test (a jsonb, b int); INSERT INTO my_test VALUES ('null', 1), (NULL, 1); When exporting this table using the json format: COPY my_test TO STDOUT (FORMAT JSON); In the resulting output, both the SQL NULL and the JSON null are rendered identically: {"a":null,"b":1} {"a":null,"b":1} what do you think? -- jian https://www.enterprisedb.com/