CREATE TYPE
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.
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.
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
| Parameter | Description |
|---|---|
name | Name of the type to create. Can be schema-qualified. |
attribute_name | Name of an attribute (column) in the composite type. |
data_type | Data type of the attribute. |
collation | Collation 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
| Parameter | Description |
|---|---|
name | Name of the type to create. |
label | A 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
| Parameter | Description |
|---|---|
name | Name of the type to create. |
subtype | The element type that the range is defined over. Must have an associated B-tree operator class. |
subtype_operator_class | B-tree operator class to use for the subtype, if not the default. |
collation | Collation for the range, if the subtype supports it. |
canonical_function | Function that converts range values to canonical form. The function takes and returns the range type. |
subtype_diff_function | Function 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.
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.
| Function | Required | Description |
|---|---|---|
input_function | Yes | Converts 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_function | Yes | Converts internal representation to external text. Accepts the new type and returns cstring. If the return value is NULL, the function is not called. |
receive_function | No | Converts 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_function | No | Converts 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_function | No | Validates 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_function | No | Converts 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_function | No | Collects 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
| Parameter | Description |
|---|---|
name | Name of the type to create. Can be schema-qualified. |
internallength | Size 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. |
PASSEDBYVALUE | Pass 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). |
alignment | Memory alignment boundary: char (1 byte), int2 (2 bytes), int4 (4 bytes), or double (8 bytes). Default: int4. Variable-length types require int4 or larger. |
storage | Storage strategy for variable-length types (fixed-length types always use plain). See Storage strategies below. |
like_type | Copy internallength, passedbyvalue, alignment, and storage from an existing type. These can be overridden individually in the same statement. |
category | A single ASCII character identifying the type category. Default: U (user-defined). Use other non-uppercase ASCII characters for custom categories. |
preferred | Set 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. |
default | Default value for the type. If not specified, defaults to null. |
element | Element 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. |
delimiter | Delimiter character used between array element values in external text representation. Default: , (comma). The delimiter belongs to the element type, not the array type. |
collatable | Set to true if the type supports collation. Functions must explicitly use collation information when this is set. Default: false. |
Storage strategies
| Strategy | Compression | Can move out of row | Behavior |
|---|---|---|---|
plain | No | No | Always stored in rows; no compression. Only valid option for fixed-length types. |
extended | Yes | Yes | Compresses excessively long values; moves data out of primary rows if needed. |
external | No | Yes | Moves data out of rows without compression. |
main | Yes | Last resort | Compresses 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_inandarray_outShares 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.
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 TYPEfor 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
);