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
создаёт тип-перечисление (такие типы описываются в Разделе 8.7). Перечисления принимают список меток в кавычках. Максимальная длина каждой метки — NAMEDATALEN
байт (64 байта в стандартной сборке Postgres Pro). (Также возможно создать перечисляемый тип без меток, но этот тип нельзя будет использовать для хранения значений, пока командой ALTER TYPE не будет добавлена хотя бы одна метка.)
Диапазонные типы
Третья форма CREATE TYPE
создаёт тип-диапазон (такие типы описываются в Разделе 8.17).
Задаваемый для диапазона подтип
может быть любым типом со связанным классом операторов B-дерева (что позволяет определить порядок значений в диапазоне). Обычно порядок элементов определяет класс операторов B-дерева по умолчанию, но его можно изменить, задав имя другого класса в параметре класс_операторов_подтипа
. Если подтип поддерживает сортировку и требуется, чтобы значения упорядочивались с нестандартным правилом сортировки, его имя можно задать в параметре правило_сортировки
.
Необязательная функция_нормализации
должна принимать один аргумент определяемого типа диапазона и возвращать значение того же типа. Она используется для преобразования значений диапазона в нормализованную форму, когда это уместно. За дополнительными сведениями обратитесь к Подразделу 8.17.8. Создаётся функция_нормализации
несколько нетривиально, так как она должна быть уже определена, прежде чем можно будет объявить тип-диапазон. Для этого нужно сначала создать тип-пустышку, который будет заготовкой типа, не имеющей никаких свойств, кроме имени и владельца. Это можно сделать, выполнив команду CREATE TYPE
без дополнительных параметров. Затем можно объявить функцию, для которой тип-пустышка будет типом аргумента и результата, и, наконец, объявить тип-диапазон с тем же именем. При этом тип-пустышка автоматически заменится полноценным типом-диапазоном.имя
Необязательная функция_разницы_подтипа
должна принимать в аргументах два значения типа подтип
и возвращать значение double precision
, представляющее разницу между двумя данными значениями. Хотя эту функцию можно не использовать, она позволяет кардинально увеличить эффективность индексов GiST для столбцов с типом-диапазоном. За дополнительными сведениями обратитесь к Подразделу 8.17.8.
Базовые типы
Четвёртая форма CREATE TYPE
создаёт новый базовый тип (скалярный тип). Чтобы создать новый базовый тип, нужно быть суперпользователем. (Это ограничение введено потому, что ошибочное определение типа может вызвать нарушения или даже сбой в работе сервера.)
Эти параметры могут перечисляться в любом порядке, не только в показанном выше, и большинство из них необязательные. Прежде чем создавать тип, необходимо зарегистрировать две или более функций (с помощью CREATE FUNCTION
). Обязательными являются функции функция_ввода
и функция_вывода
, тогда как функция_получения
, функция_отправки
, функция_модификатора_типа
, функция_вывода_модификатора_типа
и функция_анализа
могут отсутствовать. Обычно эти функции разрабатываются на C или другом низкоуровневом языке.
Функция_ввода
преобразует внешнее текстовое представление типа во внутреннее, с которым работают операторы и функции, определённые для этого типа. Функция_вывода
выполняет обратное преобразование. Функцию ввода можно объявить как принимающую один аргумент типа 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, ссылающиеся на этот тип. И наконец, команда имя
CREATE TYPE
с полным определением заменит тип-пустышку окончательным и полноценным определением, после чего новый тип можно будет использовать как обычно.
Необязательные функция_ввода_модификатора_типа
и функция_вывода_модификатора_типа
требуются, только если типы поддерживают модификаторы, или, другими словами, дополнительные ограничения, связываемые с объявлением типа, например char(5)
или numeric(30,2)
. В Postgres Pro типы могут принимать в качестве модификаторов одну или несколько простых констант или идентификаторов. Однако эти данные должны упаковываться в единственное неотрицательное целочисленное значение, которое и будет храниться в системных каталогах. Функция_ввода_модификатора_типа
получает объявленные модификаторы в виде строки cstring
. Она должна проверить значения на допустимость (и вызвать ошибку, если они неверны), а затем выдать неотрицательное значение integer
, которое будет сохранено в столбце «typmod». Если для типа не определена функция_ввода_модификатора_типа
, модификаторы типа приниматься не будут. Функция_вывода_модификатора_типа
преобразует внутреннее целочисленное значение typmod обратно, в форму, понятную пользователю. Она должна вернуть значение cstring
, которое именно в этом виде будет добавлено к имени типа; например, функция для numeric
должна вернуть (30,2)
. Функция_вывода_модификатора_типа
может быть опущена, в этом случае сохранённое целочисленное значение typmod по умолчанию будет выводиться просто в виде числа, заключённого в скобки.
Необязательная функция_анализа
выполняет сбор специфической для этого типа статистики в столбцах с таким типом данных. По умолчанию ANALYZE
пытается собрать статистику, используя операторы «равно» и «меньше», если для этого типа определён класс операторов B-дерева по умолчанию. Для нескалярных типов это поведение скорее всего не подойдёт, поэтому его можно переопределить, задав собственную функцию анализа. Эта функция должна принимать единственный аргумент типа internal
и возвращать результат boolean
.
Если особенности внутреннего представления нового типа известны функциям ввода/вывода и другим функциям, созданным специально для работы с этим типом, необходимо определить ряд характеристик внутреннего представления, о которых должен знать Postgres Pro. В первую очередь это internallength
(внутренняя длина). Если базовый тип данных имеет фиксированную длину, в internallength
указывается эта длина в виде положительного числа, а если длина переменная, в internallength
задаётся значение VARIABLE
. (Внутри при этом typlen
принимает значение -1.) Внутреннее представление всех типов переменной длины должно начинаться с 4-байтового целого, задающего общую длину значения этого типа. (Заметьте, что поле длины часто кодируется, как описано в Разделе 70.2; обращаться к нему напрямую неразумно.)
Необязательный флаг PASSEDBYVALUE
указывает, что значения этого типа данных передаются по значению, а не по ссылке. Типы, передаваемые по значению, должны быть фиксированной длины и их внутреннее представление не может быть больше размера типа Datum
(4 байта на одних машинах, 8 — на других).
Параметр выравнивание
определяет, как требуется выравнивать данные этого типа. Допускается выравнивание по границам 1, 2, 4 или 8 байт. Заметьте, что типы переменной длины должны быть выровнены как минимум по границе 4 байт, так как их первым компонентом обязательно должен быть int4
.
Параметр хранение
позволяет выбрать стратегию хранения для типов данных переменной длины. (Для типов с фиксированной длиной поддерживается только вариант plain
.) Если выбрана стратегия plain
, данные этого типа всегда хранятся внутри, без сжатия. Со стратегией extended
система сначала попытается сжать большое значение, а затем выносит его из строки основной таблицы, если оно всё же окажется слишком большим. С external
значение может быть вынесено из основной таблицы, но система не будет пытаться сжать его. Стратегия main
позволяет сжать данные, но не стремится вынести их из основной таблицы. (Элементы данных с этой стратегией хранения тем не менее могут быть вынесены из основной таблицы, если другого способа уместить их в строке нет, но всё же она отдаёт большее предпочтение основной таблице, по сравнению со стратегиями extended
и external
.)
Значения параметра хранение
, отличные от plain
, подразумевают, что функции типа данных могут принимать значения в формате toast, описанном в Разделе 70.2 и Подразделе 40.13.1. Эти значения просто определяют стратегию хранения TOAST по умолчанию для столбцов отделяемого в TOAST типа данных; пользователи могут выбирать другие стратегии для отдельных столбцов, применяя команду ALTER TABLE SET STORAGE
.
Параметр тип_образец
позволяет задать основные свойства представления типа другим способом: скопировать их из существующего типа. В частности, из указанного типа будут скопированы свойства internallength
, passedbyvalue
, alignment
и storage
. (Также возможно, хотя обычно это не требуется, переопределить некоторые из этих значений, указав их вместе с предложением LIKE
.) Определять представление типа таким образом особенно удобно, когда низкоуровневая реализация нового типа некоторым образом опирается на существующий тип.
Параметры категория
и предпочитаемый
позволяют определять, какое неявное приведение будет применяться в неоднозначных ситуациях. Каждый тип данных принадлежит к некоторой категории, обозначаемой одним символом ASCII, при этом он может быть, либо не быть «предпочитаемым» в этой категории. Анализатор запроса по возможности выберет приведение к предпочитаемому типу (но только среди других типов той же категории), когда это может помочь разрешить имя перегруженной функции или оператора. За дополнительными подробностями обратитесь к Главе 10. Если для типа не определено неявное приведение к какому-либо другому типу или обратное, для этих параметров достаточно оставить значения по умолчанию. Однако если есть группа связанных типов, для которых определены неявные приведения, часто бывает полезно пометить их все как принадлежащие некоторой категории и назначить один или два «наиболее общих» предпочитаемыми в этой категории. Параметр категория
особенно полезен при добавлении типа, определённого пользователем, в существующую встроенную категорию, например, в категорию числовых или строковых типов. Однако так же возможно создать категории типов, полностью определённые пользователем. В качестве имени такой категории можно выбрать любой ASCII-символ, кроме латинской заглавной буквы.
Если пользователь хочет назначить столбцам с этим типом данных значение по умолчанию, отличное от NULL, он может задать его в этой команде, указав его после ключевого слова DEFAULT
. (Такое значение по умолчанию можно переопределить явным предложением DEFAULT
, добавленным при создании столбца.)
Чтобы обозначить, что тип является массивом, укажите тип элементов массива, добавив ключевое слово ELEMENT
. Например, чтобы определить массив из четырёхбайтовых целых (int4
), укажите ELEMENT = int4
. Дополнительные сведения о типах массивов приведены ниже.
Параметр delimiter
позволяет задать разделитель, который будет вставляться между значениями во внешнем представлении массива с элементами этого типа. По умолчанию разделителем является запятая (,
). Заметьте, что разделитель связывается с типом элементов массива, а не с типом самого массива.
Если необязательный логический параметр сортируемый
равен true, определения столбцов и выражения с этим типом могут включать указания о порядке сортировки, в предложении COLLATE
. Как именно будут использоваться эти указания, зависит от реализации функций, работающих с этим типом; эти указания не действуют автоматически просто от того, что тип помечен как сортируемый.
Типы массивов
При создании любого нового типа Postgres Pro автоматически создаёт соответствующий тип массива, имя которого он получает, добавляя подчёркивание перед именем типа элементов. Если полученное имя оказывается не короче NAMEDATALEN
байт, оно усекается. (Если полученное таким образом имя конфликтует с именем уже существующего типа, процесс повторяется, пока не будет получено уникальное имя.) Этот неявно создаваемый тип массива имеет переменную длину и использует встроенные функции ввода и вывода array_in
и array_out
. Тип массива отражает любые изменения владельца или схемы связанного типа элемента и удаляется сам при удалении типа элемента.
Вы можете вполне резонно спросить, зачем нужен параметр ELEMENT
, если система создаёт правильный тип массива автоматически. Единственный случай, когда параметр ELEMENT
может быть полезен, это когда вы создаёте тип фиксированной длины, который внутри оказывается массивом одинаковых элементов, и вы хотите, чтобы к этим элементам можно было обращаться по индексу, помимо того, что вы можете реализовать какие угодно операции с типом в целом. Например, тип point
представлен просто как два числа с плавающей точкой, к которым можно обратиться так: point[0]
и point[1]
. Заметьте, что это работает только с типами фиксированной длины, которые представляют собой в точности последовательность одинаковых полей фиксированной длины. Тип массива переменной длины должен иметь обобщённое внутреннее представление, с которым умеют работать array_in
и array_out
. По историческим причинам (т. е. это определённо некорректно, но менять уже слишком поздно), индексы в массивах фиксированной длины начинаются с нуля, а не с 1, как в массивах переменной длины.
Параметры
имя
Имя создаваемого типа (возможно, дополненное схемой).
имя_атрибута
Имя атрибута (столбца) составного типа.
тип_данных
Имя существующего типа данных, который станет типом столбца составного типа.
правило_сортировки
Имя существующего правила сортировки, связываемого со столбцом составного типа или с типом-диапазоном.
метка
Строковая константа, представляющая текстовую метку, связанную с отдельным значением типа-перечисления.
подтип
Имя типа элемента, множество значений которого будет представлять тип-диапазон.
класс_оператора_подтипа
Имя класса операторов B-дерева для подтипа.
функция_нормализации
Имя функции нормализации для типа-диапазона.
функция_разницы_подтипа
Имя функции разницы для значений подтипа.
функция_ввода
Имя функции, преобразующей данные из внешнего текстового представления типа во внутреннюю форму.
функция_вывода
Имя функции, преобразующей данные из внутренней формы во внешнее текстовое представление типа.
функция_получения
Имя функции, преобразующей данные из внешнего двоичного представления типа во внутреннюю форму.
функция_отправки
Имя функции, преобразующей данные из внутренней формы во внешнее двоичное представление типа.
функция_ввода_модификатора_типа
Имя функции, преобразующей массив модификаторов типа во внутреннюю форму.
функция_вывода_модификатора_типа
Имя функции, преобразующей внутреннюю форму модификаторов типа во внешнее текстовое представление.
функция_анализа
Имя функции, производящей статистический анализ типа данных.
внутр_длина
Числовая константа, задающая размер внутреннего представления нового типа в байтах. По умолчанию предполагается, что тип имеет переменную длину.
выравнивание
Требуемое выравнивание для типа данных. Допустимые значения этого параметра, если он указывается:
char
,int2
,int4
илиdouble
; по умолчанию подразумеваетсяint4
.хранение
Стратегия хранения для типа данных. Допустимые значения этого параметра, если он указывается:
plain
,external
,extended
илиmain
; по умолчанию подразумеваетсяplain
.тип_образец
Имя существующего типа данных, от которого новый тип получит свойства представления. Из этого типа будут скопированы значения параметров
internallength
,passedbyvalue
,alignment
иstorage
, если их не переопределят явные указания, заданные дополнительно в этой командеCREATE TYPE
.категория
Код категории (один символ ASCII) для этого типа. По умолчанию подразумевается
'U'
(что означает пользовательский тип, «User-defined»). Коды других стандартных категорий можно найти в Таблице 54.65. Для нестандартных категорий можно выбрать другие ASCII-символы.предпочитаемый
Если значение этого параметра равно true, создаваемый тип будет предпочитаемым в своей категории. По умолчанию подразумевается false. Будьте очень осторожны, создавая новый предпочитаемый тип в существующей категории, так как это может поменять поведение выражений неожиданным образом.
по_умолчанию
Значение по умолчанию для создаваемого типа данных. Если не указано, значением по умолчанию будет NULL.
элемент
Создаваемый тип будет массивом; этот параметр определяет тип элементов массива.
разделитель
Символ, разделяющий значения в массивах, образованных из значений создаваемого типа.
сортируемый
Если значение этого параметра равно true, в операциях с создаваемым типом может учитываться информация о правилах сортировки. По умолчанию подразумевается false.
Примечания
Так как на использование типа данных после создания не накладываются ограничения, объявление базового типа или типа-диапазона по сути даёт всем право на выполнение функций, упомянутых в определении типа. Обычно это не проблема для таких функций, какие бывают полезны в определении типов. Но прежде чем создать тип, преобразование которого во внешнюю форму и обратно будет использовать «секретную» информацию, стоит подумать дважды.
В PostgreSQL до версии 8.3 имя генерируемого типа-массива всегда образовалось из имени типа элемента и добавленного спереди символа подчёркивания (_
). (Таким образом, допустимая максимальная длина имени типа была на символ меньше, чем длины других имён.) Хотя и сейчас имя типа массива чаще всего образуется таким образом, оно может быть и другим в случае достижения максимальной длины или конфликтов с именами пользовательских типов, начинающихся с подчёркивания. Поэтому полагаться на это соглашение в коде не рекомендуется. Вместо этого, имя типа массива, связанного с данным типом, следует определять по значению pg_type
.typarray
.
Вообще же можно посоветовать не использовать имена типов и таблиц, начинающиеся с подчёркивания. Хотя сервер сможет сгенерировать другое имя, не конфликтующее с пользовательским, некоторая путаница всё же возможна, особенно со старыми клиентскими приложениями, которые могут полагать, что имя типа, начинающееся с подчёркивания, всегда относится к типу массива.
В PostgreSQL до версии 8.2 у CREATE TYPE
отсутствовала форма для создания типа-пустышки. Поэтому для создания нового базового типа требовалось сначала создать функцию ввода. При таком подходе Postgres Pro воспринимал тип возврата функции ввода как имя нового типа данных и неявно создавал тип-пустышку, на который затем можно было ссылаться в определениях остальных функций ввода/вывода. Этот подход по-прежнему работает, но считается устаревшим и может быть запрещён в будущих версиях. Кроме того, во избежание непреднамеренного заполнения каталогов типами-пустышками, появляющимися в результате простых опечаток в определении функций, тип-пустышка будет создаваться таким образом, только если функция ввода написана на C.имя
Примеры
В этом примере создаётся составной тип, а затем он используется в определении функции:
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 );
Другие примеры, в том числе демонстрирующие подходящие функции ввода/вывода, можно найти в Разделе 40.13.
Совместимость
Первая форма команды CREATE TYPE
, создающая составной тип, соответствует стандарту SQL. Другие формы являются расширениями Postgres Pro. Для оператора CREATE TYPE
в стандарте SQL также определены другие формы, не реализованные в Postgres Pro.
Возможность создавать составной тип без атрибутов — специфическое отклонение Postgres Pro от стандарта (как и аналогичная особенность команды CREATE TABLE
).