CREATE CAST

Определяет новый преобразователь (cast).

Обзор

CREATE CAST (<sourcetype> AS <targettype>) 
       WITH FUNCTION <funcname> (<argtype> [, ...]) 
       [AS ASSIGNMENT | AS IMPLICIT]

CREATE CAST (<sourcetype> AS <targettype>)
       WITHOUT FUNCTION 
       [AS ASSIGNMENT | AS IMPLICIT]

CREATE CAST (<sourcetype> AS <targettype>)
       WITH INOUT 
       [AS ASSIGNMENT | AS IMPLICIT]

Описание

Команда CREATE CAST определяет новый преобразователь. Преобразователь указывает, как выполнить конвертацию между двумя типами данных. Например:

SELECT CAST(42 AS float8);

преобразует целочисленную константу 42 в тип float8, вызывая предварительно определённую функцию — в данном случае float8(int4). Если подходящий преобразователь не определён, конвертация завершится ошибкой.

Два типа могут быть бинарно совместимыми (binary coercible), что означает, что значения одного типа можно преобразовать в другой без вызова какой-либо функции. Это возможно только в том случае, если соответствующие значения имеют одинаковое внутреннее представление. Например, типы text и varchar являются бинарно совместимыми в обоих направлениях. Однако бинарная совместимость не обязательно является симметричным отношением. Например, преобразование из xml в text в текущей реализации выполняется бесплатно, тогда как обратное преобразование требует функции, которая как минимум проверяет синтаксис. (Два типа, которые бинарно совместимы в обоих направлениях, также называются бинарно совместимыми.)

Вы можете определить преобразователь как преобразователь на основе ввода/вывода (I/O conversion cast), используя синтаксис WITH INOUT. Такой преобразователь работает путём вызова функции вывода исходного типа данных и передачи полученной строки функции ввода целевого типа. Во многих распространённых случаях эта возможность позволяет избежать написания отдельной функции преобразования. Преобразователь на основе ввода/вывода действует так же, как обычный функциональный преобразователь; отличается только реализация.

По умолчанию преобразователь может быть вызван только по явному запросу, то есть с использованием конструкции CAST(x AS typename) или x::typename.

Если преобразователь помечен как AS ASSIGNMENT, его можно вызывать неявно при присваивании значения столбцу целевого типа данных. Например, если foo.f1 — это столбец типа text, то следующее выражение:

INSERT INTO foo (f1) VALUES (42);

будет разрешено, если преобразователь из типа integer в тип text помечен как AS ASSIGNMENT, иначе — нет. Такой вид преобразователей обычно называют «присваивающим преобразователем» (assignment cast).

Если преобразователь помечен как AS IMPLICIT, он может вызываться неявно в любом контексте — как при присваивании, так и внутри выражений. Такой вид преобразователей обычно называют «неявным преобразователем» (implicit cast). Например, рассмотрим следующий запрос:

SELECT 2 + 4.0;

Сначала парсер помечает константы как имеющие типы integer и numeric соответственно. В системных каталогах нет оператора integer + numeric, но существует оператор numeric + numeric. Запрос будет выполнен успешно, если существует преобразователь из типа integer в numeric (он действительно существует) и он помечен как AS IMPLICIT (что и имеет место). Парсер применяет только неявное преобразование и интерпретирует запрос так, будто он был записан следующим образом:

SELECT CAST ( 2 AS numeric ) + 4.0;

В каталогах также определён преобразователь из numeric в integer. Если бы этот преобразователь был помечен как AS IMPLICIT (что не так), парсер столкнулся бы с выбором между двумя вариантами: либо применить вышеуказанное преобразование, либо преобразовать числовую константу в integer и использовать оператор integer + integer. Не имея информации о предпочтительном варианте, парсер отказался бы от обработки и объявил бы запрос неоднозначным. Именно благодаря тому, что только одно из двух преобразований является неявным, мы «обучаем» парсер предпочитать интерпретацию смешанного выражения с numeric и integer именно как numeric; у парсера нет встроенных знаний об этом.

Следует быть осторожным при пометке преобразователей как неявных. Избыток неявных путей преобразования может заставить базу данных выбирать неожиданные трактовки команд или вообще не справляться с их разбором из-за множественности возможных интерпретаций. Хорошим правилом является помечать преобразователи как неявные только для преобразований, сохраняющих информацию, между типами одной общей категории. Например, преобразование из int2 в int4 может быть неявным, а вот преобразование из float8 в int4 должно быть доступно только при присваивании. Преобразователи между разными категориями типов, такие как text в int4, лучше делать доступными только по явному запросу.

Иногда по соображениям удобства использования или соответствия стандартам необходимо предоставить несколько неявных преобразователей между набором типов, что приводит к неизбежной неоднозначности. В таких случаях парсер использует резервную эвристику, основанную на категориях типов и предпочтительных типах, чтобы обеспечить желаемое поведение. Подробнее см. в команде CREATE TYPE.

Чтобы создать преобразователь, вы должны владеть исходным или целевым типом данных и иметь привилегию USAGE на другой тип. Чтобы создать бинарно совместимый преобразователь, вы должны быть суперпользователем. (Это ограничение введено потому, что неправильный бинарно совместимый преобразователь может легко привести к сбою сервера.)

