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 1w2CdL-00052I-12 for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 18:25:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2CdI-00BriJ-2F for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 18:25:41 +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 1w2CdI-00BrgJ-0q for pgsql-hackers@lists.postgresql.org; Mon, 16 Mar 2026 18:25:41 +0000 Received: from mail-lf1-x130.google.com ([2a00:1450:4864:20::130]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2CdF-000000003LF-2yE6 for pgsql-hackers@postgresql.org; Mon, 16 Mar 2026 18:25:40 +0000 Received: by mail-lf1-x130.google.com with SMTP id 2adb3069b0e04-5a12cd0bcd8so5643488e87.3 for ; Mon, 16 Mar 2026 11:25:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773685536; cv=none; d=google.com; s=arc-20240605; b=XEysSVgsxyu8gO24d5FqMQWcKuh7Vqo8JauYRieb2RTP/78e8KMwS7qDGlAp4D6V/e 1tOqjLxw7PsMPQ/qMIvJECDymls2+SpZKICjUXyk0c9+nWGRNAdZNF3F/gfngTNWOsxY zhIOam72eKr9xdHAPJiazTL5Iqv7ftip59426uoeW5GFH57mq3XkIi4Dmje4W+TWcWEo anRyT+Vq2FqPvlU4NIYP20gX8BhMCjUY5smQLcVHASkXrmO97CAgx6igUUt0KCDeP8ic xOGVqbFMssZ5LinOSMGOXQuRt7W7mysW6LGdSUTZkDMstjK/JPVn+AXEyW81iWDwRF54 rSYg== 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=y45lCpmz+nx8BovJ6hcwEiB3IGywuqh9QIuscie2Otk=; fh=X7tSPXINhmdArhVQtthsGlP92GC6gLhwJB/OFl5754Q=; b=dGyaIqo1Uw3itffog54Yzp81ZVW1UYyrvMxMXf6ilOzL/kSLmP50qlnQkS+I120MjG PFVfchZ3PcaU9a8Na/O4zMRPCXuxQ3+gaEu530DANFm+nPev8aaChO5ytByCRQnv//+l Wv4pjc9cH1owRMQ8D9pEtrCYl19F5GeDOliaFxSlrYPXRrPt8CnKTu32wGeOgzDOv+/e JTHHsnLF+ysZSUIdqcXV+QsphxneVl2Rt0W6dV55hdkkJsw0U4YxmRj82BpM9snEDPI3 RK43RA28JbKJ4BF04VPTdKNW5bgREPXu8SCn+Tw+F5p2vW7+a+bWUTNC3zWyB3/+n1s3 JlyA==; 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=1773685536; x=1774290336; 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=y45lCpmz+nx8BovJ6hcwEiB3IGywuqh9QIuscie2Otk=; b=ZB2wtwKDOYzW/U8rOPMESvWpFTQ67rdIGPemUfMEccH/5y1OkmF3Q6i4Vb067Ca7wJ J7K03HS713zkLeuKswmeWyjUAh7EY0/QmJXDIfwfaaddKOHTnwbUU+qKDE2rxXblNhHF Gqa37xV3q+1WuvRsha/91lQKly4eGBGbcQ9J2t+XUWZUOtjBXxh0xuz7DOd/52JYXIox WcubLTvXNZ8JhJMVQxsCcX1PYDzaa1/nvbbqDD1zYpcDhGh6FPV8JTgNLiIQ9YiQsQ3q qUmTsEXdnPDji8xKd31A43MNfdbdhGxdn2K0Lh8Wss9jhgpLVcNjuLu2pSJjliT4c38j 6FIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773685536; x=1774290336; 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=y45lCpmz+nx8BovJ6hcwEiB3IGywuqh9QIuscie2Otk=; b=TshMwktNU4uNlXLCY6OMNJnfVyUi9uTkttjA2rS6ZP2Y56UcBZwTvMjZDEK7O5TRI6 V5bTNmix6S/IkFOy9FUsvf9xqaLG9rbtQt+ocxaogvPI6m/kWYj18p18J9WIdIh+xaj9 Guv3OGMLT1zKU2p6dz2ibwcIB8Pg6C55SN13wdOK3SxGTMshGD5BylIuyQhNY6x30V+V W8XfVd/PPugm1e5t/HLcg3IFSzdS+0WjV9yiJND9JkG4XNE3sTri8TCxE/HHBg2RkD8V OY+pEbZuwe8FZskKktE++7H36+iDvIsh/B9Hlaem2eCoe3FHeDUpg+3nlJO5JeKjLDrB tMzA== X-Forwarded-Encrypted: i=1; AJvYcCVS8Ts0CVs9lrkYJoNlmzGye1VkuPCGgwxfd/8rhQz6hoscN99TiZDETVxNN3njQ7SkHReJVcqqsNuh1Afs@postgresql.org X-Gm-Message-State: AOJu0YzLNqlpy3Dq7z8ztfUxw3yQnD86TiHjHmfu/HNevULWaXmWZ/xY 8grwFliXwvRVHwPyKP+Tipe2DxhcPT/GuDZnYTXabuYZhKg/j8V/r6wS/xzjgrSNu9kXBrgGi1D XS8STfjF/Z6yw+fppDsL69cjOqy5qq60= X-Gm-Gg: ATEYQzyrJ7U4+OPnrrjT/UP8z61Q65nFCc4F9Y7zN2vQWDnuKmyo9c12XOKFfl+9fjf Tx/lx9HEsMKBbR8dp+SDeaLmdFHtR/u0Rxo9VJbr2xy7DbjMmaD/lPyXY5PGaGP12SuNgzESMA0 b1KiGOD2JH1YPViPZwfRTPCmSQUE/EIWGKeVe6RsJdRZ5Xx04pNNunqZYFLYKvmq7bHgpDYpFsn wToVtKFHJJ2a7q+jhvBIE//+3RFk/sREc4FSW4SEljoNfHui4495G8dhpBqJ8pb8fYlJbp2qkPN 6lOBYh8JNnscr0wwot4= X-Received: by 2002:ac2:5318:0:b0:5a1:33bb:ab3c with SMTP id 2adb3069b0e04-5a162705eddmr3385880e87.5.1773685535982; Mon, 16 Mar 2026 11:25:35 -0700 (PDT) MIME-Version: 1.0 References: <74d3b4e2-d201-489d-9b9a-1a6d0eb492a6@dunslane.net> <6cbbf7f3-da4a-4201-98a6-4347a3c35db0@joeconway.com> <8154f8e2-7d9c-442f-a7cb-a5caf38f6a12@dunslane.net> <57db4b6e-51ba-4984-9343-bc62a3ee0b7c@dunslane.net> In-Reply-To: From: Masahiko Sawada Date: Mon, 16 Mar 2026 11:24:58 -0700 X-Gm-Features: AaiRm516WfcQLRb8PLPtAV7ZjQ604R7tyZzquYtR3rjKpfrkP0HQ0otQJSljyJE Message-ID: Subject: Re: Emitting JSON to file using COPY TO To: jian he Cc: Andrew Dunstan , Joe Conway , Junwang Zhao , Florents Tselai , "Andrey M. Borodin" , Dean Rasheed , Daniel Verite , 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 Sun, Mar 8, 2026 at 8:49=E2=80=AFPM jian he wrote: > > On Mon, Mar 9, 2026 at 3:44=E2=80=AFAM Andrew Dunstan wrote: > > > > Hmm. But should we be scribbling on slot->tts_tupleDescriptor like that= ? > > How about something like this?: > > > > - * Full table or query without column list. Ensure the slot us= es > > - * cstate->tupDesc so that the datum is stamped with the right = type; > > - * for queries output type is RECORDOID this must be the blesse= d > > - * descriptor so that composite_to_json can look it up via > > - * lookup_rowtype_tupdesc. > > + * Full table or query without column list. For queries, the s= lot's > > + * TupleDesc may carry RECORDOID, which is not registered in th= e > > type > > + * cache and would cause composite_to_json's lookup_rowtype_tup= desc > > + * call to fail. Build a HeapTuple stamped with the blessed > > + * descriptor so the type can be looked up correctly. > > */ > > if (!cstate->rel && slot->tts_tupleDescriptor->tdtypeid =3D=3D > > RECORDOID) > > - slot->tts_tupleDescriptor =3D cstate->queryDesc->tupDesc; > > + { > > + HeapTuple tup; > > > > - rowdata =3D ExecFetchSlotHeapTupleDatum(slot); > > + tup =3D heap_form_tuple(cstate->tupDesc, > > + slot->tts_values, > > + slot->tts_isnull); > > + rowdata =3D HeapTupleGetDatum(tup); > > + } > > + else > > + { > > + rowdata =3D ExecFetchSlotHeapTupleDatum(slot); > > + } > > > This is better. I've tried to get rid of json_projvalues and json_projnul= ls. > Just using heap_form_tuple, but it won't work. > > I incorporated the v28-0004 COPY column list into v9-0002. > With this patch set, we added four fields to the struct CopyToStateData. > > + StringInfo json_buf; /* reusable buffer for JSON output, > + * initialized in BeginCopyTo */ > + TupleDesc tupDesc; /* Descriptor for JSON output; for a co= lumn > + * list this is a projected descriptor *= / > + Datum *json_projvalues; /* pre-allocated projection values,= or > + * NULL */ > + bool *json_projnulls; /* pre-allocated projection nulls, or NU= LL */ > > Using the script in > https://www.postgresql.org/message-id/CACJufxFFZqxC3p4WjpTEi4riaJm%3DpADX= %2Bpy0yQ0%3DRWTn5cqK3Q%40mail.gmail.com > I tested it again on macOS and Linux, and there are no regressions for > COPY TO with the TEXT and CSV formats. I've reviewed the patch and have some comments: --- I got a SEGV in the following scenario: postgres(1:1197708)=3D# create table test (a int, b text, c jsonb); CREATE TABLE postgres(1:1197708)=3D# copy test(a, b) to stdout with (format 'json' ); TRAP: failed Assert("tupdesc->firstNonCachedOffsetAttr >=3D 0"), File: "execTuples.c", Line: 2328, PID: 1197708 postgres: masahiko postgres [local] COPY(ExceptionalCondition+0x9e) [0xbebe= 48] postgres: masahiko postgres [local] COPY(BlessTupleDesc+0x2b) [0x729b50] postgres: masahiko postgres [local] COPY(BeginCopyTo+0xc94) [0x637bdf] postgres: masahiko postgres [local] COPY(DoCopy+0xb68) [0x62afbc] postgres: masahiko postgres [local] COPY(standard_ProcessUtility+0xa22) [0xa0ba48] postgres: masahiko postgres [local] COPY(ProcessUtility+0x10e) [0xa0b01f] postgres: masahiko postgres [local] COPY() [0xa09872] postgres: masahiko postgres [local] COPY() [0xa09acf] postgres: masahiko postgres [local] COPY(PortalRun+0x2c8) [0xa0901d] postgres: masahiko postgres [local] COPY() [0xa02055] postgres: masahiko postgres [local] COPY(PostgresMain+0xaf1) [0xa0724e] postgres: masahiko postgres [local] COPY() [0x9fdab9] postgres: masahiko postgres [local] COPY(postmaster_child_launch+0x165) [0x905378] postgres: masahiko postgres [local] COPY() [0x90b600] postgres: masahiko postgres [local] COPY() [0x908e6a] postgres: masahiko postgres [local] COPY(PostmasterMain+0x14fe) [0x90880c] postgres: masahiko postgres [local] COPY(main+0x340) [0x7a1f9c] It seems to forget to call TupleDescFinalize(). And I think we need some regression tests for this case. --- + if (cstate->opts.format =3D=3D COPY_FORMAT_JSON) + { + /* + * If FORCE_ARRAY has been specified, send the opening brac= ket. + */ + if (cstate->opts.force_array) + { + CopySendChar(cstate, '['); + CopySendTextLikeEndOfRow(cstate); + } + } We can conjunct the two if statement conditions. Regards, --=20 Masahiko Sawada Amazon Web Services: https://aws.amazon.com