CREATE TYPE
Эта страница переведена при помощи нейросети GigaChat.
CREATE TYPE
- создание нового типа данных.
Синтаксис
CREATE TYPE name AS
( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] )
CREATE TYPE name AS ENUM
( [ 'label' [, ... ] ] )
CREATE TYPE name AS RANGE (
SUBTYPE = subtype
[ , SUBTYPE_OPCLASS = subtype_operator_class ]
[ , COLLATION = collation ]
[ , CANONICAL = canonical_function ]
[ , SUBTYPE_DIFF = subtype_diff_function ]
[ , MULTIRANGE_TYPE_NAME = multirange_type_name ]
)
CREATE TYPE name (
INPUT = input_function,
OUTPUT = output_function
[ , RECEIVE = receive_function ]
[ , SEND = send_function ]
[ , TYPMOD_IN = type_modifier_input_function ]
[ , TYPMOD_OUT = type_modifier_output_function ]
[ , ANALYZE = analyze_function ]
[ , SUBSCRIPT = subscript_function ]
[ , INTERNALLENGTH = { internallength | VARIABLE } ]
[ , PASSEDBYVALUE ]
[ , ALIGNMENT = alignment ]
[ , STORAGE = storage ]
[ , LIKE = like_type ]
[ , CATEGORY = category ]
[ , PREFERRED = preferred ]
[ , DEFAULT = default ]
[ , ELEMENT = element ]
[ , DELIMITER = delimiter ]
[ , COLLATABLE = collatable ]
)
CREATE TYPE name
Описание
CREATE TYPE
регистрирует новый тип данных для использования в текущей базе данных. Пользователь, создающий тип, становится его владельцем.
Если указано имя схемы, тип создается в этой схеме. В противном случае — в текущей. Имя типа должно отличаться от имени любого существующего типа или домена в той же схеме. Поскольку таблицы также ассоциированы с типами данных, имя типа должно отличаться и от имени любой таблицы в этой же схеме.
Существует пять форм CREATE TYPE
, как показано в синтаксисе: они создают соответственно составной тип, перечисляемый тип, диапазонный тип, базовый тип или временный тип. Первые четыре рассмотрены ниже. Временный тип — это просто заготовка (заполнитель) для типа, который будет определен позже. Он создается с помощью CREATE TYPE
без параметров, кроме имени типа. Такие временные типы используются как предварительные ссылки при создании диапазонных и базовых типов, как описано далее.
Составные типы
Первая форма CREATE TYPE
создает составной тип. Он задается списком имен атрибутов и соответствующих типов данных. Для атрибута также можно указать правило сортировки, если его тип поддерживает сортировку. Составной тип фактически аналогичен строковому типу таблицы, но CREATE TYPE
позволяет создать такой тип без необходимости создавать саму таблицу. Отдельно определенный составной тип может быть полезен, например, в качестве аргумента или возвращаемого значения функции.
Для создания составного типа необходимо иметь привилегию USAGE
на все указанные типы атрибутов.
Перечислимые типы
Вторая форма CREATE TYPE
создает перечисляемый тип. Перечисляемые типы задаются списком заключенных в кавычки меток, каждая из которых должна быть короче NAMEDATALEN
байт (64 байта в стандартной сборке PostgreSQL). Можно создать перечисляемый тип без меток, но он не может хранить значения до тех пор, пока не будет добавлена хотя бы одна метка через ALTER TYPE.
Типы диапазонов
Третья форма CREATE TYPE
создает новый диапазонный тип.
Подтип диапазонного типа может быть любым типом, для которого существует соответствующий класс операторов B-tree, определяющий порядок значений. Обычно используется класс операторов B-tree по умолчанию; чтобы использовать другой, можно указать его имя через параметр subtype_opclass
. Если подтип поддерживает сортировку, и нужно использовать нестандартное правило сортировки, укажите его через параметр collation
.
Необязательная функция canonical
должна принимать один аргумент определяемого диапазонного типа и возвращать значение того же типа. Она используется для приведения значений диапазона к канонической форме. Создание такой функции довольно сложно, так как она должна быть определена до создания диапазонного типа. Чтобы это сделать, сначала необходимо создать временный тип (заготовку), выполнив команду CREATE TYPE
имя без параметров. Затем можно объявить функцию, используя этот временный тип как аргумент и результат. Наконец, можно объявить диапазонный тип с тем же именем — при этом временный тип автоматически заменяется действительным диапазонным типом.
Необязательная функция subtype_diff
должна принимать два значения подтипа и возвращать значение типа double precision
, представляющее разницу между этими двумя значениями. Хотя эта функция не обязательна, ее наличие значительно повышает эффективность GiST-индексов по колонкам с диапазонным типом.
Необязательный параметр multirange_type_name
задает имя соответствующего мультидиапазонного типа. Если имя не указано, оно формируется автоматически следующим образом:
- если имя диапазонного типа содержит подстроку
range
, она заменяется наmultirange
; - если нет, к имени диапазонного типа добавляется суффикс
_multirange
.
Базовые типы
Четвертая форма CREATE TYPE
создает новый базовый (скалярный) тип. Для создания базового типа требуется быть суперпользователем. Это ограничение необходимо, так как ошибочное определение типа может привести к сбоям или аварийному завершению работы сервера.
Параметры могут указываться в любом порядке, не только в приведенном выше. Большинство параметров являются необязательными. До создания типа необходимо зарегистрировать как минимум две функции с помощью CREATE FUNCTION
: обязательные функции input_function
и output_function
. Дополнительно можно определить функции receive_function
, send_function
, type_modifier_input_function
, type_modifier_output_function
, analyze_function
и subscript_function
. Обычно такие функции реализуются на C или другом низкоуровневом языке.
Функция input_function
преобразует внешнее текстовое представление значения в его внутреннее представление, используемое операторами и функциями типа. output_function
выполняет обратное преобразование.
Функция ввода может быть объявлена с одним аргументом типа cstring
или с тремя аргументами: cstring
, oid
, integer
. Первый аргумент — текст ввода в виде строки C, второй — OID самого типа (для массивов — OID элемента), третий — модификатор типа (typmod
), если он известен (если нет — передается -1).
Функция ввода должна возвращать значение создаваемого типа. Обычно она должна быть объявлена как STRICT
; если нет, она будет вызвана с NULL
в качестве первого аргумента при чтении NULL
значения. В этом случае функция должна вернуть NULL
, если только не выбрасывает ошибку. Такой подход чаще всего используется в функциях ввода для доменов.
Функция вывода должна принимать один аргумент нового типа и возвращать cstring
. Она не вызывается для NULL
значений.
Функция receive_function
(опционально) преобразует внешнее бинарное представление значения во внутреннее. Если функция не определена, тип не сможет использоваться с бинарным вводом. Бинарное представление должно быть легко преобразуемым во внутреннее, но в то же время достаточно переносимым.
Функция приема может быть объявлена с одним аргументом типа internal
или с тремя: internal
, oid
, integer
. Первый аргумент — указатель на буфер StringInfo
, содержащий полученные байты. Остальные аналогичны аргументам функции текстового ввода.
Функция должна возвращать значение создаваемого типа. Обычно объявляется как STRICT
.
Аналогично, send_function
преобразует внутреннее представление в бинарное внешнее. Если не указана, тип не сможет использоваться с бинарным выводом. Функция должна принимать один аргумент нового типа и возвращать bytea
. Не вызывается для NULL-значений.
Чтобы определить функции с аргументами или результатами нового типа до его создания, сначала создается временный тип: CREATE TYPE name
. Это заглушка, не имеющая свойств кроме имени и владельца. Затем регистрируются функции, ссылающиеся на этот временный тип. После этого выполняется полноценный CREATE TYPE
, который заменяет заглушку на полноценное определение типа.
Функции type_modifier_input_function
и type_modifier_output_function
требуются, если тип поддерживает модификаторы (например, char(5)
или numeric(30,2)
). PostgreSQL допускает один или несколько простых констант или идентификаторов в качестве модификаторов.
Функция type_modifier_input_function
получает массив cstring
с модификаторами, проверяет их и возвращает одно неотрицательное целое число, которое будет сохранено как typmod
.
Если функция не указана, модификаторы для типа будут запрещены.
type_modifier_output_function
преобразует typmod
обратно в строку, отображаемую пользователю (например, numeric
может возвращать строку (30,2)
). При ее отсутствии typmod
будет отображаться как целое число в скобках.
Функция analyze_function
используется для сбора статистики по типу. По умолчанию ANALYZE
использует операторы равенства и «меньше», если у типа есть стандартный B-tree класс операторов. Для не скалярных типов это может быть неуместно, поэтому можно указать пользовательскую функцию анализа. Она должна принимать один аргумент типа internal
и возвращать boolean
. Подробный интерфейс описан в src/include/commands/vacuum.h
.
Функция subscript_function
позволяет обращаться к типу с использованием синтаксиса индексов (например, val[1]
). Тип при этом не становится полноценным массивом (например, не используется с ARRAY[]
). Функция должна принимать один аргумент типа internal
и возвращать internal
— указатель на структуру с методами индексирования. Подробный API для функций подстрок приведен в src/include/nodes/subscripting.h
. Также может быть полезно прочитать реализацию массива в src/backend/utils/adt/arraysubs.c
или более простой код в contrib/hstore/hstore_subs.c
. Дополнительная информация представлена в разделе «Типы массивов» ниже.
Внутреннее представление типа определяется функциями ввода/вывода и другими функциями, но PostgreSQL необходимо указать некоторые свойства явно. Главное из них — internallength
. Тип может быть фиксированной длины (положительное целое число) или переменной (VARIABLE
, внутри представляется как typlen = -1
). Внутреннее представление переменных типов должно начинаться с 4-байтового целого, задающего общую длину значения. Длина может быть закодирована, смотрите раздел «TOAST».
Флаг PASSEDBYVALUE
указывает, что значения передаются по значению, а не по ссылке. Такой тип должен быть фиксированной длины и помещаться в один Datum
(4 или 8 байт в зависимости от платформы).
Параметр alignment
задает выравнивание при хранении: 1, 2, 4 или 8 байт. Для переменных типов минимальное выравнивание — 4 байта, так как первым элементом должен быть int4
.
Параметр storage
определяет стратегию хранения переменных типов:
plain
— хранение в строке таблицы без сжатия (только для фиксированной длины);extended
— сначала сжатие, затем возможно вынос из строки;external
— только вынос, без сжатия;main
— допускается сжатие, но старается оставить в строке.
Элементы данных со стратегией хранения могут по-прежнему перемещаться из основной таблицы, если нет другого способа подогнать строку, но они будут предпочтительно оставаться в основной таблице вместо элементов extended
и external
.
Все стратегии кроме plain
предполагают, что функции типа работают с значениями в формате toast (смотрите раздел «TOAST»). Стратегия влияет только на поведение по умолчанию, можно переопределить ее для конкретной колонки через ALTER TABLE SET STORAGE
.
Параметр like_type
позволяет скопировать свойства хранения (internallength
, passedbyvalue
, alignment
, storage
) с существующего типа. Можно переопределить отдельные свойства. Полезно при использовании внутреннего представления другого типа.
Параметры category
и preferred
влияют на выбор неявных преобразований типов. Все типы делятся на категории (один ASCII-символ). В каждой категории может быть «предпочтительный» тип. Парсер отдает предпочтение такому типу при выборе перегруженных функций. Если тип не имеет неявных преобразований, эти параметры можно не указывать. В группе родственных типов полезно отнести их к категории и пометить один или два как предпочтительные. Параметр category
особенно полезен при добавлении пользовательского типа к существующей встроенной категории, такой как числовые или строковые типы. Тем не менее также возможно создавать новые полностью определенные пользователем категории типов. Выберите любой символ ASCII, отличный от заглавной буквы, чтобы назвать такую категорию.
Можно указать значение по умолчанию, если пользователь хочет, чтобы столбцы этого типа данных принимали значение по умолчанию, отличное от нулевого значения. Укажите значение по умолчанию с помощью ключевого слова DEFAULT
. Такое значение по умолчанию может быть переопределено явным условием DEFAULT
, прикрепленным к конкретному столбцу.
Чтобы указать, что тип представляет собой массив фиксированной длины, используется ключевое слово ELEMENT
, например ELEMENT = int4
для массива 4-байтовых целых. Дополнительную информацию смотрите в разделе «Типы массивов» ниже.
Для указания разделителя между элементами массива в текстовом представлении используется параметр delimiter
. По умолчанию — запятая. Разделитель задается на уровне типа элемента, а не самого массива.
Если установлен флаг collatable
, определения столбцов и выражения этого типа могут использовать COLLATE
для указания сортировки. Реализация функций, работающих с этим типом, должна фактически использовать информацию о сопоставлении — это не происходит автоматически.
Типы массивов
При создании пользовательского типа PostgreSQL автоматически создает связанный с ним тип массива. Его имя формируется путем добавления символа подчеркивания перед именем типа элемента, при необходимости оно усекается до длины NAMEDATALEN
. Если сгенерированное имя уже занято, процесс повторяется до получения уникального имени. Такой массивный тип является переменной длины и использует встроенные функции array_in
и array_out
для ввода и вывода. Именно этот тип применяется системой при использовании конструкций вида ARRAY[]
с пользовательским типом. Тип массива отслеживает изменения владельца или схемы типа элемента и удаляется при удалении типа элемента.
Можно задаться вопросом: зачем нужен параметр ELEMENT
, если система и так создает массивный тип автоматически? Основной случай, когда ELEMENT
полезен — создание типа фиксированной длины, который внутренне представляет собой массив одинаковых компонентов, к которым нужно обеспечить доступ через индексирование, помимо прочих операций над типом в целом.
Например, тип point
реализован как два значения с плавающей запятой, доступ к которым возможен как point[0]
и point[1]
. Следует отметить, что это работает только для типов фиксированной длины, внутреннее представление которых — строго последовательность одинаковых полей фиксированной длины. По историческим причинам (то есть это ошибка, но слишком поздно ее исправлять), индексирование в таких типах начинается с нуля, а не с единицы, как у массивов переменной длины.
Указание параметра SUBSCRIPT
позволяет обращаться к значениям типа по индексу, даже если тип не считается массивом в полном смысле. Описанное выше поведение для фиксированных массивов реализуется с помощью функции обработчика raw_array_subscript_handler
, которая используется автоматически при указании ELEMENT
без SUBSCRIPT
для типа фиксированной длины.
При указании собственной функции SUBSCRIPT
параметр ELEMENT
указывать не обязательно, если только обработчику индекса не требуется обращаться к typelem
, чтобы определить, что возвращать. Однако стоит учитывать, что ELEMENT
заставляет систему считать, что новый тип содержит или каким-либо образом зависит от типа элемента. В результате, например, нельзя будет изменить свойства типа элемента, если где-либо используется тип, от него зависящий.
Параметры
name
- Имя типа данных, при необходимости дополненное схемой, который должен быть создан.
attribute_name
- Имя атрибута (столбца) составного типа.
data_type
- Имя существующего типа данных, который будет использоваться как тип столбца в составном типе.
collation
- Имя существующей сортировки, которая должна быть связана со столбцом составного типа или с диапазонным типом.
label
- Строковый литерал, представляющий текстовую метку, связанную с одним значением перечислимого типа.
subtype
- Имя элементного типа, диапазоны которого будет представлять создаваемый диапазонный тип.
subtype_operator_class
- Имя класса операторов B-tree, применимого к элементному диапазонному типу.
canonical_function
- Имя функции канонизации для диапазонного типа.
subtype_diff_function
- Имя функции, вычисляющей разницу между двумя значениями элементного типа.
multirange_type_name
- Имя соответствующего мультидиапазонного типа.
input_function
- Имя функции, преобразующей данные из текстового внешнего представления типа во внутреннее.
output_function
- Имя функции, преобразующей данные из внутреннего представления типа во внешнее текстовое.
receive_function
- Имя функции, преобразующей данные из бинарного внешнего представления во внутреннее.
send_function
- Имя функции, преобразующей данные из внутреннего представления в бинарное внешнее.
type_modifier_input_function
- Имя функции, преобразующей массив модификаторов типа во внутреннюю форму.
type_modifier_output_function
- Имя функции, преобразующей внутреннюю форму модификаторов типа в текстовое представление.
analyze_function
- Имя функции, выполняющей сбор статистики для типа данных.
subscript_function
- Имя функции, определяющей поведение при обращении к значению типа по индексу.
internallength
- Числовая константа, указывающая длину во внутреннем представлении типа (в байтах). По умолчанию считается переменной длины.
alignment
- Требуемое выравнивание данных типа в памяти. Допустимые значения:
char
,int2
,int4
илиdouble
. По умолчанию —int4
.
storage
- Стратегия хранения данных типа. Допустимые значения:
plain
,external
,extended
илиmain
. По умолчанию —plain
.
like_type
- Имя существующего типа данных, с которым новый тип будет иметь ту же структуру представления. Параметры
internallength
,passedbyvalue
,alignment
иstorage
копируются от указанного типа, если не переопределены явно в командеCREATE TYPE
.
category
- Код категории (один ASCII-символ) для нового типа. По умолчанию —
'U'
(пользовательский тип). Стандартные коды категорий приведены в таблице pg_user_mapping. Также можно использовать другие ASCII-символы для создания собственных категорий типов.
preferred
- Указывает, является ли тип предпочтительным в своей категории (
true
илиfalse
). По умолчанию —false
. Изменение предпочтительности внутри существующей категории может непредсказуемо повлиять на поведение приведения типов.
default
- Значение по умолчанию для типа данных. Если не указано — используется
null
.
element
- Создаваемый тип — массив, и задает тип элементов этого массива.
delimiter
- Разделительный символ, используемый между значениями в массивах данного типа. По умолчанию — запятая.
collatable
- Указание, могут ли операции над типом использовать информацию о сортировке (
true
илиfalse
). По умолчанию —false
.
Примечания
Поскольку после создания типа данных не накладывается никаких ограничений на его использование, создание базового или диапазонного типа фактически приравнивается к предоставлению публичного права на выполнение всех функций, указанных в определении типа. Обычно это не вызывает проблем, так как такие функции, как правило, безопасны. Но прежде чем создать тип, преобразование которого во внешнюю форму и обратно будет использовать «секретную» информацию, стоит подумать дважды.
До версии PostgreSQL 8.3 имя автоматически создаваемого типа массива всегда представляло собой имя элементного типа с добавленным в начале символом подчеркивания (_
). Из-за этого длина имен типов была на один символ меньше, чем у остальных объектов. Хотя такая схема все еще используется по умолчанию, имя типа массива может отличаться в случае, если имя достигает максимальной длины или возникает конфликт с пользовательским типом, начинающимся с подчеркивания. Поэтому писать код, который полагается на это соглашение об именовании, не рекомендуется. Вместо этого следует использовать столбец pg_type.typarray
, чтобы получить тип массива, связанный с конкретным типом.
Рекомендуется избегать использования имен типов и таблиц, начинающихся с подчеркивания. Хотя сервер автоматически изменяет имена создаваемых типов массивов, чтобы избежать конфликтов с пользовательскими именами, остается риск путаницы — особенно при использовании старого клиентского ПО, которое может предполагать, что имя типа, начинающееся с подчеркивания, всегда указывает на массив.
До версии PostgreSQL 8.2 синтаксис создания оболочки типа CREATE TYPE name
не существовал. Для создания нового базового типа нужно было сначала создать функцию ввода. В этом случае PostgreSQL сначала «видит» имя нового типа данных как тип возвращаемого значения функции ввода. Оболочка типа создается автоматически, и затем на нее можно ссылаться в определениях остальных функций ввода/вывода. Этот способ все еще работает, но считается устаревшим и может быть запрещен в будущих версиях. Кроме того, чтобы избежать случайного засорения системных каталогов оболочками типов из-за опечаток в определениях функций, создание оболочки таким способом допускается только если функция ввода написана на языке C.
В версиях PostgreSQL начиная с 16 желательно, чтобы входные функции базовых типов выдавали ошибки «мягкие» с использованием нового механизма errsave()
/ereturn()
, вместо генерации исключений ereport()
, как это делалось в предыдущих версиях. Дополнительную информацию см. в разделе src/backend/utils/fmgr/README
.
Примеры
В этом примере создается составной тип, а затем он используется в определении функции:
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. Остальные формы являются расширениями PostgreSQL. В стандарте SQL также определены другие формы оператора CREATE TYPE
, которые в PostgreSQL не реализованы.
Возможность создать составной тип с нулевым числом атрибутов является отклонением от стандарта, специфичным для PostgreSQL (аналогично случаю с CREATE TABLE
).