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 1w2YZY-000Oad-0h for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 17:51:16 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2YZX-003tX3-06 for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 17:51:15 +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 1w2YZW-003tWv-2I for pgsql-hackers@lists.postgresql.org; Tue, 17 Mar 2026 17:51:14 +0000 Received: from mail-lf1-x12e.google.com ([2a00:1450:4864:20::12e]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2YZT-00000000e5K-49Bo for pgsql-hackers@postgresql.org; Tue, 17 Mar 2026 17:51:14 +0000 Received: by mail-lf1-x12e.google.com with SMTP id 2adb3069b0e04-5a27992d47cso113473e87.1 for ; Tue, 17 Mar 2026 10:51:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773769872; cv=none; d=google.com; s=arc-20240605; b=N7maZ23bFOQbu7Ne3C8SimIYWfLVYK2LlrJfBAwf+3ZVdiBqZB/ZSOCh3SSQtsM0wo 4fo0itVw8WlPh+ZrdB4rCO+dEGZlwE0Pxp8c/B0l/dxZUsWdY0uVz078pLv2BpUbDMI2 Eb0UyppiVrQjiuGQZYcwSSfkWXN1OQxqwsEpDLQpOTT8gZ3kgtdhT2YkPH8LGJAdk2/Y dYUWZDEWMqi0CzWoDxnKCRIiq6rlBQ84B7smYs3wh3KkLm1/HAViDPSwbH/2eU0skEVA CGZzQFhlOrV+bToKbNbv3MJgz4Y8YW4/YJRzht/oP3xWQfhpmLGRULFVRMhW+AP9bTL3 4C6w== 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=Jr4mHE3S+HB1/KJbo9Bm7jtZb/OECwOWPM/dFZRIJTk=; fh=JH8MKB1U8Rcwph7BEW6r8PhgaTKLPi7gcHG9xIsyJ4c=; b=ajNrKr0VTqy+TzfZhQEahaow2NrW6ynOz+pz+C/vPXqm4wZC7TFEfxxjbKbhBCY9YM RJ6UNiYg1npZW0RhqfVulY1ahLuLGbbCBkPLXODB8IPdboRFKztChm7RVcLyNJk+JJPz +aVhLggE24eXnfdQIa8PApNfdc7QFY/925PLwH2ipIQoGLFSGxywCNifcSq52TB/597+ hQeX5z7Gso321kX7RzfFHK83OgXDiFmrpR9Uvl4kOaz1LMNAavbBarGeiTuqt4oN9T6Y Jhx+fAZLOFE6YARLUrA7/4llDrHuYiVvFaewsWex0f7XRfT6abL/2X8ShpdGmlUraVuQ AxnA==; 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=1773769872; x=1774374672; 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=Jr4mHE3S+HB1/KJbo9Bm7jtZb/OECwOWPM/dFZRIJTk=; b=S8+/jE0obyqSDWTbOd9SUmNZveYVdj1Fcjl9MhAZZQ4ZknFrUzDlMrTzcQ9FfSzV2x zra0bwV3CXLOMhDmx+3oPO/CSKzn5IKIoQMuHlL/+Bn1k9qeLkQx+IFmgt19/hrcn4dM pHCFj7cO+jMC+Ci0jgfPe1jbOoKPxJ3GiXJttmiCGxt6vAjOQwgokpryfRlnylKKurlv wLArZIwkpfKGpNN6zuolWFfJgT0OOowFFCXS5G8gKneEZm9t8U2kKszxGrwGB54DtEMv +CdCYhWl/Go5L80vLhh3rGAh65sv5BvP24XUFAi5x/pcPhu9L/ml4f96RDQT6MAJje19 b2+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773769872; x=1774374672; 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=Jr4mHE3S+HB1/KJbo9Bm7jtZb/OECwOWPM/dFZRIJTk=; b=SMNhBKOU2DxlbiGbDEFcQseZNWykrCwvEAkcmDokcO0EVBgxI59tWZZon4QlChsqXs PdRW/CGjpNWWofajsZLUynWjgbQMmjhP1nd85tsSjNFtxQyfVzeqTS174hxuGVvii+v3 bn1ouOIpo6FNW4RBmmz20kcfhbSux8GDYh2IgeTgcsvElnKIYs5IDPjyS81ctcqW4GUG RopuF2XBrIBRS+vbbdVkXLfICrGI14awusUNGnh4Bq2eOXJAM9e5veUm6eeu2HFEi1Wo rgGatML0z3y72lFbwk8kNZCDJOhKOaQlK56Qz+r0ri+SmqSvGY1hnYI8ehF2Mt5Agma9 doQQ== X-Forwarded-Encrypted: i=1; AJvYcCWyqqdPUF9hWCdY0huReUDZMd3uDxVEY1UbW/Zn4rSD0UYgFJ1RXjbR+JfAxIKkPwD0h2EyRIt8lm2WYqcW@postgresql.org X-Gm-Message-State: AOJu0YwrBMRFpUPwTZcgJcV4X2gFcGXl+P8m3aMDW5eI6//310ZS75H4 P99XdRf/kgBfgNaR71ehWBojiwT6i0ZXYo82kHleHMep0XbDaJNDPrDyrAQnqQQgjMbaOrvRK9x EqTpIGAcbizR+QOhqBs3ztW5NBLs254E= X-Gm-Gg: ATEYQzw3FAql5mDwh296JLoAqKiHFsbSC0WdIvecSB32+G5kpgUidBRK3aFh8AEDty+ K4GZwpH2tXOT4OJ/VK+S96ZeOvI1s4e2so65uCLNeV6MrwH+BDxnvbWH/2g9GIsWIE7rDAuOmtj fK/A7Ajs2+//ewvDRFlk90oN10Z0huHBMHgJtmmZ9kIEuwyeLPO8DJkIaMB0RoZyL2eIij8Ni3e BOFx6xya2xQ83AUe83Ipz1mrjmFWw8WbT1OY95FDlSLQT66psPRlBd5hAWtOGlII/Zum2cF5ku1 FIFoQnWc X-Received: by 2002:ac2:48a8:0:b0:5a1:5762:4d08 with SMTP id 2adb3069b0e04-5a27957dc6fmr144664e87.9.1773769871422; Tue, 17 Mar 2026 10:51:11 -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> <964a3377-d957-4a06-ad02-8228aefa65fc@dunslane.net> In-Reply-To: <964a3377-d957-4a06-ad02-8228aefa65fc@dunslane.net> From: Masahiko Sawada Date: Tue, 17 Mar 2026 10:50:35 -0700 X-Gm-Features: AaiRm52s_ofKKBch5NL6E0GcZy0POBbMvXcPgX2rvykn2uAxvCUD3A6DiP71bds Message-ID: Subject: Re: Emitting JSON to file using COPY TO To: Andrew Dunstan Cc: jian he , 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 Mon, Mar 16, 2026 at 1:59=E2=80=AFPM Andrew Dunstan wrote: > > > On 2026-03-16 Mo 2:24 PM, Masahiko Sawada wrote: > > 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 uses > - * cstate->tupDesc so that the datum is stamped with the right ty= pe; > - * for queries output type is RECORDOID this must be the blessed > - * descriptor so that composite_to_json can look it up via > - * lookup_rowtype_tupdesc. > + * Full table or query without column list. For queries, the slo= t's > + * TupleDesc may carry RECORDOID, which is not registered in the > type > + * cache and would cause composite_to_json's lookup_rowtype_tupde= sc > + * 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) [0xbe= be48] > 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 br= acket. > + */ > + if (cstate->opts.force_array) > + { > + CopySendChar(cstate, '['); > + CopySendTextLikeEndOfRow(cstate); > + } > + } > > We can conjunct the two if statement conditions. > > > Here's a v30 set that I hope fixes these issues. Thank you for updating the patch! The patches look good to me. Regards, --=20 Masahiko Sawada Amazon Web Services: https://aws.amazon.com