Параметры

  • sourcetype

    • Имя исходного типа данных преобразователя.
  • targettype

    • Имя целевого типа данных преобразователя.
  • funcname(argtype [, ...])

    • Функция, используемая для выполнения преобразования. Имя функции может быть указано с указанием схемы. Если оно не указано, Database ищет функцию в пути поиска схем. Тип возвращаемого значения функции должен совпадать с целевым типом преобразователя.

    • Функции реализации преобразователей могут принимать от одного до трёх аргументов. Первый аргумент должен быть того же типа, что и исходный тип преобразователя, либо бинарно совместим с ним. Второй аргумент, если он присутствует, должен иметь тип integer; он получает модификатор типа, связанный с целевым типом, или -1, если модификатор отсутствует. Третий аргумент, если он присутствует, должен иметь тип boolean; он получает значение true, если преобразование явное, и false в противном случае. Спецификация SQL требует различного поведения для явных и неявных преобразований в некоторых случаях. Этот аргумент предоставляется функциям, которым необходимо реализовать такое поведение. Рекомендуется не проектировать собственные типы данных таким образом.

    • Тип возвращаемого значения функции преобразования должен быть идентичен целевому типу преобразователя или бинарно совместим с ним.

    • Обычно преобразователь должен иметь разные исходный и целевой типы данных. Однако разрешается объявлять преобразователь с одинаковыми исходным и целевым типами, если его функция реализации принимает более одного аргумента. Это используется для представления специфичных для типа функций сжатия длины в системных каталогах. Указанная функция используется для приведения значения типа к значению модификатора типа, заданному вторым аргументом.

    • Когда преобразователь имеет разные исходный и целевой типы и функцию, принимающую более одного аргумента, он выполняет преобразование из одного типа в другой и одновременно применяет сжатие длины за один шаг. Если такой записи нет, приведение к типу, использующему модификатор, требует два шага: первый — преобразование между типами данных, второй — применение модификатора.

    • Преобразование в доменный тип или из него в настоящее время не оказывает никакого эффекта. Преобразования в доменный тип или из него используют преобразователи, связанные с базовым типом.

  • WITHOUT FUNCTION

    • Указывает, что исходный тип бинарно совместим с целевым типом, поэтому функция для выполнения преобразования не требуется.
  • WITH INOUT

    • Указывает, что преобразователь является преобразователем на основе ввода/вывода, который выполняется путём вызова функции вывода исходного типа и передачи полученной строки функции ввода целевого типа.
  • AS ASSIGNMENT

    • Указывает, что преобразователь может быть вызван неявно в контекстах присваивания.
  • AS IMPLICIT

    • Указывает, что преобразователь может быть вызван неявно в любом контексте.

Примечания

Обратите внимание, что в данной версии Database пользовательские функции, используемые в пользовательских преобразователях, должны быть определены как IMMUTABLE. Все скомпилированные модули (разделяемые библиотеки) для пользовательских функций должны находиться в одном и том же месте на каждом хосте в массиве Database (координаторе и всех сегментах). Это место также должно входить в LD_LIBRARY_PATH, чтобы сервер мог найти файлы.

Помните, что если вы хотите иметь возможность преобразовывать типы в обоих направлениях, вам нужно явно объявить преобразователи в обоих направлениях.

Обычно не требуется создавать преобразователи между пользовательскими типами и стандартными строковыми типами (text, varchar, char(n) и другими пользовательскими типами, определёнными как принадлежащие к строковой категории). Database предоставляет автоматические преобразователи на основе ввода/вывода для этих случаев. Автоматические преобразователи в строковые типы рассматриваются как присваивающие, а автоматические преобразователи из строковых типов — только как явные. Вы можете переопределить это поведение, объявив свой собственный преобразователь вместо автоматического, но обычно единственная причина сделать это — сделать преобразование более легко вызываемым, чем при стандартных настройках «только для присваивания» или «только явное». Другой возможной причиной может быть желание, чтобы преобразование работало иначе, чем функция ввода/вывода типа — подумайте дважды, прежде чем делать это. (Небольшое количество встроенных типов действительно имеет различное поведение при преобразованиях, в основном из-за требований стандарта SQL.)

Рекомендуется следовать соглашению об именовании функций реализации преобразователей по имени целевого типа данных, как это сделано для встроенных функций. Многие пользователи привыкли выполнять преобразование типов с помощью функциональной нотации, то есть typename(x).

Существуют два случая, когда конструкция вызова функции рассматривается как запрос преобразования, даже если она не соответствует какой-либо существующей функции. Если вызов name(x) не точно соответствует ни одной существующей функции, но name — это имя типа данных, и в pg_cast определён бинарно совместимый преобразователь из типа x в этот тип, то такой вызов будет интерпретирован как бинарно совместимое преобразование. Database делает это исключение, чтобы бинарно совместимые преобразователи можно было вызывать с использованием функционального синтаксиса, даже если они не имеют функции. Аналогично, если в pg_cast нет записи, но преобразование осуществляется в строковый тип или из него, вызов интерпретируется как преобразование на основе ввода/вывода. Это исключение позволяет вызывать преобразователи на основе ввода/вывода с использованием функционального синтаксиса.

Существует исключение из этого исключения: преобразователи на основе ввода/вывода из составных типов в строковые типы нельзя вызывать с использованием функционального синтаксиса, их необходимо записывать с явным указанием преобразования (в нотации CAST или ::). Это исключение существует потому, что после появления автоматических преобразователей на основе ввода/вывода оказалось слишком легко случайно вызвать такое преобразование, когда предполагалась функция или ссылка на столбец.

Примеры

Создание присваивающего преобразователя из типа bigint в тип int4 с использованием функции int4(bigint) (этот преобразователь уже предопределён в системе):

CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT;

Совместимость

Команда CREATE CAST соответствует стандарту SQL, за исключением того, что в SQL не предусмотрены бинарно совместимые типы или дополнительные аргументы функций реализации. Ключевое слово AS IMPLICIT также является расширением Database.

См. также

CREATE FUNCTION, CREATE TYPE, DROP CAST