Columns

Column definitions: identity, generated, compression, storage, statistics, and the COLUMN reference block.

Column definitions live in the ( ) list (Part 1). Per-column attributes that PostgreSQL expresses as ALTER TABLE ... ALTER COLUMN statements go in the COLUMN name { } block inside the table’s { } (Part 2).

Standard column types

TABLE products (
    id          BIGINT GENERATED ALWAYS AS IDENTITY,
    sku         TEXT    NOT NULL,
    price       NUMERIC(10,2) NOT NULL DEFAULT 0,
    tags        TEXT[]  NOT NULL DEFAULT '{}',
    metadata    JSONB,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT pk_products PRIMARY KEY (id)
);
-- emits
CREATE TABLE "public"."products" (
    "id"         bigint GENERATED ALWAYS AS IDENTITY,
    "sku"        text NOT NULL,
    "price"      numeric(10,2) NOT NULL DEFAULT 0,
    "tags"       text[] NOT NULL DEFAULT '{}',
    "metadata"   jsonb,
    "created_at" timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT "pk_products" PRIMARY KEY ("id")
);

GENERATED ALWAYS AS (computed column)

TABLE order_items (
    quantity   INTEGER NOT NULL,
    unit_price NUMERIC(10,2) NOT NULL,
    total      NUMERIC(12,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
    CONSTRAINT pk_order_items PRIMARY KEY (order_item_id)
);
-- emits
CREATE TABLE "public"."order_items" (
    "quantity"   integer NOT NULL,
    "unit_price" numeric(10,2) NOT NULL,
    "total"      numeric(12,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
    CONSTRAINT "pk_order_items" PRIMARY KEY ("order_item_id")
);

GENERATED … AS IDENTITY (sequence-backed)

TABLE users (
    id_always BIGINT GENERATED ALWAYS AS IDENTITY,
    id_default BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1),
    CONSTRAINT pk PRIMARY KEY (id_always)
);
-- emits
CREATE TABLE "public"."users" (
    "id_always"  bigint GENERATED ALWAYS AS IDENTITY,
    "id_default" bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1),
    CONSTRAINT "pk" PRIMARY KEY ("id_always")
);

COLUMN reference block

COLUMN name { } inside the table’s { } block holds attributes that PG expresses as ALTER TABLE ALTER COLUMN statements.

TABLE users (
    email      TEXT NOT NULL,
    status     user_status NOT NULL,
    payload    JSONB,
    notes      TEXT,
    CONSTRAINT pk_users PRIMARY KEY (id)
)
{
    COLUMN email {
        COMMENT    "Verified email address";
        STATISTICS 300;
        GRANTS     { SELECT TO reporting_role; }
    }

    COLUMN status {
        STATISTICS 500;
    }

    COLUMN payload {
        COMPRESSION lz4;
        STORAGE     extended;
    }

    COLUMN notes {
        STATISTICS 0;
    }
}
-- emits
COMMENT ON COLUMN "public"."users"."email" IS 'Verified email address';
ALTER TABLE "public"."users" ALTER COLUMN "email"   SET STATISTICS 300;
ALTER TABLE "public"."users" ALTER COLUMN "status"  SET STATISTICS 500;
ALTER TABLE "public"."users" ALTER COLUMN "payload" SET COMPRESSION lz4;
ALTER TABLE "public"."users" ALTER COLUMN "payload" SET STORAGE extended;
ALTER TABLE "public"."users" ALTER COLUMN "notes"   SET STATISTICS 0;
GRANT SELECT ("email") ON TABLE "public"."users" TO "reporting_role";

Full COLUMN block attribute reference

AttributeEmits
COMMENT 'text'COMMENT ON COLUMN t.c IS '...'
STATISTICS nALTER TABLE t ALTER COLUMN c SET STATISTICS n
COMPRESSION methodALTER TABLE t ALTER COLUMN c SET COMPRESSION m
STORAGE typeALTER TABLE t ALTER COLUMN c SET STORAGE s
DEPRECATED "msg"COMMENT ON COLUMN t.c IS '[DEPRECATED] msg'
RENAMED FROM oldALTER TABLE t RENAME COLUMN old TO new
GRANTS { }GRANT privilege (col) ON TABLE t TO role
REVOKES { }REVOKE privilege (col) ON TABLE t FROM role

Column rename

The new name goes in the ( ) list and the COLUMN block. The old name appears only in RENAMED FROM.

TABLE users (
    email_address TEXT NOT NULL,
    CONSTRAINT uq_users_email UNIQUE (email_address)   -- must use new name
)
{
    COLUMN email_address {
        RENAMED FROM email;
        COMMENT 'Verified email address';
    }
}
-- emits
ALTER TABLE "public"."users" RENAME COLUMN "email" TO "email_address";
COMMENT ON COLUMN "public"."users"."email_address" IS 'Verified email address';

After the migration, remove RENAMED FROM email from source. See Lifecycle Directives for full rename rules.