Определяет новый преобразователь (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.