36.2. Система типов PostgreSQL #
Типы данных PostgreSQL делятся на базовые, типы-контейнеры, составные, доменные и псевдотипы.
36.2.1. Базовые типы #
Базовые типы — это типы, такие как integer
, которые реализуются ниже уровня языка SQL (обычно на низкоуровневом языке, например C). В общих чертах они соответствуют так называемым абстрактным типам данных. PostgreSQL может работать с такими типами только через функции, предоставленные пользователем, и понимать их поведение только в той степени, в какой его опишет пользователь. Встроенные базовые типы описываются в Главе 8.
Типы-перечисления (enum) можно считать подкатегорией базовых типов. Они отличаются от других типов тем, что их можно создавать просто командами SQL, обходясь без низкоуровневого программирования. За подробностями обратитесь к Разделу 8.7.
36.2.2. Типы-контейнеры #
В PostgreSQL есть три вида «типов-контейнеров», то есть типов, которые могут содержать в себе несколько значений других типов. Это массивы, составные типы и диапазоны.
Массивы могут содержать множество значений, имеющих один тип. Тип массива автоматически создаётся для каждого базового и составного типа, диапазона и домена, но не для массивов — массивы массивов не существуют. Для системы типов многомерные массивы не отличаются от одномерных. За дополнительными сведениями обратитесь к Разделу 8.15.
Составные типы, или типы строк, образуются при создании любой таблицы. С помощью команды CREATE TYPE также можно определить «независимый» составной тип, не связанный с таблицей. Составной тип представляет собой просто список типов с определёнными именами полей. Значением составного типа является строка таблицы или запись из значений полей. За дополнительными сведениями обратитесь к Разделу 8.16.
Диапазонный тип может содержать два значения одного типа, которые определяют нижнюю и верхнюю границу диапазона. Диапазонные типы создаются пользователем, хотя существует и несколько встроенных. За дополнительными сведениями обратитесь к Разделу 8.17.
36.2.3. Домены #
Домен основывается на определённом нижележащем типе и во многих аспектах взаимозаменяем с ним. Однако домен может иметь ограничения, уменьшающие множество допустимых для него значений относительно нижележащего типа. Домены создаются SQL-командой CREATE DOMAIN. За дополнительными сведениями обратитесь к Разделу 8.18.
36.2.4. Псевдотипы #
Для специальных целей существует также несколько «псевдотипов». Псевдотипы нельзя задействовать в столбцах таблицы или в типах-контейнерах, но их можно использовать в объявлениях аргументов и результатов функций. Это даёт возможность выделить в системе типов специальные классы функций. Все существующие псевдотипы перечислены в Таблице 8.27.
36.2.5. Полиморфные типы #
Особый интерес представляет подмножество псевдотипов, полиморфные типы, которые применяются в объявлениях полиморфных функций. Используя такие типы, можно объявить всего одну функцию, которая будет работать с разными типами данных, определяя конкретные типы в зависимости от того, значения каких типов были переданы ей при вызове. Полиморфные типы перечислены в Таблице 36.1. Некоторые примеры их использования показаны в Подразделе 36.5.11.
Таблица 36.1. Полиморфные типы
Имя | Семейство | Описание |
---|---|---|
anyelement | Простое | Указывает, что функция принимает любой тип |
anyarray | Простое | Указывает, что функция принимает любой тип массива |
anynonarray | Простое | Указывает, что функция принимает любой тип, отличный от массива |
anyenum | Простое | Указывает, что функция принимает любой тип-перечисление (см. Раздел 8.7) |
anyrange | Простое | Указывает, что функция принимает любой диапазонный тип (см. Раздел 8.17) |
anymultirange | Простое | Указывает, что функция принимает любой мультидиапазонный тип (см. Раздел 8.17) |
anycompatible | Общее | Указывает, что функция принимает любой тип, с автоматическим приведением нескольких аргументов к общему типу |
anycompatiblearray | Общее | Указывает, что функция принимает любой тип массива, с автоматическим приведением нескольких аргументов к общему типу |
anycompatiblenonarray | Общее | Указывает, что функция принимает любой тип, отличный от массива, с автоматическим приведением нескольких аргументов к общему типу |
anycompatiblerange | Общее | Указывает, что функция принимает любой диапазонный тип, с автоматическим приведением нескольких аргументов к общему типу |
anycompatiblemultirange | Общее | Указывает, что функция принимает любой мультидиапазонный тип данных и может автоматически приводить различные аргументы к общему типу данных |
Полиморфные аргументы и результаты связаны друг с другом и сводятся к конкретным типам данным при разборе запроса, вызывающего полиморфную функцию. Когда полиморфных аргументов несколько, фактические типы данных входных значений должны совмещаться, как описано далее. Если тип результата функции полиморфный или у неё имеются выходные параметры полиморфных типов, фактические типы этих результатов выводятся из типов полиморфных входных значений, как описано ниже.
Для «простого» семейства полиморфных типов действуют следующие правила совмещения и выведения типов:
В каждой позиции (в аргументах или возвращаемом значении), объявленной как anyelement
, может передаваться любой фактический тип данных, но в каждом конкретном вызове все эти фактические типы должны быть одинаковыми. Аналогичным образом, в каждой позиции, объявленной как anyarray
, может передаваться любой тип данных массива, но все фактические типы должны совпадать. Так же и во всех позициях, объявленных как anyrange
, должен передаваться одинаковый диапазонный тип, а в объявленных как anymultirange
— одинаковый мультидиапазонный тип.
Более того, если некоторые позиции объявлены как anyarray
, а другие как anyelement
, то фактическим типом в позициях anyarray
должен быть массив, элементы которого имеют тот же тип, что и значения в позициях anyelement
. Псевдотип anynonarray
обрабатывается так же, как anyelement
, но с дополнительным ограничением — фактический тип не должен быть типом массива. Псевдотип anyenum
тоже обрабатывается как anyelement
, но его фактические типы ограничиваются перечислениями.
Подобным образом, если одни позиции объявлены как anyrange
, а другие как anyelement
или anyarray
, фактическим типом в позициях anyrange
должен быть диапазон, подтип которого совпадает с типом элементов в позициях anyelement
и с типом, передаваемым в позициях anyarray
. Если есть позиции, объявленные anymultirange
, их фактический мультидиапазонный тип должен содержать диапазоны, соответствующие объявленным параметрам anyrange
, и базовые элементы, соответствующие параметрам, объявленным anyelement
и anyarray
.
Таким образом, когда с полиморфным типом объявлено несколько аргументов, в итоге допускаются только определённые комбинации фактических типов. Например, функция, объявленная как equal(anyelement, anyelement)
, примет в аргументах любые два значения, но только если их типы данных совпадают.
Когда с полиморфным типом объявлено возвращаемое значение функции, так же полиморфным должен быть минимум один аргумент, и фактический тип результата при конкретном вызове определится по типу фактически переданного полиморфного аргумента (или аргументов). Например, если бы отсутствовал механизм обращения к элементам массива, его можно было бы реализовать, создав функцию subscript(anyarray, integer) returns anyelement
. С таким объявлением первым фактическим аргументом должен быть массив, и из него будет выведен правильный тип результата при разборе запроса. В качестве другого примера можно привести функцию f(anyarray) returns anyenum
, которая будет принимать только массивы перечислений.
В большинстве случаев при разборе функции фактический тип данных для полиморфного результата может быть выведен из аргументов, имеющих другой полиморфный тип из того же семейства; например, подтип anyarray
может выводиться из anyelement
и наоборот. Исключение представляет полиморфный результат типа anyrange
— для него требуется аргумент типа anyrange
; вывести его фактический тип из типа аргументов anyarray
или anyelement
нельзя. Это объясняется тем, что на одном подтипе могут базироваться несколько диапазонных типов.
Заметьте, что anynonarray
и anyenum
представляют не отдельные типы переменных; это те же типы, что и anyelement
, но с дополнительными ограничениями. Например, объявление функции f(anyelement, anyenum)
равнозначно объявлению f(anyenum, anyenum)
: оба фактических аргумента должны быть одинаковыми типами-перечислениями.
Для «общего» семейства полиморфных типов работают примерно те же правила совмещения и выведения типов, что и для «простого» семейства, но есть одно важно отличие: фактические типы аргументов не должны совпадать, если они могут быть неявно приведены к некоторому общему типу. Этот общий тип выбирается по тем же правилам, что применяются в UNION
и подобных конструкциях (см. Раздел 10.5). При выборе общего типа учитываются фактические типы аргументов anycompatible
и anycompatiblenonarray
, типы элементов в аргументах anycompatiblearray
, подтипы диапазонов в аргументах anycompatiblerange
и подтипы мультидиапазонов в аргументах anycompatiblemultirange
. Если присутствует тип anycompatiblenonarray
, общим типом не должен быть тип массива. После того как общий тип определён, аргументы anycompatible
и anycompatiblenonarray
автоматически приводятся к этому типу, а аргументы anycompatiblearray
приводятся к типу-массиву с элементами этого типа.
Так как невозможно выбрать диапазонный тип, зная только его подтип, при использовании anycompatiblerange
и/или anycompatiblemultirange
необходимо, чтобы все аргументы, объявленные с этим типом, имели один диапазонный и/или мультидиапазонный тип, а его подтип соответствовал выбранному общему типу, что позволяет обойтись без приведения типов для диапазонных значений. Как и в случае с типами anyrange
и anymultirange
, типы anycompatiblerange
и anycompatiblemultirange
можно использовать в качестве типа результата функции, только если у неё имеется аргумент того же типа (anycompatiblerange
или anycompatiblemultirange
).
Заметьте, что типа anycompatibleenum
не существует. Такой тип был бы не очень полезным, так как никаких неявных приведений к типам-перечислениям обычно нет, то есть не существует способа найти общий тип для двух различных перечислений.
Полиморфные семейства «простое» и «общее» представляют два независимых набора переменных типов. Рассмотрите, например, объявление:
CREATE FUNCTION myfunc(a anyelement, b anyelement, c anycompatible, d anycompatible) RETURNS anycompatible AS ...
Когда эта функция вызывается, первые два аргумента обязательно должны иметь один и тот же тип. Последние два аргумента должны быть приводимыми к общему типу, причём этот тип может не совпадать с типом первых двух аргументов. Этот общий тип станет типом результата.
Функции с переменным числом аргументом (описанные в Подразделе 36.5.6) тоже могут быть полиморфными: для этого их последний параметр описывается как VARIADIC
anyarray
или VARIADIC
anycompatiblearray
. Для целей сопоставления аргументов и определения фактического типа результата такая функция представляется так же, как если бы в ней явно объявлялось нужное число параметров anynonarray
или anycompatiblenonarray
.