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 1w4llI-002XQO-0V for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Mar 2026 20:20:32 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w4llG-002IJ7-1v for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Mar 2026 20:20:31 +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 1w4llG-002IIy-0u for pgsql-hackers@lists.postgresql.org; Mon, 23 Mar 2026 20:20:30 +0000 Received: from mail-dl1-x1236.google.com ([2607:f8b0:4864:20::1236]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w4llE-00000000kyI-0oks for pgsql-hackers@postgresql.org; Mon, 23 Mar 2026 20:20:30 +0000 Received: by mail-dl1-x1236.google.com with SMTP id a92af1059eb24-1273349c56bso4379134c88.0 for ; Mon, 23 Mar 2026 13:20:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1774297227; cv=none; d=google.com; s=arc-20240605; b=Dw1wMThZeXxVsYR+v2ERzQ7gcUlq7dD51TXx5YluGHS802keUlLoXCve9+5rU9wI0R 2hvvcZruOPgjHH2haoTKOY+iJuO2pjJ8q0VvaHmGn+uQuImXvZrnxj2Rmy6Yq+eKOWK6 +GWtCl3Q8Vs7sTO36rEuCyoibwCSOWiwOpZFUMGVGOGF5gh+XTMEfy7BLtIA9LDU3QWQ C1i0lm8BUFDrbUN3ZPKPo0oXBuHW0uKxddQ+vrNkyugfqsFRzdgon66646bU6mGBP+8y sczIht3gcfa+1TPZybG6jlbWQ6IvSeq/ceWoC2zcseQ85vJBQh/kWk2yCQVvMJekFkDE MEjA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=6amtB0SCvC21x5jaCFA8hLAb+yVtPv7fX9j0XlQ1Yjw=; fh=40MW+I2VwbJlAaFyY/V87HUB+kz1FWoxVSJwd3HXHtA=; b=caCPXg+bQzERyIKCtP+eMZLQqN1Usfj1fRfoJeS37jtZe+2scLjZDG1SOxROeoDvbk YZmQBIbY5V/QyLv8B37zF57v0Wes97o8P045R9JjtpIXylk4IacpcqhHzhIEHntTOiuP UMwD9qmIrhCKTBluvLaoI/8E9cMcVbjPRWAJZQ5mlrKjd7SiCES3JzASJLeVHLD9Z1NM emtrrJunERMToIM7YrH00RyaWEOrhplEHl1IXelOpB+YCKEPu4/ymP7+kOqbBVYVjPPY pdqUa+O+SS6G6DrJbVV9gb59Rfdnra3DV7T5JB+YdwdH9ifINwQquORKb1CPgD7/99Al /dvg==; 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=20251104; t=1774297227; x=1774902027; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=6amtB0SCvC21x5jaCFA8hLAb+yVtPv7fX9j0XlQ1Yjw=; b=IrB+x+AAJe07vZsU/Tk8mwrNEsflbaAfOvCqmHNKlG4ZqWAw//i8qSvw40uuw8LYDi BcQEqd9vJTfbIx1ZwqSINKT72FZ0Zf1H1zv5fP1YG3l8F8nVvoiMWsVUxgsIDMhxyEAJ j+8TU0wpirjLM0bNQ+N93V52XYIuaq6hvpqh5gBMlR0JBnR9e9jQ8gccf4Ns1zKDIzlW mqvq9QzyCuGZHCYPhxHHl6qwzoZMGFRxkDxvo6MdlWpehyAwD1XkrVOUdwOxKagBetJl qQbc+q4hCarQFLkdntEVRW1bPz2wVETX61G+6gBgirKowmRIb4PcYY4XIgkRzRa0sB5x yNKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774297227; x=1774902027; h=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=6amtB0SCvC21x5jaCFA8hLAb+yVtPv7fX9j0XlQ1Yjw=; b=NTLadQ98LC7wMuRijrBvmWJPwzFCSmgImfh+zQ64pA+6oO5M0BW8bd9OqDbFLqNmyt BEJ++ANZRZg23dVdmtK6QoNE3dMXoHCbjzsGiDixd7DwQyDCPyg3GMjTZ6VytTzLrGRB 1Nd9FoO/fSVye8yc/qtyHg8X8p1n0bjua59CwqQUs61xxp22W/oz05sSjEAeYKztN0Ar 2pd2KPHy/tZStyHFn/Pi8doMIojo0WtXvd/Rxy52QlFDZFwQ4a1OtcfTmn8ZOaoMgOJ1 921xQZvFZBUb72DjjZKkMUD1flFsIDyaD7mFpd8tlIMQZPstCVlWIS5QlRQfNIVFE8ht XGSQ== X-Forwarded-Encrypted: i=1; AJvYcCXh6tKLHGNFOpuckn1bc8drxDOac0uWHLGqhJs79r4O6ktK3PUZZchpemtReGOcUQ84ufgrmW1KL80Wt02n@postgresql.org X-Gm-Message-State: AOJu0YxOakJKhKcFurnqyzdWJJRDQWFH6yFrvkdDJge68f8FIK/Ouk43 e3/x7qM6Cz3DgQeu2vNxbA7Bs1WD0OBBuKJmVzmgLqEpFUqwTlRGsDZXwy7HvOUbK1vW5G65QzI n1d9xGtV8aQz4wmy8Rg06wuNuvh0aheo= X-Gm-Gg: ATEYQzzmu2WmULs1GHXvoqs3Zj1yX5TZ5clhD5azFjlHhoXohdA7VZHmLxljV9/lmDa QvMHZ7XKw1sDwTDci0SEZH0OzL0IlXqytbFq6i6XTulmwqFdWyLPYiyM0gnk/gxIZqKOpXy4s+I Qm4hsoV5rreY9+T0pWNOlzmukTGOOECh+9r6yPWSPTAOSYrxbhvrNSNsFqrd4bB3YI88rfTlsWP +nsQt0xT1zVXIPFqScav5v3BP8P3mJiIKKFwZ6FGrFUr8m0oNFtjJCIZw7+AHuR4dbGZUeN+QB1 4gvay9tJCHYPRoROtVrDFuqkSRY36YoquPPt+sUxkg7kzj2s3K4/mmFnd26SSDIjaq49hpI4AE1 MYJUTEslVYa6Eiy+3M5tgVgD4 X-Received: by 2002:a05:7022:2224:b0:128:ceac:6db1 with SMTP id a92af1059eb24-12a726caecemr6459055c88.28.1774297226757; Mon, 23 Mar 2026 13:20:26 -0700 (PDT) MIME-Version: 1.0 References: <4c358bf8-87e8-4760-83d5-a6f9fcd88907@postgresfriends.org> <1b702de4-86b0-4144-a2d0-38266296894f@postgresfriends.org> <82b4223e-84d4-432d-8167-dd698ce489f5@postgresfriends.org> In-Reply-To: From: Corey Huinker Date: Mon, 23 Mar 2026 16:20:14 -0400 X-Gm-Features: AaiRm53lwSYpTLRl1hUfTQvIkMK9YCO9gldFNwuqVcmwOeZ4lHZCdWJkuaEdrnc Message-ID: Subject: Re: implement CAST(expr AS type FORMAT 'template') To: zengman , suryapoondla4@gmail.com Cc: jian he , "David G. Johnston" , pgsql-hackers , Vik Fearing Content-Type: multipart/alternative; boundary="000000000000ed4b2b064db6c3a7" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000ed4b2b064db6c3a7 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > > > Typos > - parse_expr.c:2745: "formmatted" =E2=86=92 "formatted" > - parse_coerce.c:156: "Cocerce" =E2=86=92 "Coerce" > > In `coerce_type_with_format()` (parse_coerce.c): > > 1. The check `if (fmtcategory !=3D TYPCATEGORY_STRING && fmtcategory !=3D > TYPCATEGORY_UNKNOWN)` > could be moved earlier. It doesn't depend on any other > calculations, so failing fast here would avoid unnecessary work. > > 2. consider using` list_make2(node, format)` instead of `list_make1() + > lappend()`. > > -- > Regards, > Man Zeng Surya and I did a pair-review of this. In addition to the notes above (which we agree with), we have the following notes: + if (inputBaseTypeId =3D=3D targetBaseTypeId) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot cast type %s to %s while using a format template", + format_type_be(inputBaseTypeId), + format_type_be(targetBaseTypeId)), + errdetail("binary coercible type cast is not supported while using a format template"), + parser_coercion_errposition(pstate, location, node)); This could use a bit more explanation in a comment - is it because there is no plausible type that can take a FORMAT and be cast to itself? + if (s_typcategory !=3D TYPCATEGORY_NUMERIC && + s_typcategory !=3D TYPCATEGORY_STRING && + s_typcategory !=3D TYPCATEGORY_DATETIME && + s_typcategory !=3D TYPCATEGORY_TIMESPAN) In situations like this, the committers have shown a strong preference for switch() statements. Though it may make more sense to package this if into a static function is_formattable_type() or similar. + if (t_typcategory =3D=3D TYPCATEGORY_STRING) + funcname =3D list_make2(makeString("pg_catalog"), + makeString("to_char")); ... + else + elog(ERROR, "failed to find conversion function from %s to %s while using a format template", + format_type_be(inputTypeId), format_type_be(targetTypeId)); Similar thing here, committers will probably want to see a switch. +create function imm_const() returns text as $$ begin return 'YYYY-MM-DD'; end; $$ language plpgsql immutable;; +select cast(col1 as date format imm_const()) from tcast; double-; Overall, the test coverage appears complete and the close mirroring of existing tests makes the intention very clear. --000000000000ed4b2b064db6c3a7 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable

Typos
- parse_expr.c:2745: "formmatted" =E2=86=92 "formatted"=
- parse_coerce.c:156: "Cocerce" =E2=86=92 "Coerce"

In `coerce_type_with_format()` (parse_coerce.c):

1. The check `if (fmtcategory !=3D TYPCATEGORY_STRING && fmtcategor= y !=3D TYPCATEGORY_UNKNOWN)`
could be moved earlier. It doesn't depend on any other
calculations, so failing fast here would avoid unnecessary work.

2. consider using` list_make2(node, format)` instead of `list_make1() + lap= pend()`.

--
Regards,
Man Zeng

Surya and I did a pair-review of t= his. In addition to the notes above (which we agree with), we have the foll= owing notes:

+ =C2=A0 =C2=A0 =C2=A0 if (inputBaseTypeId =3D=3D targe= tBaseTypeId)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ereport(= ERROR,
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 errcode(ERRCODE_FEATURE_NOT_SUPPO= RTED),
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 errmsg("cannot cast type %s = to %s while using a format template",
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0format_type_be(inputBaseTypeId= ),
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0format_type_be(targetBaseTypeId)),
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 errdetail("binary coercible type cast is not supported while using= a format template"),
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser_coerc= ion_errposition(pstate, location, node));

This could use a bit more = explanation in a comment - is it because there is no plausible type that ca= n take a FORMAT and be cast to itself?

+ =C2=A0 = =C2=A0 =C2=A0 if (s_typcategory !=3D TYPCATEGORY_NUMERIC &&
+ = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s_typcategory !=3D TYPCATE= GORY_STRING &&
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 s_typcategory !=3D TYPCATEGORY_DATETIME &&
+ =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s_typcategory !=3D TYPCATEGORY_TIMESPAN)=

In situations like this, the committers have shown a strong prefere= nce for switch() statements. Though it may make more sense to package this = if into a static function is_formattable_type() or similar.

<= /div>
+ =C2=A0 =C2=A0 =C2=A0 if (t_typcategory =3D=3D TYPCATEGORY_STRIN= G)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 funcname =3D list_= make2(makeString("pg_catalog"),
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 makeString("to_char"));
...
+ =C2=A0 = =C2=A0 =C2=A0 else
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 el= og(ERROR, "failed to find conversion function from %s to %s while usin= g a format template",
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0format_type_be(inputTypeId), forma= t_type_be(targetTypeId));

Similar thing here, committers will probab= ly want to see a switch.

+create function imm_cons= t() returns text as $$ begin return 'YYYY-MM-DD'; end; $$ language = plpgsql immutable;;
+select cast(col1 as date format imm_const()) from t= cast;

double-;

Overall, the test coverage appears complete an= d the close mirroring of existing tests makes the intention very clear.
--000000000000ed4b2b064db6c3a7--