Updated on 2026-03-04 GMT+08:00

CREATE TYPE

Function

CREATE TYPE creates a user-defined type (UDT). It encapsulates a group of related fields or values into a new and reusable data type. It is similar to the structure in programming languages, making the database table design more suitable for business logic, reducing redundancy, and improving readability. This function applies only to row-store tables.

There are composite types, base types, shell types, and enumerated types.

  • Composite types

    A composite type is defined by a list of attribute names and their data types. If the data type of an attribute is collatable, you can specify a collation for the attribute. A composite type is essentially the same as the row type of a table. However, if you only need to define a structured data type, using CREATE TYPE to avoid creating an actual table.

    An independent composite type is useful as the parameter or return type of a user-defined function. For example, a user table needs to store the home and work addresses (including the province, city, detailed address, and postal code). The address structure needs to be reused to avoid redundant table fields. In this case, you can create a composite type.

    When creating a composite type, you must have the USAGE permission on all data types of its attributes.

  • Base types

    To create a new base type (non-composite type or non-enumerated type), you need to define the input and output functions to encode and decode the type. This type is available for special service types (such as mobile number, ID card number, and amount with currency). The custom functions used by the base type must be coded in C or another low-level language.

    For example, the user contact information must be an 11-digit mobile number, non-digit characters are automatically filtered out to avoid invalid format recording.

  • Shell types

    With this parameter, you can create a shell type, which is a type name without a definition. To create a shell type, use CREATE TYPE with only the type name. Shell types are needed as forward references when base types are created.

  • Enumerated types

    An enumerated type is a list of enumerated values. Each value is a non-empty string with the maximum length of 64 bytes. Enumerated types are used to restrict field values to a fixed enumerated value set. They can replace the check constraints of other data types such as VARCHAR, improving data consistency and readability. For example, spelling errors can be avoided.

    For example, the order status can only be to be paid, paid, shipped, delivered, or cancelled. Ensure that the status values in all tables (order table and refund table) are the same to avoid spelling errors.

Precautions

  • This syntax applies only to row-store tables.
  • If a schema name is given, the type will be created in the specified schema. Otherwise, it will be created in the current schema. The type name must be distinct from the name of any existing type or domain in the same schema. (Because tables have associated data types, the type name must also be distinct from the name of any existing table in the same schema.)
  • When creating a base type, you can place parameters in any order. The input_function and output_function parameters are mandatory, and other parameters are optional.
  • You need to first create input and output functions and then create a new type. Input and output functions can be declared as results or parameters of the new type. The new type should first be defined as a shell type, which is a placeholder type that has no attributes except for the name and owner. This can be done by running CREATE TYPE name without extra parameters. Then, the I/O functions written in C can be defined to reference this shell type. Finally, CREATE TYPE with a full definition replaces the shell type with a complete, valid type definition. After that, the new type can be used normally.

Syntax

Composite type

1
2
CREATE TYPE name AS
    ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] )

Base type

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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

1
CREATE TYPE name

Enumerated type

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

Parameter Description

Table 1 Composite type parameters

Parameter

Description

Value Range

name

Specifies the name (optionally schema-qualified) of the type to be created.

It must comply with the naming convention.

attribute_name

Specifies the name of an attribute (column) for the composite type.

-

data_type

Specifies the data type of the composite type attribute. The data type must be an existing type in DWS, such as int, varchar (50), or date.

-

collation

Specifies the collation of the composite type attribute. It controls the collation and comparison logic of string data (for example, case-insensitive collation).

-

Table 2 Base type parameters

Parameter

Description

Value Range

name

Specifies the name (optionally schema-qualified) of the type to be created.

It must comply with the naming convention.

input_function

Specifies the name of a function that converts data from the external text format of a type to its internal format.

An input function can be declared as taking one parameter of the cstring type or taking three parameters of the cstring, oid, and integer types.

  • The cstring-type parameter is the input text as a C string.
  • The oid-type parameter is the OID of the type (except for array types, where the parameter is the element type OID of an array type).
  • The integer-type parameter is typmod of the destination column. -1 will be passed if typmod is not specified.

An input function must return a value of the data type itself. Generally, an input function must be declared as STRICT. If it is not, it will be called with a NULL parameter coming first when the system reads a NULL input value. In this case, the function must still return NULL unless an error raises. (This mechanism is designed for supporting domain input functions, which may need to reject NULL input values.)

-

output_function

Specifies the name of a function that converts data from the internal format of a type to its external text format.

An output function must be declared as taking one parameter of a new data type. It must return data of the cstring type. Output functions are not invoked for NULL values.

-

receive_function (optional)

Specifies the name of a function that converts data from the external binary format of a type to its internal format.

