Skip to content

Commit dede143

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 89b5676 commit dede143

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
@@ -755,25 +755,33 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
755755
CoercionPathType pathtype;
756756
Oid funcId;
757757

758-
/*
759-
* A negative typmod is assumed to mean that no coercion is wanted. Also,
760-
* skip coercion if already done.
761-
*/
762-
if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
758+
/* Skip coercion if already done */
759+
if (targetTypMod == exprTypmod(node))
763760
return node;
764761

762+
/* Suppress display of nested coercion steps */
763+
if (hideInputCoercion)
764+
hide_coercion_node(node);
765+
765766
pathtype = find_typmod_coercion_function(targetTypeId, &funcId);
766767

767768
if (pathtype != COERCION_PATH_NONE)
768769
{
769-
/* Suppress display of nested coercion steps */
770-
if (hideInputCoercion)
771-
hide_coercion_node(node);
772-
773770
node = build_coercion_expression(node, pathtype, funcId,
774771
targetTypeId, targetTypMod,
775772
ccontext, cformat, location);
776773
}
774+
else
775+
{
776+
/*
777+
* We don't need to perform any actual coercion step, but we should
778+
* apply a RelabelType to ensure that the expression exposes the
779+
* intended typmod.
780+
*/
781+
node = applyRelabelType(node, targetTypeId, targetTypMod,
782+
exprCollation(node),
783+
cformat, location, false);
784+
}
777785

778786
return node;
779787
}

src/test/regress/expected/expressions.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,40 @@ select count(*) from date_tbl
158158
12
159159
(1 row)
160160

161+
--
162+
-- Test parsing of a no-op cast to a type with unspecified typmod
163+
--
164+
begin;
165+
create table numeric_tbl (f1 numeric(18,3), f2 numeric);
166+
create view numeric_view as
167+
select
168+
f1, f1::numeric(16,4) as f1164, f1::numeric as f1n,
169+
f2, f2::numeric(16,4) as f2164, f2::numeric as f2n
170+
from numeric_tbl;
171+
\d+ numeric_view
172+
View "public.numeric_view"
173+
Column | Type | Collation | Nullable | Default | Storage | Description
174+
--------+---------------+-----------+----------+---------+---------+-------------
175+
f1 | numeric(18,3) | | | | main |
176+
f1164 | numeric(16,4) | | | | main |
177+
f1n | numeric | | | | main |
178+
f2 | numeric | | | | main |
179+
f2164 | numeric(16,4) | | | | main |
180+
f2n | numeric | | | | main |
181+
View definition:
182+
SELECT numeric_tbl.f1,
183+
numeric_tbl.f1::numeric(16,4) AS f1164,
184+
numeric_tbl.f1::numeric AS f1n,
185+
numeric_tbl.f2,
186+
numeric_tbl.f2::numeric(16,4) AS f2164,
187+
numeric_tbl.f2 AS f2n
188+
FROM numeric_tbl;
189+
190+
explain (verbose, costs off) select * from numeric_view;
191+
QUERY PLAN
192+
-------------------------------------------------------------------------------------------------------------------------------------------------------
193+
Seq Scan on public.numeric_tbl
194+
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
195+
(2 rows)
196+
197+
rollback;

src/test/regress/sql/expressions.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,23 @@ select count(*) from date_tbl
6565
where f1 not between symmetric '1997-01-01' and '1998-01-01';
6666
select count(*) from date_tbl
6767
where f1 not between symmetric '1997-01-01' and '1998-01-01';
68+
69+
70+
--
71+
-- Test parsing of a no-op cast to a type with unspecified typmod
72+
--
73+
begin;
74+
75+
create table numeric_tbl (f1 numeric(18,3), f2 numeric);
76+
77+
create view numeric_view as
78+
select
79+
f1, f1::numeric(16,4) as f1164, f1::numeric as f1n,
80+
f2, f2::numeric(16,4) as f2164, f2::numeric as f2n
81+
from numeric_tbl;
82+
83+
\d+ numeric_view
84+
85+
explain (verbose, costs off) select * from numeric_view;
86+
87+
rollback;

0 commit comments

Comments
 (0)