Правила преобразования типов через оператор CAST

Правила преобразования примитивных типов данных

  • В процессе преобразования примитивных типов данных часть исходной информации может быть отброшена, если она не содержится в целевом типе. Например:

    • Дробная часть Float/Double при преобразовании в целочисленные типы;
    • Время Datetime/Timestamp при преобразовании в Date.
    • Таймзона при преобразовании из типов с таймзоной к типу даты/времени без таймзоны.
  • Если для определённого сочетания исходного и целевого типа преобразование не может быть выполнено для всех возможных значений исходного типа, то при неудачном преобразовании CAST вернёт NULL. В таких случаях к типу возвращаемого значения добавляется один уровень Optional, если его не было. Например, конструкции: CAST("3.14" AS Float?) и CAST("3.14" AS Float) полностью эквиваленты и возвращают Float?.

  • Если же преобразование возможно для всех значений исходного типа, то добавление '?' работает как Just сверху: CAST(3.14 AS Utf8?) то же, что и Just(CAST(3.14 AS Utf8))

Все сочетания примитивных типов, для которых возможен CAST описаны тут.

Правила преобразований для контейнеров

Правила для Optional

  • Если для целевого типа задан больший уровень Optional чем для исходного то это эквивалентно добавлению Just поверх CAST с меньшим уровнем Optional.
  • Если же больший уровень Optional у исходного типа, то NULL на любом уровне больше целевого приводит к NULL в результате.
  • При равных уровнях Optional значение NULL остаётся на том же уровне.
SELECT
    CAST(1 AS Int32?),                  -- тоже что и Just(1)
    CAST(Just(2/1) AS Float??),         -- [2]
    CAST(Just(3/0) AS Float??) IS NULL; -- false: результат Just(NULL)

Правила для List/Dict

  • Список формируется путём выполнения CAST для каждого элемента исходного списка в тип элемента целевого типа.
  • Если тип элемента целевого типа не опциональный, а CAST элемента может быть неуспешным, то такие преобразования отбрасываются. В этом случае список в результате может быть меньшей длины или вовсе пустой, если успешных преобразований не было.
  • Для словарей преобразование выполняется полностью аналогично спискам, выполняя CAST для ключей и значений.
SELECT
    CAST([-1, 0, 1] AS List<Uint8?>),             -- [null, 0, 1]
    CAST(["3.14", "bad", "42"] AS List<Float>),   -- [3.14, 42]

    CAST({-1:3.14, 7:1.6} AS Dict<Uint8, Utf8>),  -- {7: "1.6"}
    CAST({-1:3.14, 7:1.6} AS Dict<Uint8?, Utf8>); -- {7: "1.6", null:"3.14"}

Правила для Struct/Tuple

  • Структура или кортеж формируется путём выполнения CAST для каждого элемента исходного типа в элемент с тем же именем или индексом целевого типа.
  • Если какое-то поле отсутствует в целевом типе, оно просто отбрасывается.
  • Если какое-то поле отсутствует в типе исходного значения, то оно может быть добавлено только если является опциональным, и получает значение NULL.
  • Если какое-то поле не является опциональным в целевом типе, но его преобразование может быть неуспешным, то CAST добавляет опциональность на уровень структуры или кортежа и может вернуть NULL для всего результата.
SELECT
    CAST((-1, 0, 1) AS Tuple<Uint16?, Uint16?, Utf8>), -- (null, 0, "1")
    CAST((-2, 0) AS Tuple<Uint16, Utf8>),              -- null
    CAST((3, 4) AS Tuple<Uint16, String>),             -- (3, "4") тип Tuple<Uint16, String>?
    CAST(("4",) AS Tuple<Uint16, String?>),            -- (4, null)
    CAST((5, 6, null) AS Tuple<Uint8?>);               -- (5,)  элементы удалены.

SELECT -- Одно поле удалено и одно добавлено: ("three":null, "two": "42")
    CAST(<|one:"8912", two:42|> AS Struct<two:Utf8, three:Date?>);

Правила для Variant

  • Для варианта с определёнными именем или индексом выполняется преобразование в вариант с тем же именем или индексом.
  • Если преобразование для варианта может быть неуспешным и тип этого варианта не опциональный, то CAST добавляет опциональность на верхний уровень и может вернуть NULL.
  • Если какой-то вариант отсутствует в целевом типе, то CAST добавляет опциональность на верхний уровень и для такого значения возвращает NULL.

Вложенные контейнеры

  • Все вышеперечисленные правила работают рекурсивно для вложенных контейнеров.