Что нового в PostgreSQL 11: улучшения в кастинге



    Продолжая тему новый возможностей грядущего релиза 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)


    Вы спросите, а почему нет прямого кастинга? Думаю, просто еще ни у кого не дошли руки его дописать. Звучит, как возможность для самореализации, не находите? ;)
    • +13
    • 5,4k
    • 7
    Postgres Professional 274,20
    Российский вендор PostgreSQL
    Поделиться публикацией
    Похожие публикации
    Комментарии 7
    • +1
      =# select 'true' :: jsonb :: bool;
      bool
      — t

      Это точно ожидаемое поведение? Кажется должно возвращаться именно значение в bool (возможно ошибка в тексте).
      • +4
        Если вы по поводу символа 't' то это стандартный способ обозначения true в PostgreSQL.
      • +3
        По-моему, конструкции вида
        'true' :: jsonb
        '12345.67' :: jsonb

        априори должны кидать какой-нибудь WrongFormatException. Ну, не является 'true' валидным json-объектом.

        Для подобного должен существовать отдельный тип, например json_element.
        • 0
          json-объектом — не является. Но JSON (в ECMA-404 в частности) не обязывает на верхнем уровне держать именно объект — там должен быть Value, а он уж может быть объектом, а может быть и числом.

          www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
        • +3

          С чего это не является валидным? Вполне себе такой же валидный, как и JSON от массива или объекта.

          • –1
            true — это json-значение, а не объект.
          • +1

            Для обратного каста есть же to_jsonb(some_value).

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое