CREATE TYPE

CREATE TYPE — определить новый тип данных

Синтаксис

CREATE TYPE имя AS
    ( [ имя_атрибута тип_данных [ COLLATE правило_сортировки ] [, ... ] ] )

CREATE TYPE имя AS ENUM
    ( [ 'метка' [, ... ] ] )

CREATE TYPE имя AS RANGE (
    SUBTYPE = подтип
    [ , SUBTYPE_OPCLASS = класс_оператора_подтипа ]
    [ , COLLATION = правило_сортировки ]
    [ , CANONICAL = каноническая_функция ]
    [ , SUBTYPE_DIFF = функция_разницы_подтипа ]
)

CREATE TYPE имя (
    INPUT = функция_ввода,
    OUTPUT = функция_вывода
    [ , RECEIVE = функция_получения ]
    [ , SEND = функция_отправки ]
    [ , TYPMOD_IN = функция_ввода_модификатора_типа ]
    [ , TYPMOD_OUT = функция_вывода_модификатора_типа ]
    [ , ANALYZE = функция_анализа ]
    [ , INTERNALLENGTH = { внутренняя_длина | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = выравнивание ]
    [ , STORAGE = хранение ]
    [ , LIKE = тип_образец ]
    [ , CATEGORY = категория ]
    [ , PREFERRED = предпочитаемый ]
    [ , DEFAULT = по_умолчанию ]
    [ , ELEMENT = элемент ]
    [ , DELIMITER = разделитель ]
    [ , COLLATABLE = сортируемый ]
)

CREATE TYPE имя

Описание

Команда CREATE TYPE регистрирует новый тип данных для использования в текущей базе данных. Пользователь, определивший тип, становится его владельцем.

Если задано имя схемы, то тип будет создан в указанной схеме, в противном случае — в текущей. Имя типа должно отличаться от имени любого существующего типа или домена в той же схеме. (Поскольку таблицы связаны с типами данных, имя типа также должно отличаться от имени любой существующей таблицы в той же схеме.)

Существует пять форм команды CREATE TYPE, как показано выше в блоке синтаксис. Они создают соответственно: составной тип, перечисляемый тип, диапазонный тип, базовый тип или тип-оболочку. Первые четыре из них рассматриваются ниже. Тип-оболочка представляет собой просто заготовку для типа, который будет определен позже; он создается командой CREATE TYPE без параметров, только с указанием имени типа. Типы-оболочки необходимы в качестве прямых ссылок при создании диапазонных и базовых типов, как это описано в соответствующих разделах.

Составные типы

Первая форма CREATE TYPE создает составной тип. Составной тип определяется списком имен атрибутов и типов данных. Для атрибута может быть также указано правило сортировки, если тип данных является сортируемым. Составной тип по существу совпадает с типом строки таблицы, но использование CREATE TYPE позволяет избежать необходимости создавать настоящую таблицу, когда требуется всего лишь определить тип. Отдельный составной тип может быть полезен, например, в качестве аргумента или возвращаемого типа функции.

Чтобы создать составной тип, необходимо иметь право USAGE для всех типов его атрибутов.

Перечисляемые типы

Вторая форма CREATE TYPE создает перечисляемый тип, как описано в разделе Перечисляемые типы. Перечисляемые типы принимают список заключенных в кавычки меток, каждая из которых может иметь длину не больше NAMEDATALEN байт (64 байта в стандартной сборке QHB). (Можно создать перечислимый тип без меток, но такой тип нельзя использовать для хранения значений, пока хотя бы одна метка не будет добавлена с помощью команды ALTER TYPE.)

Диапазонные типы

Третья форма CREATE TYPE создает новый диапазонный тип, как описано в разделе Диапазонные типы.

Подтип диапазонного типа может быть любым типом со связанным классом операторов B-дерева (для определения порядка значений в диапазоне). Обычно для определения порядка используется класс операторов B-дерева по умолчанию; чтобы изменить класс оператора, укажите его имя в параметре класс_операторов_подтипа. Если подтип поддерживает сортировку и вы хотите использовать правила сортировки, отличные от заданных по умолчанию, укажите нужные правила сортировки в параметре правило_сортировки.

Необязательная каноническая_функция должна принимать один аргумент определяемого диапазонного типа и возвращать значение того же типа. Эта настройка используется для преобразования значений диапазона в каноническую форму, когда это применимо. Дополнительную информацию см. в разделе Определение новых диапазонных типов. Создание канонической_функции сложнее, так как она должна быть определена до того, как диапазонный тип может быть объявлен. Для этого необходимо сначала создать тип-оболочку, который является заглушкой, не имеющей никаких свойств, кроме имени и владельца. Это делается путем выполнения команды CREATE TYPE имя без дополнительных параметров. Затем может быть объявлена функция с использованием типа-оболочки в качестве аргумента и результата, и наконец диапазонный тип может быть объявлен с использованием того же имени. Последнее действие автоматически заменяет тип-оболочку допустимым диапазонным типом.

Необязательная функция_разницы_подтипа должна принимать в качестве аргументов два значения типа подтип и возвращать значение double precision, представляющее собой разницу между двумя первыми значениями. Хотя эта функция необязательная, с помощью нее можно кардинально увеличить эффективность индексов GiST для столбцов диапазонного типа. Дополнительную информацию см. в разделе Определение новых диапазонных типов.

Базовые типы

Четвертая форма CREATE TYPE создает новый базовый тип (скалярный тип). Чтобы создать новый базовый тип, нужно быть суперпользователем. (Это ограничение сделано потому, что ошибочное определение типа может вызвать нарушения или даже сбой в работе сервера.)

Параметры могут перечисляться в любом порядке, а не только в том, который показан выше, и большинство из них являются необязательными. Перед определением типа необходимо зарегистрировать две или более функций (с помощью CREATE FUNCTION). Обязательными функциями являются функция_ввода и функция_вывода, а функции функция_получения, функция_отправки, функция_модификатора_типа, функция_вывода_модификатора_типа и функция_анализа можно не указывать. Как правило, эти функции должны быть написаны на C/RUST или другом языке низкого уровня.

Функция_ввода преобразует внешнее текстовое представление типа во внутреннее, используемое операторами и функциями, определенными для данного типа. Функция_вывода выполняет обратное преобразование. Функция ввода может быть объявлена как принимающая один аргумент типа cstring или как принимающая три аргумента с типами cstring, oid, integer. Первый аргумент — это вводимый текст в виде строки в стиле C, второй аргумент — собственный OID типа (за исключением типов массивов, для которых передается OID типа элемента), а третий — это модификатор_типа для целевого столбца, если он известен (и -1, если нет). Функция ввода должна возвращать значение нового типа данных. Обычно функция ввода должна быть объявлена строгой (STRICT); если это не так, при получении на вход значения NULL она будет вызываться с первым параметром NULL. В этом случае функция всё равно должна возвращать значение NULL, иначе будет вызвана ошибка. (Этот случай в основном предназначен для поддержки функций ввода доменных типов, которые не должны принимать значения NULL). Функция вывода должна быть объявлена как принимающая один аргумент нового типа данных, а возвращать должна тип cstring. Функции вывода не вызываются для значений NULL.

Необязательная функция_получения преобразует внешнее двоичное представление типа во внутреннее представление. Если эта функция не указана, тип не может участвовать в двоичном вводе. Двоичное представление должно быть выбрано так, чтобы оно легко преобразовывалось во внутреннюю форму, будучи при этом достаточно переносимым. (Например, для стандартных целочисленных типов данных в качестве внешнего двоичного представления используют сетевой порядок байтов, в то время как внутреннее представление определяется порядком байтов в процессоре.) Функция получения должна выполнять адекватную проверку, чтобы убедиться, что значение является допустимым. Функция получения может быть объявлена как принимающая один аргумент типа internal либо как принимающая три аргумента с типами internal, oid, integer. Первый аргумент — это указатель на буфер StringInfo, содержащий полученную байтовую строку; а остальные аргументы такие же, как и для функции ввода текста. Функция получения должна возвращать значение нового типа данных. Обычно функция получения должна быть объявлена строгой (STRICT); если это не так, при прочтении входного значения NULL, она будет вызываться с первым параметром NULL. В этом случае функция всё равно должна возвращать значение NULL, иначе будет вызвана ошибка. (Этот случай в основном предназначен для поддержки функций ввода доменных типов, которые не должны принимать значения NULL.) Аналогично, необязательная функция_отправки преобразует данные из внутреннего представления во внешнее двоичное представление. Если эта функция не указана, тип не может участвовать в двоичном выводе. Функция отправки должна быть объявлена как принимающая один аргумент нового типа данных, а возвращать тип bytea. Функции отправки не вызываются для значений NULL.

На этом этапе может возникнуть вопрос, как функции ввода и вывода могут быть объявлены принимающими или возвращающими значения нового типа, если они должны быть созданы до того, как новый тип объявлен. Ответ прост: тип сначала должен быть определен как тип-оболочка, который является заготовкой и не имеет никаких свойств, кроме имени и владельца. Он объявляется командой CREATE TYPE имя без дополнительных параметров. Тогда функции ввода/вывода на C/RUST могут быть определены, ссылаясь на этот пустой тип. Затем команда CREATE TYPE с полным определением заменит тип-оболочку на полное, допустимое определение типа, после чего новый тип можно будет использовать как обычно.

Необязательные функции функция_ввода_модификатора_типа и функция_вывода_модификатора_типа необходимы, если тип поддерживает модификаторы, то есть дополнительные ограничения, связанные с объявлением типа, такие как char(5) или numeric(30,2). QHB позволяет создавать типы, которые могут принимать одну или несколько простых констант или идентификаторов в качестве модификаторов. Однако эта информация должна быть упакована в одно неотрицательное целочисленное значение для хранения в системных каталогах. Функция_ввода_модификатора_типа получает объявленный(ые) модификатор(ы) в виде строки cstring. Функция должна проверить значения на допустимость (и вызвать ошибку, если они неверны) и, если они верны, выдать одно неотрицательное значение integer, которое будет сохранено в столбце «typmod». Модификаторы типа не будут приниматься, если функция_ввода_модификатора_типа для типа не указана. Функция_вывода_модификатора_типа преобразует внутреннее целочисленное значение typmod обратно в понятную форму для отображения пользователю. Она должна вернуть значение cstring, которое является строкой для добавления к имени типа; например, функция numeric должна вернуть (30,2). Функция_вывода_модификатора_типа, может быть не указана; в этом случае отображением по умолчанию будет сохраненное целочисленное значение typmod, заключенное в круглые скобки.

Необязательная функция_анализа выполняет сбор специфичной для типа статистики по столбцам с этим типом данных. По умолчанию команда ANALYZE попытается собрать статистику, используя операторы «равно» и «меньше, чем», если для типа определен класс операторов B-дерева по умолчанию. Для нескалярных типов такое поведение в большинстве случаев не подходит, поэтому его можно переопределить, указав собственную функцию анализа. Функция анализа должна быть объявлена как принимающая один аргумент типа internal, и возвращающая результат boolean. Подробно API функций анализа описан в разделе src/include/commands/vacuum.h.

Хотя сведения о внутреннем представлении нового типа известны только функциям ввода-вывода и другим функциям, созданным для работы с типом, существует еще несколько свойств внутреннего представления, которые должны быть объявлены в QHB. Прежде всего, это внутренняя_длина. Если базовый тип данных фиксированной длины, то внутренняя_длина указывается в виде положительного числа, а если тип переменной длины, во внутренней_длине задается значение VARIABLE. (Внутри же typlen принимает значение -1.) Внутреннее представление всех типов переменной длины должно начинаться с 4-байтового целого числа, задающего общую длину значения типа. (Обратите внимание, что поле длины часто кодируется, как описано в разделе TOAST; неразумно обращаться к нему напрямую.)

Необязательный флаг PASSEDBYVALUE указывает, что значения создаваемого типа данных передаются по значению, а не по ссылке. Типы, передаваемые по значению, должны быть фиксированной длины, и их внутреннее представление не может быть больше размера типа Datum (4 байта на одних машинах, 8 байт на других).

Параметр выравнивание указывает, как требуется выравнивать данные этого типа. Допустимые значения выравнивания по границам 1, 2, 4 или 8 байт. Обратите внимание, что типы переменной длины должны иметь выравнивание не менее 4 байт, так как они обязательно содержат своим первым компонентом int4.

Параметр хранение позволяет выбирать стратегии хранения для типов данных переменной длины. (Для типов фиксированной длины допускается только вариант plain.) Вариант plain указывает, что данные этого типа всегда будут храниться внутри и не будут сжаты. Вариант extended указывает, что система сначала попытается сжать большое значение данных, а затем, если оно и после сжатия окажется слишком большим, переместит его из строки основной таблицы. Вариант external позволяет переместить значение из основной таблицы, но система не будет пытаться сжать его. Вариант main разрешает сжатие, но препятствует перемещению значения из основной таблицы. (Элементы данных с этим вариантом хранения всё равно могут быть перемещены из основной таблицы, если нет другого способа уместить их в строке, но предпочтение будет отдано сохранению в основной таблице, в отличие от вариантов extended и external.)

Все значения параметра хранение, кроме plain, подразумевают, что функции типа данных могут принимать значения в формате toast, как описано в разделах TOAST и Использование TOAST. Такое значение просто определяет вариант хранения TOAST по умолчанию для столбцов отделяемого в TOAST типа данных; другие варианты для отдельных столбцов можно выбирать с помощью команды ALTER TABLE SET STORAGE.

Параметр тип_образец предоставляет альтернативный метод для указания основных свойств представления типа данных: скопировать их из другого существующего типа. Из указанного типа будут скопированы значения свойств внутренняя_длина, передача_по_значению, выравнивание и хранение. (Можно, хотя обычно это нежелательно, переопределить некоторые из этих значений, указав их вместе с предложением LIKE) Указывать представление типа таким образом особенно удобно при низкоуровневой реализации нового типа, которая некоторым образом «опирается» на существующий тип.

Параметры категория и предпочитаемый могут использоваться для поддержки в управлении тем, какое неявное приведение будет применяться в неоднозначных ситуациях. Каждый тип данных относится к категории, обозначаемой одним символом ASCII, и каждый тип в этой категории является либо «предпочтительным» либо нет. Анализатор запроса постарается выбрать приведение к предпочтительному типу (но только из других типов в той же категории); это правило полезно при разрешении перегруженных функций или операторов. Более подробную информацию см. в главе PL/pgSQL. Для типов, которые не имеют неявных приведений к какому-либо другому типу или из них, достаточно оставить значения по умолчанию. Однако для группы связанных типов, которые имеют неявные приведения, часто бывает полезно пометить их все как принадлежащие к категории и выбрать один или два «наиболее общих» типа как предпочтительные в категории. Параметр категория особенно полезен при добавлении пользовательского типа в существующую встроенную категорию, например, числовые или строковые типы. Однако также можно создавать новые, полностью пользовательские категории типов. В качестве имени такой категории можно выбрать любой ASCII-символ, кроме латинской заглавной буквы.

При желании можно назначить столбцам с этим типом данных значение по умолчанию, отличное от NULL, указав его после ключевого слова DEFAULT в этой команде. (Такое значение по умолчанию может быть переопределено явным предложением DEFAULT, добавленным при создании определенного столбца.)

Чтобы указать, что тип является массивом, задайте тип элементов массива с помощью ключевого слова ELEMENT. Например, чтобы определить массив 4-байтовых целых чисел (int4), задайте ELEMENT = int4. Ниже приведены дополнительные сведения о типах массивов.

Для указания разделителя, который будет использоваться между значениями во внешнем представлении массива с элементами этого типа, можно задать параметру delimiter определенный символ. Разделителем по умолчанию является запятая (,). Обратите внимание, что разделитель связан с типом элементов массива, а не с самим типом массива.

Если у необязательного логического параметра сортируемый установлено значение true, определения столбцов и выражения этого типа могут содержать информацию о правилах сортировки с помощью предложения COLLATE. Получение фактической пользы от этих правил зависит от реализаций функций, работающих с типом; просто от того, что тип помечен как сортируемый, эти правила действовать не будут.

Типы массивов

Каждый раз при создании пользовательского типа QHB автоматически создает связанный тип массива, имя которого состоит из имени типа элемента и знака подчеркивания перед ним. Полученное имя усекается, если это его длина превышает NAMEDATALEN байт. (Если имя, созданное таким образом, конфликтует с существующим именем типа, процесс повторяется до тех пор, пока не будет найдено непротиворечивое имя.) Этот неявно созданный тип массива имеет переменную длину и использует встроенные функции ввода и вывода array_in и array_out. Тип массива отражает любые изменения владельца или схемы его типа элемента и удаляется, если удаляется тип элемента.

Вы можете задать вопрос, зачем существует параметр ELEMENT, если система создает правильный тип массива автоматически. Единственный случай, когда полезно использовать ELEMENT, это когда вы создаете тип фиксированной длины, который оказывается массивом нескольких одинаковых элементов, и вы хотите, чтобы эти элементы были доступны при обращению по индексу, в дополнение к любым операциям, которые вы планируете реализовать для типа в целом. Например, тип point представляется в виде двух чисел с плавающей запятой, доступ к которым можно получить с помощью point[0] и point[1]. Обратите внимание, что эта функция работает только для типов фиксированной длины, внутренняя форма которых представляет собой точную последовательность одинаковых полей фиксированной длины. Тип массива переменной длины должен иметь обобщенное внутреннее представление, с которым умеют работать array_in и array_out. По историческим причинам (т.е. это определенно некорректно, но слишком поздно это менять), индексы в массивах фиксированной длины начинаются с нуля, а не с единицы, как для массивов переменной длины.

Параметры

имя

Имя создаваемого типа (может быть дополнено схемой).

имя_атрибута

Имя атрибута (столбца) для составного типа.

тип_данных

Имя существующего типа данных, который должен стать столбцом составного типа.

правило_сортировки

Имя существующего правила сортировки, которое должно быть связано со столбцом составного или диапазонного типа.

метка

Строковый литерал, представляющий текстовую метку, связанную с одним значением перечисляемого типа.

подтип

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

класс_оператора_подтипа

Имя класса операторов B-дерева для подтипа.

каноническая_функция

Имя функции канонизации для диапазонного типа.

функция_разницы_подтипа

Имя функции разницы для данного подтипа.

функция_ввода

Имя функции, преобразующей данные из внешней текстовой формы (представления) типа в его внутреннюю форму.

функция_вывода

Имя функции, преобразующей данные из внутренней формы (представления) типа во внешнюю текстовую форму.

функция_получения

Имя функции, преобразующей данные из внешней двоичной формы (представления) типа в его внутреннюю форму.

функция_отправки

Имя функции, преобразующей данные из внутренней формы типа во внешнюю двоичную форму типа.

функция_ввода_модификатора_типа

Имя функции, преобразующей массив модификаторов для данного типа во внутреннюю форму.

функция_вывода_модификатора_типа

Имя функции, преобразующей внутреннюю форму модификатора(ов) типа во внешнюю текстовую форму.

функция_анализа

Имя функции, выполняющей статистический анализ для данного типа данных.

внутренняя_длина

Числовая константа, указывающая длину внутреннего представления нового типа в байтах. По умолчанию предполагается, что тип имеет переменную длину.

выравнивание

Требуемое выравнивание для типа данных. Допустимые значения: char, int2, int4 или double; значение по умолчанию равно int4.

хранение

Стратегия хранения для данного типа данных. Допустимые значения: plain, external, extended или main; значение по умолчанию равно plain.

тип_образец

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

категория

Код категории (один символ ASCII) для этого типа. Значение по умолчанию: 'U', что означает «пользовательский тип» (user-defined). Другие коды категорий можно найти в разделе Таблица кодов категорий. Вы также можете выбрать другие символы ASCII для создания пользовательских категорий.

предпочитаемый

Если значение равно true, то этот тип является предпочтительным типом в своей категории типов. Значение по умолчанию равно false. Будьте очень осторожны при создании нового предпочтительного типа в рамках существующей категории типов, так как это может вызвать неожиданные изменения в поведении.

по_умолчанию

Значение по умолчанию для данного типа данных. Если этот параметр не задан, будет использовано значение по умолчанию, равное NULL.

элемент

Указывает что создаваемый тип является массивом; это параметр определяет тип элементов массива.

разделитель

Символ-разделитель, используемый между значениями в массивах, образованных из значений создаваемого типа.

сортируемый

Если значение равно true, в операциях с этим типом может учитываться информация о параметрах сортировки. Значение по умолчанию равно false.

Примечания

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

Рекомендуется избегать использования имен типов и таблиц, начинающихся с символа подчеркивания(_). Хотя сервер сможет сгенерировать другое имя, чтобы избежать конфликтов с пользовательскими именами, по-прежнему существует риск возникновения путаницы, особенно со старыми клиентскими приложениями, которые могут предположить, что имена типов, начинающиеся с подчеркивания, относится к типу массива.

Примеры

В этом примере создается составной тип, который затем используется в определении функции:

CREATE TYPE compfoo AS (f1 int, f2 text);

CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$
    SELECT fooid, fooname FROM foo
$$ LANGUAGE SQL;

В этом примере создается перечисляемый тип, который затем используется в определении таблицы:

CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');

CREATE TABLE bug (
    id serial,
    description text,
    status bug_status
);

В этом примере создается диапазонный тип:

CREATE TYPE float8_range AS RANGE (subtype = float8, subtype_diff = float8mi);

В этом примере создается базовый тип данных box, который затем используется в определении таблицы:

CREATE TYPE box;

CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function
);

CREATE TABLE myboxes (
    id integer,
    description box
);

Если бы внутренняя структура box была массивом из четырех элементов float4, можно было бы вместо этого использовать:

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function,
    ELEMENT = float4
);

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

В этом примере создается тип большого объекта и используется в определении таблицы:

CREATE TYPE bigobj (
    INPUT = lo_filein, OUTPUT = lo_fileout,
    INTERNALLENGTH = VARIABLE
);
CREATE TABLE big_objs (
    id integer,
    obj bigobj
);

Дополнительные примеры, включая соответствующие функции ввода и вывода, приведены в разделе Пользовательские типы.

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

Первая форма команды CREATE TYPE, которая создает составной тип, соответствует стандарту SQL. Другие формы являются расширением QHB. Команда CREATE TYPE в стандарте SQL также имеет другие формы, которые не реализованы в QHB.

Возможность создания составного типа без атрибутов является специфическим для QHB отклонением от стандарта (аналогично CREATE TABLE).

См. также

ALTER TYPE, CREATE DOMAIN, CREATE FUNCTION, DROP TYPE