If this function is not used, the type cannot participate in binary input. It costs lower to convert the binary format to the internal format, more portable. (For example, the standard integer data types use the network byte order as an external binary representation, whereas the internal representation is in the machine's native byte order.) This function should perform adequate checks to ensure a valid value.

Also, this function can be declared as taking one parameter of the internal type or taking three parameters of the internal, oid, and integer types.

  • The internal-type parameter is a pointer to a StringInfo buffer holding received byte strings.
  • The oid- and integer-type parameters are the same as those of the text input function.

A receive function must return a value of the data type itself. Generally, a receive function must be declared as STRICT. If it is not, it will be called with a NULL parameter coming first when the system reads a NULL input value. In this case, the function must still return NULL unless an error raises. (This mechanism is designed for supporting domain receive functions, which may need to reject NULL input values.)

-

send_function (optional)

Specifies the name of a function that converts data from the internal format of a type to its external binary format.

If this function is not used, the type cannot participate in binary output. A send function must be declared as taking one parameter of a new data type. It must return data of the bytea type. Send functions are not invoked for NULL values.

-

type_modifier_input_function (optional)

Specifies the name of a function that converts an array of modifiers for a type to its internal format.

type_modifier_input_function and type_modifier_output_function are needed if a type supports modifiers, that is, optional constraints attached to a type declaration, such as char(5) or numeric(30,2). DWS allows user-defined types to take one or more simple constants or identifiers as modifiers. However, this information must be capable of being packed into a single non-negative integer value for storage in system catalogs. Declared modifiers are passed to type_modifier_input_function in the cstring array format. The parameter must check values for validity, throwing an error if they are wrong. If they are correct, the parameter will return a single non-negative integer value, which will be stored as typmod in a column. If the type does not have type_modifier_input_function, type modifiers will be rejected. type_modifier_output_function converts the internal integer typmod value back to a correct format for user display. It must return a cstring value, which is the exact string appending to the type name. For example, a numeric function may return (30,2). If the default display format is enclosing a stored typmod integer value in parentheses, you can omit type_modifier_output_function.

-

type_modifier_output_function (optional)

Specifies the name of a function that converts the internal format of modifiers for a type to its external text format.

-

analyze_function (optional)

Specifies the name of a function that performs statistical analysis for a data type.

By default, if there is a default B-tree operator class for a type, ANALYZE will attempt to gather statistics by using the "equals" and "less-than" operators of the type. This behavior is inappropriate for non-scalar types, and can be overridden by specifying a custom analysis function. The analysis function must be declared to take one parameter of the internal type and return a boolean result.

-

internallength (optional)

Specifies a numeric constant for specifying the length in bytes of the internal representation of a new type. By default, it is variable-length.

Although the details of the new type's internal representation are only known to I/O functions and other functions that you create to work with the type, there are still some attributes of the internal representation that must be declared to DWS. The most important one is internallength. Base data types can be fixed-length (when internallength is a positive integer) or variable-length (when internallength is set to VARIABLE; internally, this is represented by setting typlen to -1). The internal representation of all variable-length types must start with a 4-byte integer. internallength defines the total length.

-

PASSEDBYVALUE (optional)

Specifies that values of a data type are passed by value, rather than by reference. Types passed by value must be fixed-length, and their internal representation cannot be larger than the size of the Datum type (4 bytes on some machines, and 8 bytes on others).

-

alignment (optional)

Specifies the storage alignment required for a data type. It supports values char, int2, int4, and double. The default value is int4.

The allowed values equate to alignment on 1-, 2-, 4-, or 8-byte boundaries. Note that variable-length types must have an alignment of at least 4 since they must contain an int4 value as their first component.

-

storage (optional)

Specifies the storage strategy for a data type.

It supports values plain, external, extended, and main. The default value is plain.

  • plain specifies that data of a type will always be stored in-line and not compressed. (Only plain is allowed for fixed-length types.)
  • extended specifies that the system will first try to compress a long data value and will then move the value out of the main table row if it is still too long.
  • external allows a value to be moved out of the main table, but the system will not try to compress it.
  • main allows for compression, but discourages moving a value out of the main table. (Data items with this storage strategy might still be moved out of the main table if there is no other way to make a row fit. However, they will be kept in the main table preferentially over extended and external items.)

    All storage values except plain imply that the functions of the data type can handle values that have been toasted. A specified value determines the default TOAST storage strategy for columns of data types that support TOAST. Users can select different strategies for individual columns using ALTER TABLE SET STORAGE.

-

like_type (optional)

Specifies the name of an existing data type that has the same representation as a new type. The values of internallength, passedbyvalue, alignment, and storage are copied from this type, unless they are overridden by explicit specifications elsewhere in the CREATE TYPE command.

Specifying representation in this way is especially useful when the low-level implementation of a new type references an existing type.

-

category (optional)

Specifies the category code (a single ASCII character) for a type. The default value is U, indicating the user-defined type. You can also choose other ASCII characters to create custom categories.

The category and preferred parameters can be used to help determine which implicit cast excels in ambiguous situations. Each data type belongs to a category named by a single ASCII character, and each type is either preferred or not within its category. If this rule is helpful in resolving overloaded functions or operators, the parser will prefer casting to preferred types (but only from other types within the same category). For types that have no implicit casts to or from any other types, it is sufficient to leave these parameters at their default values. However, for a group of types that have implicit casts, mark them all as belonging to a category and select one or two of the most general types as being preferred within the category. The category parameter is helpful in adding a user-defined type to an existing built-in category, such as the numeric or string type. However, you can also create new entirely-user-defined type categories. Select any ASCII character other than an uppercase letter to name such a category.

-

preferred (optional)

Specifies whether a type is preferred within its type category. If it is, the value will be TRUE, or it is FALSE. The default value is FALSE. Be cautious when creating a new preferred type within an existing type category because this could cause significant changes in behavior.

-

default (optional)

Specifies the default value for a data type. If this parameter is omitted, the default value will be NULL.

A default value can be specified if you expect the columns of a data type to default to something other than the NULL value. You can also specify a default value using the DEFAULT keyword. (Such a default value can be overridden by an explicit DEFAULT clause attached to a particular column.)

-

element (optional)

Specifies the type of an array element when an array type is created. For example, to define an array of 4-byte integers (int4), set ELEMENT to int4.

-

delimiter (optional)

Specifies the delimiter character to be used between values in arrays made of a type.

delimiter can be set to a specific character. The default delimiter is a comma (,). Note that a delimiter is associated with the array element type, instead of the array type itself.

-

collatable (optional)

Specifies whether a type's operations can use collation information. If they can, the value will be TRUE, else FALSE (default).

If collatable is TRUE, column definitions and expressions of a type may carry collation information by using the COLLATE clause. It is the implementations of functions operating on the type that actually use the collation information. This use cannot be achieved merely by marking the type collatable.

-

Table 3 Shell type parameters

Parameter

Description

Value Range

name

Specifies the name (optionally schema-qualified) of the type to be created.

It must comply with the naming convention.

Table 4 Enumerated type parameters

Parameter

Description

Value Range

name

Specifies the name (optionally schema-qualified) of the type to be created.

It must comply with the naming convention.

label (optional)

Specifies a text label associated with an enumerated value. It is a non-empty string of up to 64 characters.

-

Example: Creating a Composite Type to Represent Address Information

  1. Creates a composite type to represent address information, including the province, city, street, and house number.

    1
    2
    3
    4
    5
    6
    CREATE TYPE address_type AS (
        province VARCHAR(20),
        city VARCHAR(50),
        street VARCHAR(100),
        number VARCHAR(10)
    );
    

  2. Use this type to create a table.

    1
    2
    3
    4
    5
    6
    CREATE TABLE customers (
        customer_id SERIAL PRIMARY KEY,
        name VARCHAR(100),
        billing_address address_type,
        shipping_address address_type
    );
    

  3. Insert data.

    1
    2
    3
    4
    5
    6
    INSERT INTO customers (name, billing_address, shipping_address)
    VALUES (
        'John Doe',
        ('SDong', 'QD', '123 Main St', '10001')::address_type,
        ('SDong', 'QD', '456 Park Ave', '10022')::address_type
    );
    

  4. Query data.

    1
    2
    3
    4
    5
    SELECT 
        name,
        (billing_address).street as bill_street,
        (shipping_address).city as ship_city
    FROM customers;
    

  5. Update data.

    1
    2
    3
    UPDATE customers 
    SET billing_address = ('SDong', 'QD','789 Broadway', '10003')::address_type
    WHERE customer_id = 1;
    

Example: Creating an Enumeration Type for E-commerce Order Management

  1. Create an enumeration type. The enumeration values include pending, paid, shipped, delivered, and cancelled.

    1
    2
    3
    CREATE TYPE order_status AS ENUM (
        'pending', 'paid', 'shipped', 'delivered', 'cancelled'
    );
    

  2. Create an order table. The status column uses the enumerated values of order_status.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE orders (
        order_id varchar(32) PRIMARY KEY,
        user_id int,
        goods_id int,
        amount decimal(10,2),
        status order_status DEFAULT 'pending', --The default status is pending.
        create_time timestamp DEFAULT now(),
        update_time timestamp DEFAULT now()
    );
    

  3. Insert test data.

    1
    2
    INSERT INTO orders (order_id, user_id, goods_id, amount) 
    VALUES ('ORD20260127001', 1001, 5001, 299.99); --The default status is pending.
    

  4. Update the order status. Only the enumerated values can be updated.

    1
    UPDATE orders SET status = 'paid', update_time = now() WHERE order_id = 'ORD20260127001';
    

  5. Query data.

    1
    SELECT * FROM orders WHERE order_id = 'ORD20260127001';
    

  6. Check that the system reports an error when there is any non-enumerated value to ensure data consistency.

    1
    UPDATE orders SET status = 'wait_pay' WHERE order_id = 'ORD20260127001';
    

Example: Creating a Basic Type for C Language Function Invocation

  1. Before creating a base type, create a shell type (placeholder) so that the type can be referenced when I/O functions are defined.

    1
    CREATE TYPE complex;
    

    This statement creates a placeholder for the type to be created, which can then be referenced when defining its I/O functions.

  2. Define I/O functions. These functions reference the preceding shell type complex. When creating a function, you must declare the function in NOT FENCED mode. That is, the function must be executed in the CN or DN process to reduce the fork process and communication overhead. For more information, see CREATE FUNCTION.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    -- Create the text input function.
    CREATE FUNCTION
    complex_in(cstring)
        RETURNS complex
        AS 'filename' -- Replace it with the actual .so file path, for example, /opt/dws/complex.so.
        LANGUAGE C IMMUTABLE STRICT not fenced;
    
    -- Create the text output function.
    CREATE FUNCTION
    complex_out(complex)
        RETURNS cstring
        AS 'filename' -- Replace it with the actual .so file path.
        LANGUAGE C IMMUTABLE STRICT not fenced;
    
    -- Create the binary input function.
    CREATE FUNCTION
    complex_recv(internal)
        RETURNS complex
        AS 'filename' -- Replace it with the actual .so file path.
        LANGUAGE C IMMUTABLE STRICT not fenced;
    
    -- Create the binary output function.
    CREATE FUNCTION
    complex_send(complex)
        RETURNS bytea
        AS 'filename' -- Replace it with the actual .so file path.
        LANGUAGE C IMMUTABLE STRICT not fenced;
    

  3. Create a complete base type to replace the previous shell type.

    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TYPE complex (
    internallength = 16,      -- Define the fixed length 16 bytes, corresponding to two double-precision values (each 8 bytes).
    input = complex_in,       -- Bind the text input function.
    output = complex_out,      -- Bind the text output function.
    receive = complex_recv,      -- Bind the binary input function.
    send = complex_send,      -- Bind the binary output function.
    alignment = double      -- Specifies an 8-byte alignment, as required for double-precision values.
    );
    

  4. View the C language code of the input, output, receive, and send functions for further development reference.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    -- Define a structure body Complex:
    typedef struct Complex {
        double      x;    
        double      y;    
    } Complex;
    
    -- Define an input function:
    PG_FUNCTION_INFO_V1(complex_in);   
    
    Datum
    complex_in(PG_FUNCTION_ARGS)
    {
            char       *str = PG_GETARG_CSTRING(0);
        double      x,           
                    y;           
        Complex    *result;      
    
        if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid input syntax for complex: \"%s\"",
                            str)));
    
        result = (Complex *) palloc(sizeof(Complex));
        result->x = x;
        result->y = y;
        PG_RETURN_POINTER(result);
    }
    
    -- Define an output function:
    PG_FUNCTION_INFO_V1(complex_out);
    
    Datum
    complex_out(PG_FUNCTION_ARGS)
    {
            Complex    *complex = (Complex *) PG_GETARG_POINTER(0);
            char       *result;
    
            result = (char *) palloc(100);
            snprintf(result, 100, "(%g,%g)", complex->x, complex->y);
            PG_RETURN_CSTRING(result);
    }
    
    -- Define a receive function:
    PG_FUNCTION_INFO_V1(complex_recv);
    
    Datum
    complex_recv(PG_FUNCTION_ARGS)
    {
        StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
        Complex    *result;
    
        result = (Complex *) palloc(sizeof(Complex));
        result->x = pq_getmsgfloat8(buf);
        result->y = pq_getmsgfloat8(buf);
        PG_RETURN_POINTER(result);
    }
    
    -- Define a send function:
    PG_FUNCTION_INFO_V1(complex_send);
    
    Datum
    complex_send(PG_FUNCTION_ARGS)
    {
        Complex    *complex = (Complex *) PG_GETARG_POINTER(0);
        StringInfoData buf;
    
        pq_begintypsend(&buf);
        pq_sendfloat8(&buf, complex->x);
        pq_sendfloat8(&buf, complex->y);
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    }
    

Helpful Links

ALTER TYPE, DROP TYPE