Система типов QHB
Типы данных QHB можно разделить на базовые типы, контейнерные типы, домены и псевдотипы.
Базовые типы
Базовые типы — это типы вроде integer, которые реализуются ниже уровня языка SQL (обычно на низкоуровневом языке, например C или RUST). В целом, они соответствуют тому, что часто называют абстрактными типами данных. QHB может работать с такими типами только через функции, предоставляемые пользователем, и понимает поведение таких типов только в той степени, в которой пользователь их описывает. Встроенные базовые типы описаны в главе Типы данных.
Перечислимые типы (enum) можно рассматривать как подкатегорию базовых типов. Основное различие в том, что их можно создать, используя одни только команды SQL, без какого-либо низкоуровневого программирования. Подробную информацию см. в разделе Перечислимые типы.
Контейнерные типы
В QHB есть три вида «контейнерных» типов, т. е. типов, которые содержат внутри себя несколько значений других типов. Это массивы, составные типы и диапазоны.
Массивы могут содержать несколько значений одного типа. Тип массива автоматически создается для каждого базового, составного, диапазонного и доменного типов. А вот массива массивов не бывает. С точки зрения системы типов, многомерные массивы не отличаются от одномерных. Подробную информацию см. в разделе Массивы.
Составные типы, или типы строк, создаются всякий раз, когда пользователь создает таблицу. Кроме того, с помощью команды CREATE TYPE можно создать «независимый» составной тип без привязки к таблице. Составной тип — это просто список типов со связанными именами полей. Значением составного типа является строка или запись из значений полей. Подробную информацию см. в разделе Составные типы.
Диапазонный тип может содержать два значения одного типа, являющиеся нижней и верхней границами диапазона. Диапазонные типы создает пользователь, хотя существует и несколько встроенных. Подробную информацию см. в разделе Диапазонные типы.
Домены
Домен основывается на конкретном нижележащем типе и во многих смыслах взаимозаменяем с ним. Однако у домена могут быть ограничения, сужающие его допустимые значения до подмножества, допускаемого нижележащим типом. Домены создаются с помощью команды SQL CREATE DOMAIN. Подробную информацию см. в разделе Доменные типы.
Псевдотипы
Существует несколько «псевдотипов» для особых целей. Псевдотипы не могут выступать в качестве столбцов таблиц или составляющих контейнерных типов, но их можно использовать для объявления аргументов и результирующих типов функций. Это обеспечивает в системе типов механизм, позволяющий определять особые классы функций. В таблице Псевдотипы перечислены существующие псевдотипы.
Полиморфные типы
Особый интерес представляют некоторые псевдотипы, называемые полиморфными типами, которые используются для объявления полиморфных функций. Особенности этих типов позволяют определить одну функцию, которая будет работать со множеством различных типов данных, причем конкретный тип (или типы) определяется из типов данных, фактически переданных ей в конкретном вызове. Полиморфные типы приведены в Таблице 1. Некоторые примеры их использования можно найти в подразделе Полиморфные функции SQL.
Таблица 1. Полиморфные типы
| Имя | Семейство | Описание |
|---|---|---|
| anyelement | Простое | Указывает, что функция принимает любой тип данных |
| anyarray | Простое | Указывает, что функция принимает любой тип массива |
| anynonarray | Простое | Указывает, что функция принимает любой тип, отличный от массива |
| anyenum | Простое | Указывает, что функция принимает любой перечислимый тип (см. раздел Перечислимые типы) |
| anyrange | Простое | Указывает, что функция принимает любой диапазонный тип (см. раздел Диапазонные типы) |
| anymultirange | Простое | Указывает, что функция принимает любой мультидиапазонный тип (см. раздел Диапазонные типы) |
| 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 и связанных конструкциях (см. раздел UNION, CASE и связанные конструкции). При выборе общего типа учитываются фактические типы аргументов anycompatible и anycompatiblenonarray, типы элементов массива аргументов anycompatiblearray, диапазонные подтипы аргументов anycompatiblerange и мультидиапазонные подтипы аргументов anycompatiblemultirange. Если присутствует anycompatiblenonarray, то общий тип должен быть отличным от массива. После определения общего типа аргументы в позициях anycompatible и anycompatiblenonarray автоматически приводятся к этому типу, а аргументы в позициях anycompatiblearray автоматически приводятся к типу массива для этого типа.
Поскольку невозможно выбрать диапазонный тип, зная только его подтип, использование anycompatiblerange и/или anycompatiblemultirange требует, чтобы все аргументы, объявленные с этим типом, имели один фактический диапазонный и/или мультидиапазонный тип, и чтобы этот тип соответствовал выбранному общему типу, поэтому в приведении типов для диапазонных значений нет необходимости. Как и в случае с anyrange и anymultirange, использование anycompatiblerange и anymultirange в качестве типа результата функции требует наличия у нее аргумента типа anycompatiblerange или anycompatiblemultirange.
Обратите внимание, что типа anycompatibleenum не существует. Такой тип был бы не очень полезен, поскольку обычно никаких неявных приведений к перечислимым типам нет, то есть не существует способа получить общий тип для различных перечислимых аргументов.
«Простое» и «общее» полиморфные семейства представляют два независимых набора переменных типов. Рассмотрим, например, объявление функции:
CREATE FUNCTION myfunc(a anyelement, b anyelement,
c anycompatible, d anycompatible)
RETURNS anycompatible AS ...
При фактическом вызове этой функции первые два аргумента обязательно должны иметь один тип. Последние два аргумента должны быть приводимыми к общему типу, но этому типу необязательно совпадать с типом первых двух аргументов. Результат будет иметь общий тип последних двух аргументов.
Функция с переменным числом аргументов (которая принимает переменное число аргументов, как описано в подразделе Функции SQL с переменным числом аргументов) тоже может быть полиморфной: для этого надо объявить ее последний параметр как VARIADIC anyarray или VARIADIC anycompatiblearray. В целях сопоставления аргументов и определения фактического типа результата такая функция ведет себя так же, как если бы в ней записали соответствующее число параметров anynonarray или anycompatiblenonarray.