Example of PostgreSQL Automatically Creating a Nested Composite Type

Whenever we create a table in PostgreSQL, a composite type is automatically created behind the scenes. This composite type is based on the table that we created. Each column in the table becomes a field in the composite type.

If the table already uses a composite type, then the composite type that PostgreSQL creates will include that type in its definition, thereby creating a situation where we effectively have a nested composite type.

Example

Suppose we create the following composite type:

CREATE TYPE address AS(
   street text, 
   city varchar(200),
   state varchar(50),
   postcode varchar(12),
   country_code char(3)
);

And we create the following table that uses that type:

CREATE TABLE person (
    person_id serial primary key,
    first_name varchar(100),
    last_name varchar(100),
    address_details address
    );

Here, the address_details column uses the address composite type that we created.

As mentioned, whenever we create a table, PostgreSQL automatically creates a composite type. The composite type gets the same name as the table. Therefore, when we created the person table, a composite type called person was created.

Given the person table already uses a composite type (for the address_details column), the new (automatically created) person composite type will include that composite type in its definition. Therefore, we’ll effectively have a nested composite type.

Using the Nested Composite Type

We could go ahead and create another table and assign the newly created composite type to a column:

CREATE TABLE person2 (
    details person
    );

Here, I created a table with one column. That column uses the person composite type that PostgreSQL created automatically when I created the person table in the previous example.

This isn’t without its limitations though. Probably the main limitation would be the fact that we can’t apply constraints against composite types (at least, not as of PostgreSQL 16).

Using our example, the original table had a primary key (the person_id column). When we created the person2 table, that column is represented by a field in the person composite type, and so it is no longer a primary key for the table. Of course, we could always create a separate primary key column in the person2 table.

It’s also possible to create a domain over the composite type, and then create any constraints as CHECK constraints of the domain.