If I execute query: SELECT '10' AS a_name;
it gives 10 AS text type.
In case of query: SELECT 10::text AS a_name;
it gives again 10 as text.
So one could expect that following two queries gave the same result:
SELECT '10'::interval day;
SELECT 10::text::interval day;
Nevertheless the first query gives 10 days
and the second gives 00:00:00
. both type of interval.
Please, explain why SELECT '10'
and SELECT 10::text
are interpreted in different way even they both give the same tape and values, or where is my understanding mistaken.
2
Answers
The difference in behavior is because the two expressions,
'10'
and'10'::TEXT
are not semantically equivalent within the database. In the expression'10'::INTERVAL DAY
,'10'
is a symbol of unknown type which is then cast toINTERVAL DAY
. In the expression'10'::TEXT::INTERVAL DAY
,'10'
is again a symbol of unknown type, which is cast toTEXT
before being cast toINTERVAL DAY
. The reason'10'
shows in the client as having typeTEXT
is because of an implicit cast from unknown type toTEXT
when the results are returned to the client.The first example is an assignment.
'10'
is anunknown
-type constant. When you assigninterval
type to it, Postgres assumes it’s'10 seconds'
:The
day
is normally only a field restriction type modifier but in an assignment context, it also changes the assumption about units andexplain
can show you that it’s altering the constant to'10 days'
:Unfortunately, the doc doesn’t describe the second part of that mechanism:
The reason it does that is likely the principle of least astonishment – it’s not unreasonable to expect
10 days
when you typeinterval day '10'
or'10'::interval day
.The second example involves one assignment and one cast. That whole guesswork that applies in the assignment context, still kicks in, but now it sees a
10
that wants to becometext
, so no additional steps are taken:(10::unknown)::text → '10'::text
On the second cast of what’s already a textual
'10'
to aninterval
, theday
restriction does not affect the constant – it’s been pre-processed already and at that stage no further guesswork trickery takes place. As a result it becomes a00:00:10
which is then subjected to theday
restriction, so to only keep whole days, the 10 seconds get truncated away, leaving you with00:00:00
.All constants start off as
::unknown
but only the first type assignment from that is an assignment where this sort of thing can happen, the rest is a cast that works differently. Unfortunately, you can’t go back and re-do the assignment: