Skip to content

Commit f230614

Browse files
committed
Don't elide casting to typmod -1.
Casting a value that's already of a type with a specific typmod to an unspecified typmod doesn't do anything so far as run-time behavior is concerned. However, it really ought to change the exposed type of the expression to match. Up to now, coerce_type_typmod hasn't bothered with that, which creates gotchas in contexts such as recursive unions. If for example one side of the union is numeric(18,3), but it needs to be plain numeric to match the other side, there's no direct way to express that. This is easy enough to fix, by inserting a RelabelType to update the exposed type of the expression. However, it's a bit nervous-making to change this behavior, because it's stood for a really long time. But no complaints have emerged about 14beta3, so go ahead and back-patch. Back-patch of 5c056b0 into previous supported branches. Discussion: https://postgr.es/m/CABNQVagu3bZGqiTjb31a8D5Od3fUMs7Oh3gmZMQZVHZ=uWWWfQ@mail.gmail.com Discussion: https://postgr.es/m/1488389.1631984807@sss.pgh.pa.us
1 parent c9d07ee commit f230614

File tree

3 files changed

+74
-9
lines changed

3 files changed

+74
-9
lines changed

src/backend/parser/parse_coerce.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -752,25 +752,33 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
752752
CoercionPathType pathtype;
753753
Oid funcId;
754754

755-
/*
756-
* A negative typmod is assumed to mean that no coercion is wanted. Also,
757-
* skip coercion if already done.
758-
*/
759-
if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
755+
/* Skip coercion if already done */
756+
if (targetTypMod == exprTypmod(node))
760757
return node;
761758

759+
/* Suppress display of nested coercion steps */
760+
if (hideInputCoercion)
761+
hide_coercion_node(node);
762+
762763
pathtype = find_typmod_coercion_function(targetTypeId, &funcId);
763764

764765
if (pathtype != COERCION_PATH_NONE)
765766
{
766-
/* Suppress display of nested coercion steps */
767-
if (hideInputCoercion)
768-
hide_coercion_node(node);
769-
770767
node = build_coercion_expression(node, pathtype, funcId,
771768
targetTypeId, targetTypMod,
772769
ccontext, cformat, location);
773770
}
771+
else
772+
{
773+
/*
774+
* We don't need to perform any actual coercion step, but we should
775+
* apply a RelabelType to ensure that the expression exposes the
776+
* intended typmod.
777+
*/
778+
node = applyRelabelType(node, targetTypeId, targetTypMod,
779+
exprCollation(node),
780+
cformat, location, false);
781+
}
774782

775783
return node;
776784
}

src/test/regress/expected/expressions.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,40 @@ SELECT current_schema;
7575
(1 row)
7676

7777
RESET search_path;
78+
--
79+
-- Test parsing of a no-op cast to a type with unspecified typmod
80+
--
81+
begin;
82+
create table numeric_tbl (f1 numeric(18,3), f2 numeric);
83+
create view numeric_view as
84+
select
85+
f1, f1::numeric(16,4) as f1164, f1::numeric as f1n,
86+
f2, f2::numeric(16,4) as f2164, f2::numeric as f2n
87+
from numeric_tbl;
88+
\d+ numeric_view
89+
View "public.numeric_view"
90+
Column | Type | Collation | Nullable | Default | Storage | Description
91+
--------+---------------+-----------+----------+---------+---------+-------------
92+
f1 | numeric(18,3) | | | | main |
93+
f1164 | numeric(16,4) | | | | main |
94+
f1n | numeric | | | | main |
95+
f2 | numeric | | | | main |
96+
f2164 | numeric(16,4) | | | | main |
97+
f2n | numeric | | | | main |
98+
View definition:
99+
SELECT numeric_tbl.f1,
100+
numeric_tbl.f1::numeric(16,4) AS f1164,
101+
numeric_tbl.f1::numeric AS f1n,
102+
numeric_tbl.f2,
103+
numeric_tbl.f2::numeric(16,4) AS f2164,
104+
numeric_tbl.f2 AS f2n
105+
FROM numeric_tbl;
106+
107+
explain (verbose, costs off) select * from numeric_view;
108+
QUERY PLAN
109+
-------------------------------------------------------------------------------------------------------------------------------------------------------
110+
Seq Scan on public.numeric_tbl
111+
Output: numeric_tbl.f1, (numeric_tbl.f1)::numeric(16,4), (numeric_tbl.f1)::numeric, numeric_tbl.f2, (numeric_tbl.f2)::numeric(16,4), numeric_tbl.f2
112+
(2 rows)
113+
114+
rollback;

src/test/regress/sql/expressions.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,23 @@ SELECT current_schema;
3434
SET search_path = 'pg_catalog';
3535
SELECT current_schema;
3636
RESET search_path;
37+
38+
39+
--
40+
-- Test parsing of a no-op cast to a type with unspecified typmod
41+
--
42+
begin;
43+
44+
create table numeric_tbl (f1 numeric(18,3), f2 numeric);
45+
46+
create view numeric_view as
47+
select
48+
f1, f1::numeric(16,4) as f1164, f1::numeric as f1n,
49+
f2, f2::numeric(16,4) as f2164, f2::numeric as f2n
50+
from numeric_tbl;
51+
52+
\d+ numeric_view
53+
54+
explain (verbose, costs off) select * from numeric_view;
55+
56+
rollback;

0 commit comments

Comments
 (0)