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 1w3ZRK-001M3l-2W for pgsql-hackers@arkaria.postgresql.org; Fri, 20 Mar 2026 12:58:58 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w3ZRH-005zYE-0U for pgsql-hackers@arkaria.postgresql.org; Fri, 20 Mar 2026 12:58:55 +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 1w3ZRG-005zXx-22 for pgsql-hackers@lists.postgresql.org; Fri, 20 Mar 2026 12:58:55 +0000 Received: from mail-yw1-x1130.google.com ([2607:f8b0:4864:20::1130]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w3ZR9-00000000ATq-45kw for pgsql-hackers@postgresql.org; Fri, 20 Mar 2026 12:58:53 +0000 Received: by mail-yw1-x1130.google.com with SMTP id 00721157ae682-79a60975dc5so21135687b3.0 for ; Fri, 20 Mar 2026 05:58:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=joeconway.com; s=google; t=1774011527; x=1774616327; darn=postgresql.org; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=XmcUayxLR6Tlvr+pibaJ9V2OmdVDVXx3zmVBvUH1faw=; b=T4OpG0r6ZNyYFeBwbfqKrpx+vAXKTkFZjSJhZ4x1/ye/aVPW/GNbcAiDqO7B0lPn3c gvxzecfXhoyZU0Lg32bnBKEnHALky6WtBJfljCmZEyZziiTo9s+Ex1OJFMg+Vij8wYO1 66amCwEw/+ZcwRk1w2NO649LRbA9CXJ0zHgYkDC7WzuaUaJLZ5hKEawOSzwsChrJDRND brrbvsAyjSyJNeQ054XjQelbBT9MnLW5PqOZHTaNQP6+5QF/bDhnXDMK4+7Q6QiTa9Ym 3ruG22J97CPYcRWzzYLjap8KnaHuX8jma3xXzdnW738j5cXlwcofuThJyhAHmqAlejj/ IevQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774011527; x=1774616327; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=XmcUayxLR6Tlvr+pibaJ9V2OmdVDVXx3zmVBvUH1faw=; b=dQX7aH3vUJc4HIitgC55WkdgDFWcqVIf8HVOinKE7IFOOvshdKbOjJTalfF27KeR2V XUjM6oMBHZGajcKFKY3mqsdVGQEqusjkHrfPUMTfPfY3nhvzYWpZhctpmsf79kdqaEL7 3QnjIphfPFwjccGf2lWO1IM/SYv49m6KnT5kTxue0qxFZmK7S/mn+JmydKztcdwhjkVG S3xKqCZwApVFOyPhbDlQzxg1UInaXcDmdZ1m8S7ryTpizAXIEF0aMYhHHzc0OfLcbKyr sFZQ322FRShFo+39tNdgxakr+N3I/9GYhdAP9BSokrdU9TPJKI+SWRXAs62cJob/jVwA WySQ== X-Forwarded-Encrypted: i=1; AJvYcCXFTPx5gCGceu9aJAQWZi7KaWZ4KitxtKNxHCqMMI8NWqPRi2t9XuEoQrsjh7acCUSswBt1N/FxWbx/sH+S@postgresql.org X-Gm-Message-State: AOJu0YyAjsusvkoMPYLhAsEeB4wxrWOykQ+mzLWxrNpY5lD6FwwTagzS S+/f5oohTmeecStb39mAtzImYGsGek7ymJCzvrdH5AerdmBWL1iNlG4KKHv3cRcMwAw= X-Gm-Gg: ATEYQzw6pKLWsCoNNuWVkziScYo1WSNfFGlU5r3QX3u53vQ2SPR4b802nXpDjxebCXi e2786CQ/kQIqw+o+W+fxX54JjMRsgsl990do82kuJaGNF+7lNaiXAsBFh/++Q+L6GuLPE9DhoQe GZLCDmIjGOaurambkGEFc0aLxj/FlwkStPQrFHdm0h2vQtEJo0l/a8aKklZ61BfhY0wJyIvCClH k0R4oxDt7JHGzYHPfpIkc4WvHtrVXsUjI2L2PVukPEVB44o4ephoRmvYdP6jpRm87DeCmdIv/eR Sio/kFZo7vK+0whMKxBGU1MR1B9Vx9J20xLso49oFU+OrkTF9Y+Qh7uZLA0S6BbvtRA3VEpfi4e PRQbxoHuCJ4poJz8HluQgitBKds2zbDOLg7dSimXsUdEr6XA+fznciwuwvDgypMda4uIsHbbGko nKP0SZ8hcBJ1l3far8E0QlNyA7 X-Received: by 2002:a05:690c:c4e8:b0:79a:7a6e:3c51 with SMTP id 00721157ae682-79a90bf2035mr28115097b3.41.1774011527016; Fri, 20 Mar 2026 05:58:47 -0700 (PDT) Received: from [192.168.4.42] ([46.110.138.68]) by smtp.gmail.com with ESMTPSA id 00721157ae682-79a903f82e6sm15473207b3.17.2026.03.20.05.58.45 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 20 Mar 2026 05:58:46 -0700 (PDT) Message-ID: <373fe053-9a46-4d24-8c51-f4e46a79aa52@joeconway.com> Date: Fri, 20 Mar 2026 08:58:45 -0400 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Emitting JSON to file using COPY TO To: Andrew Dunstan , jian he , Daniel Verite Cc: Masahiko Sawada , Junwang Zhao , Florents Tselai , "Andrey M. Borodin" , Dean Rasheed , Davin Shearer , PostgreSQL development References: <964a3377-d957-4a06-ad02-8228aefa65fc@dunslane.net> <8428eb0f-f467-4cbd-a510-5be4f29b9ad7@dunslane.net> <90984087-723c-48e6-88ca-661ed15b2dd6@joeconway.com> <2732ec01-112c-40ae-8b17-a6df5f30b266@dunslane.net> Content-Language: en-US From: Joe Conway In-Reply-To: <2732ec01-112c-40ae-8b17-a6df5f30b266@dunslane.net> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 3/20/26 08:41, Andrew Dunstan wrote: > > On 2026-03-19 Th 12:06 PM, Joe Conway wrote: >> On 3/19/26 11:02, Andrew Dunstan wrote: >>> >>> On 2026-03-18 We 9:58 PM, jian he wrote: >>>> On Wed, Mar 18, 2026 at 10:37 PM Daniel Verite >>>> wrote: >>>>> Currently there's no difference in output between the null >>>>> json value and the SQL null. >>>>> >>>>> postgres=# create table tbl  (j jsonb); >>>>> postgres=# insert into tbl values('null'); >>>>> postgres=# insert into tbl values(null); >>>>> postgres=# 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 = 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? >>>> >>>> >>>> >>> >>> I can live with that, if others can. >> >> +1 >> WFM >> > > pushed with that addition. Awesome -- thanks for carrying that over the top! -- Joe Conway PostgreSQL Contributors Team Amazon Web Services: https://aws.amazon.com