Composite Types

Learn how to define and use PostgreSQL composite types to group related fields into a single reusable structure for table columns and function signatures.

5 min read · Last updated: March 2026 · Back to overview

Quick Answer

A PostgreSQL composite type is a user-defined data type that groups multiple fields—each with its own data type—into a single named structure. Use CREATE TYPE ... AS (...) to define one, then reference it as a column type or function parameter.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

PostgreSQL composite types let you bundle related fields into a single named structure. Once defined, a composite type can be used as the data type of a table column, returned from a function, or passed as a function argument—just like any built-in type.

Defining a composite type

Use CREATE TYPE ... AS (...) to declare a composite type with its field names and data types:

CREATE TYPE address_type AS (
  street   TEXT,
  city     TEXT,
  state    TEXT,
  zip_code INTEGER,
  country  TEXT
);

You can now use address_type as the type of any table column:

CREATE TABLE contacts (
  id      INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name    VARCHAR(255) NOT NULL,
  address address_type
);

Note: when you create a table, PostgreSQL automatically creates a corresponding composite type with the same name as the table. So CREATE TABLE contacts (...) also implicitly creates a contacts composite type.

Inserting and querying composite values

Construct a composite value using the ROW() expression (the ROW keyword is optional when multiple fields are listed):

INSERT INTO contacts (name, address)
VALUES (
  'John Smith',
  ROW('123 Main St', 'Houston', 'TX', 77001, 'USA')
);

-- Insert using individual field notation (dot syntax)
INSERT INTO contacts (name, address.street, address.city, address.state, address.zip_code)
VALUES ('Jane Doe', '4000 N. 1st Street', 'San Jose', 'CA', 95134);

To read individual fields from a composite column, wrap the column name in parentheses and append the field name with a dot:

SELECT
  id,
  name,
  (address).city,
  (address).state,
  (address).zip_code
FROM contacts;

Output:

 id |    name    |   city   | state | zip_code
----+------------+----------+-------+----------
  1 | John Smith | Houston  | TX    |    77001
  2 | Jane Doe   | San Jose | CA    |    95134

To expand all fields of a composite column, use the .* shorthand:

SELECT id, name, (address).* FROM contacts;

Updating composite columns

Update individual fields of a composite column using dot notation after SET (no parentheses around the column name in SET):

UPDATE contacts
SET address.country = 'USA'
WHERE id = 2
RETURNING *;

Output:

 id |   name   |                    address
----+----------+------------------------------------------------
  2 | Jane Doe | ("4000 N. 1st Street","San Jose",CA,95134,USA)

Practical tips

  • Composite types are best for logically grouped fields that always travel together, such as addresses, coordinates, or monetary amounts with currency.
  • Querying individual fields requires the (column_name).field syntax—forgetting the parentheses causes a parse error.
  • When using composite types in WHERE clauses, compare individual fields rather than the whole composite value for clarity and index usage.
  • Altering a composite type with ALTER TYPE is possible but can be disruptive—plan schema changes carefully in production.

Continue in PostgreSQL Data Types: Back to tutorial overview.

Related in this section: Boolean · CHAR, VARCHAR, and TEXT · NUMERIC

Frequently Asked Questions

What is a composite type in PostgreSQL?

A composite type is a user-defined data type that groups multiple named fields into a single structure, similar to a struct in C or a record in other languages. You define one with CREATE TYPE ... AS (...) and then use it wherever a regular data type is accepted.

How do I access a field inside a composite column?

Wrap the column name in parentheses and append the field name with a dot: (column_name).field_name. For example, (address).city. The parentheses are required to avoid ambiguity with table-qualified column names.

Can I use a composite type as a function return type?

Yes. Functions can declare a composite type as their RETURNS type, allowing them to return a structured record. You can also use RETURNS TABLE(...) as a more readable alternative for set-returning functions.

How do I update just one field of a composite column?

Use dot notation in the SET clause: UPDATE t SET col.field = value WHERE .... Do not wrap the column name in parentheses in SET—parentheses are only needed in SELECT to read fields.

What is the difference between a composite type and a table row type?

Every table in PostgreSQL automatically has an associated composite type of the same name. A standalone composite type created with CREATE TYPE exists independently of any table. Both can be used as column types or function parameters.