Продолжая тему новый возможностей грядущего релиза PostgreSQL 11 (предыдущие посты: раз, два, три), я хотел бы рассказать об одном небольшом, но важном изменении. Изменение это касается кастинга одних типов в другие. И, конечно же, оно касается JSONB, потому что в PostgresPro мы его очень любим!
Речь идет о патче c0cbe00f:
commit c0cbe00fee6d0a5e0ec72c6d68a035e674edc4cc
Author: Teodor Sigaev <teodor@sigaev.ru>
Date: Thu Mar 29 16:33:56 2018 +0300
Add casts from jsonb
Add explicit cast from scalar jsonb to all numeric and bool types. It would be
better to have cast from scalar jsonb to text too but there is already a cast
from jsonb to text as just text representation of json. There is no way to have
two different casts for the same type's pair.
Bump catalog version
Author: Anastasia Lubennikova with editorization by Nikita Glukhov and me
Review by: Aleksander Alekseev, Nikita Glukhov, Darafei Praliaskouski
Discussion: https://www.postgresql.org/message-id/flat/0154d35a-24ae-f063-5273-9ffcdf1c7f2e@postgrespro.ru
Суть проблемы заключается вот в чем. Если вы возьмете PostgreSQL 10, то обнаружите следующее поведение:
=# select 'true' :: jsonb :: bool;
ERROR: cannot cast type jsonb to boolean
LINE 1: select 'true' :: jsonb :: bool;
=# select '12345' :: jsonb :: int4;
ERROR: cannot cast type jsonb to integer
LINE 1: select '12345' :: jsonb :: int4;
=# select '12345.67' :: jsonb :: float8;
ERROR: cannot cast type jsonb to double precision
LINE 1: select '12345.67' :: jsonb :: float8;
То есть, JSONB не приводится к числовым типам и boolean. Конечно, не то чтобы это было прямо очень большой проблемой. В конце концов, всегда можно кастонуть через text:
=# select '12345.67' :: jsonb :: text :: float8;
float8
----------
12345.67
Тем не менее, такое решение выглядит как-то костыльно, да и по поводу его производительности есть вопросы.
Теперь взглянем на поведение ветки master:
=# select 'true' :: jsonb :: bool;
bool
------
t
=# select '12345' :: jsonb :: int4;
int4
-------
12345
=# select '12345.67' :: jsonb :: float8;
float8
----------
12345.67
Как видите, теперь можно кастовать JSONB в boolean и числовые типы. Ура!
Характерно, что кастинг в обратную сторону пока что возможен только через промежуточный кастинг в text:
=# select 12345.67 :: jsonb;
ERROR: cannot cast type numeric to jsonb
LINE 1: select 12345.67 :: jsonb;
=# select 12345.67 :: text :: jsonb;
jsonb
----------
12345.67
(1 row)
Вы спросите, а почему нет прямого кастинга? Думаю, просто еще ни у кого не дошли руки его дописать. Звучит, как возможность для самореализации, не находите? ;)
Комментарии (5)
aleksandy
11.05.2018 06:28По-моему, конструкции вида
'true' :: jsonb
'12345.67' :: jsonb
априори должны кидать какой-нибудь WrongFormatException. Ну, не является 'true' валидным json-объектом.
Для подобного должен существовать отдельный тип, например json_element.
cooladmin
Это точно ожидаемое поведение? Кажется должно возвращаться именно значение в bool (возможно ошибка в тексте).
afiskon Автор
Если вы по поводу символа 't' то это стандартный способ обозначения true в PostgreSQL.