CREATE TYPE

Updated at:
Copy as MD

CREATE TYPE registers a new user-defined data type in the current database. The user who runs the statement automatically becomes the owner of the new type.

PolarDB supports five variants of CREATE TYPE: composite, enumerated, range, base, and shell. This topic covers the first four. Shell types are placeholders used as forward references when creating range and base types—covered in those sections below.

Note

Type names must be unique within a schema. A new type name cannot conflict with an existing type, domain, or table in the same schema, because tables have associated row types.

Syntax

Composite type

CREATE [ OR REPLACE ] TYPE name AS
    ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] );

Enumerated type

CREATE TYPE name AS ENUM
    ( [ 'label' [, ... ] ] );

Range type

CREATE TYPE name AS RANGE (
    SUBTYPE = subtype
    [ , SUBTYPE_OPCLASS = subtype_operator_class ]
    [ , COLLATION = collation ]
    [ , CANONICAL = canonical_function ]
    [ , SUBTYPE_DIFF = subtype_diff_function ]
);

Base type

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 ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , LIKE = like_type ]
    [ , CATEGORY = category ]
    [ , PREFERRED = preferred ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
    [ , COLLATABLE = collatable ]
);

Shell type

CREATE TYPE name;

Composite type

A composite type is a named set of attribute–type pairs, similar to a table's row type. Unlike a table, a composite type has no storage of its own—it exists purely as a type definition for use in function signatures, return values, and column definitions.

-- Define a composite type
CREATE TYPE compfoo AS (f1 int, f2 text);

-- Use it as a function return type
CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$
    SELECT fooid, fooname FROM foo
$$ LANGUAGE SQL;

OR REPLACE behavior

OR REPLACE overwrites an existing composite type with the same name. Use it to update a type definition without dropping and recreating it.

Warning

Replacing a type affects all database objects that depend on it. Review dependencies before using OR REPLACE.

Permissions

To create a composite type, you need the USAGE privilege on all data types used as attribute types.

Parameters

ParameterDescription
nameName of the type to create. Can be schema-qualified.
attribute_nameName of an attribute (column) in the composite type.
data_typeData type of the attribute.
collationCollation to apply to the attribute, if the attribute's data type supports it.

Enumerated type

An enumerated type is an ordered list of string labels. Labels must be enclosed in quotation marks and are limited to NAMEDATALEN bytes (64 bytes by default).

-- Define an enumerated type
CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');

-- Use it as a column type
CREATE TABLE bug (
    id serial,
    description text,
    status bug_status
);

An enumerated type can be created with no labels. It cannot store values until at least one label is added via ALTER TYPE ... ADD VALUE.

Parameters

ParameterDescription
nameName of the type to create.
labelA quoted string representing one value in the enumeration.

Range type

A range type represents a continuous interval of values from a subtype. The subtype must have a B-tree operator class to define value ordering—the default B-tree operator class is used unless you specify otherwise.

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

Canonical function

The optional canonical function converts range values to a standard representation. For example, an integer range [1,4) and [1,3] represent the same set of integers—a canonical function can normalize both to [1,4).

Because the canonical function must reference the range type before it is fully defined, create a shell type first:

-- Step 1: Create a shell type as a forward reference
CREATE TYPE myrange;

-- Step 2: Define the canonical function referencing the shell type
CREATE FUNCTION myrange_canonical(myrange) RETURNS myrange AS ... ;

-- Step 3: Replace the shell type with the full definition
CREATE TYPE myrange AS RANGE (
    SUBTYPE = integer,
    CANONICAL = myrange_canonical
);

Parameters

ParameterDescription
nameName of the type to create.
subtypeThe element type that the range is defined over. Must have an associated B-tree operator class.
subtype_operator_classB-tree operator class to use for the subtype, if not the default.
collationCollation for the range, if the subtype supports it.
canonical_functionFunction that converts range values to canonical form. The function takes and returns the range type.
subtype_diff_functionFunction that returns the difference between two subtype values as double precision. Optional, but improves the efficiency of GiST (Generalized Search Tree) indexes on range columns.

Base type

A base type (also called a scalar type) defines entirely new internal storage behavior. Creating a base type requires superuser privileges.

Important

Defining a base type improperly can cause unexpected behavior or crash the server.

Required functions

Before running CREATE TYPE for a base type, register at least two functions with CREATE FUNCTION. In most cases, these functions must be written in C or another low-level language because they interact directly with the internal database representation.

