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 1x6U0Y-000GHr-1r for pgsql-docs@arkaria.postgresql.org; Tue, 15 Sep 2026 14:19:38 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1x6U0X-002GpG-2E for pgsql-docs@arkaria.postgresql.org; Tue, 15 Sep 2026 14:19:37 +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 1x6U0X-002Gp8-1X for pgsql-docs@lists.postgresql.org; Tue, 15 Sep 2026 14:19:37 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x6U0V-00000000CUj-2RCY for pgsql-docs@lists.postgresql.org; Tue, 15 Sep 2026 14:19:36 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.18.1/8.18.1) with ESMTP id 68FEJUNU274669; Tue, 15 Sep 2026 10:19:30 -0400 From: Tom Lane To: "David G. Johnston" cc: "kengruven@gmail.com" , "pgsql-docs@lists.postgresql.org" Subject: Re: Anonymous record member access In-reply-to: References: <178941114936.1263.15482664434063598995@wrigleys.postgresql.org> Comments: In-reply-to "David G. Johnston" message dated "Tue, 15 Sep 2026 07:29:28 -0500" MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-ID: <274667.1789481970.1@sss.pgh.pa.us> Content-Transfer-Encoding: 8bit Date: Tue, 15 Sep 2026 10:19:30 -0400 Message-ID: <274668.1789481970@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk "David G. Johnston" writes: > On Monday, September 14, 2026, PG Doc comments form > wrote: >> To summarize, things I don't understand from the docs: >> - where exactly the .f1 notation is documented > It probably isn’t documented behavior and thus should not be relied upon. Yeah, it's not documented in any user-facing place, AFAICS. That's because it's not a feature so much as a collection of legacy behaviors. >> - where/why it's not allowed to be used >> - how/why a ROW changes behavior when returned from a FUNCTION > I suspect these are just boundary issues that would be considered bugs if > this entire thing were considered documented behavior. The key thing to understand is static versus dynamic typing. If foo() is declared to return a named composite type, then when you write SELECT (foo(...)).x the parser can look up the composite type and identify that yes, x is a column of that type, it has position n and data type so-and-so, and then it knows the result type of that expression and can continue parsing. But if foo() is declared to return record then no such information is available, and the parser must throw up its hands. The function might return one thing today and something entirely different tomorrow. ROW() constructors do need to assign column names in the anonymous record type they construct, and what they use is indeed f1,f2,etc. But for most purposes in SQL you can't see that because SQL is mostly a statically-typed language, so it can't do much with an anonymous record value other than pass it around. There are functions like to_json() that are declared to accept type record, which means that they can work on any composite type whatever. They use below-SQL-level implementation details to find out what are the column names and datatypes inside whatever they're handed. You can do some of this stuff in plpgsql or other PLs, which are less resolute about being statically typed than the main SQL grammar. There are also behaviors that are just plain warts, such as your example SELECT (ROW(3,4,5)).f1; -- returns 3 On what I've been telling you, that ought to fail. It does work, because the parser logic that looks up the composite type of the left-hand side of a field accessor dot has a special case for when that left-hand side is exactly a RowExpr. (I think yours truly might be responsible for that, but it's still a wart.) So it's all pretty messy and no one has cared to try to make it coherent enough to be document-able. I join with David in recommending that you avoid relying on this. If we ever did try to make it coherent, we'd likely elect to break some behaviors that happen to work today. regards, tom lane