FunctionRequiredDescription
input_functionYesConverts external text to internal representation. Accepts cstring (or cstring, oid, integer) and returns the new type. The oid parameter is the object identifier (OID) of the base type; the integer parameter is the typmod value (-1 if unknown). In most cases, declared as STRICT: if NULL is input, the function is not called.
output_functionYesConverts internal representation to external text. Accepts the new type and returns cstring. If the return value is NULL, the function is not called.
receive_functionNoConverts external binary to internal representation. Accepts a StringInfo buffer as internal (or internal, oid, integer). Enables binary protocol support. In most cases, declared as STRICT: if NULL is received, the function is not called.
send_functionNoConverts internal representation to external binary. Accepts the new type and returns bytea. If the return value is NULL, the function is not called.
type_modifier_input_functionNoValidates type modifiers (e.g., the 5 in char(5)). Receives declared modifiers as an array of cstring, throws an error if a modifier is invalid, and returns a non-negative integer stored in typmod. If this function is not defined, any type modifiers are rejected.
type_modifier_output_functionNoConverts the stored typmod integer back to display form (e.g., (30,2) for numeric). If the default representation is to enclose typmod in parentheses, this function can be omitted.
analyze_functionNoCollects statistics for the type. Accepts internal and returns boolean. Use when the default statistics collection is not appropriate.

Because input_function and output_function must reference the new type before it is fully defined, create a shell type first:

-- Step 1: Create a shell type as a forward reference
CREATE TYPE box;

-- Step 2: Define the required I/O functions
CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;

-- Step 3: Replace the shell type with the full definition
CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function
);

-- Use it as a column type
CREATE TABLE myboxes (
    id integer,
    description box
);

If box internally stores four float4 values and you want element-level subscript access:

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

Parameters

ParameterDescription
nameName of the type to create. Can be schema-qualified.
internallengthSize of the internal representation in bytes. Use VARIABLE for variable-length types; sets typlen to -1 internally. Variable-length types must begin with a 4-byte integer holding the total length.
PASSEDBYVALUEPass values by value rather than by reference. Only valid for fixed-length types whose size fits in the Datum type (4 or 8 bytes depending on platform).
alignmentMemory alignment boundary: char (1 byte), int2 (2 bytes), int4 (4 bytes), or double (8 bytes). Default: int4. Variable-length types require int4 or larger.
storageStorage strategy for variable-length types (fixed-length types always use plain). See Storage strategies below.
like_typeCopy internallength, passedbyvalue, alignment, and storage from an existing type. These can be overridden individually in the same statement.
categoryA single ASCII character identifying the type category. Default: U (user-defined). Use other non-uppercase ASCII characters for custom categories.
preferredSet to true to make this type the preferred implicit conversion target within its category. Default: false. Use with care—adding a preferred type to an existing category can change parser behavior unexpectedly.
defaultDefault value for the type. If not specified, defaults to null.
elementElement type, if this type is a fixed-length array of identical elements (e.g., ELEMENT = float4 for a type holding four floats). Enables subscript access starting from index 0.
delimiterDelimiter character used between array element values in external text representation. Default: , (comma). The delimiter belongs to the element type, not the array type.
collatableSet to true if the type supports collation. Functions must explicitly use collation information when this is set. Default: false.

Storage strategies

StrategyCompressionCan move out of rowBehavior
plainNoNoAlways stored in rows; no compression. Only valid option for fixed-length types.
extendedYesYesCompresses excessively long values; moves data out of primary rows if needed.
externalNoYesMoves data out of rows without compression.
mainYesLast resortCompresses data; moves out of rows only when no other option is available to keep row size acceptable.

All strategies except plain support TOAST (The Oversized-Attribute Storage Technique). The strategy set here is the column default; change it later with ALTER TABLE ... SET STORAGE.

Array types

When you create any new type, PolarDB automatically creates a corresponding array type. The array type is named by prefixing the element type name with an underscore (_). If the generated name exceeds NAMEDATALEN, PolarDB truncates and adjusts it to avoid conflicts.

The auto-created array type:

  • Has variable length

  • Uses built-in functions array_in and array_out

  • Shares the ownership and schema of its element type

  • Is deleted automatically when the element type is deleted

Even though PolarDB auto-creates array types, the ELEMENT option is still required when creating a fixed-length type that is essentially an array of multiple identical elements and you want to allow direct subscript access to individual elements. For fixed-length array types, subscripts start from 0. For variable-length array types, subscripts start from 1.

Note

Avoid naming your types or tables with a leading underscore. Names starting with _ are reserved for auto-generated array type names.

Usage notes

  • If you specify a schema name, the type is created in that schema. Otherwise, it is created in the current schema.

  • The parameters of CREATE TYPE for base types can be specified in any order.

  • An empty enumerated type (no labels) is valid but cannot store values until labels are added with ALTER TYPE.

Examples

Create a composite type for use as a function return type

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

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

Update an existing composite type definition

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

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

Create an enumerated type and use it in a table

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

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

Create a range type

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

Create a base type

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
);

Create a large object type

CREATE TYPE bigobj (
    INPUT = lo_filein, OUTPUT = lo_fileout,
    INTERNALLENGTH = VARIABLE
);

CREATE TABLE big_objs (
    id integer,
    obj bigobj
);