Built-in plugins

Every part of pgcraft’s dimension pipeline is a plugin. This page documents each built-in plugin: what it does, what context keys it reads and writes, and how to configure it.

For the plugin architecture itself (dependency declarations, topological sort, singletons, writing custom plugins), see Plugin architecture.

SerialPKPlugin

Adds an auto-incrementing integer primary key column.

Produces: pk_columns

Singleton group: __pk__

Parameters:

column_name

Name for the PK column (default "id").

Example:

from pgcraft.plugins.pk import SerialPKPlugin

# Default: adds "id SERIAL PRIMARY KEY"
SerialPKPlugin()

# Custom column name
SerialPKPlugin(column_name="user_id")

UUIDV4PKPlugin

Adds a UUIDv4 primary key column using PostgreSQL’s gen_random_uuid() as the server default.

Produces: pk_columns

Singleton group: __pk__

Parameters:

column_name

Name for the PK column (default "id").

Example:

from pgcraft.plugins.pk import UUIDV4PKPlugin

# Default: adds "id UUID PRIMARY KEY DEFAULT gen_random_uuid()"
UUIDV4PKPlugin()

# Custom column name
UUIDV4PKPlugin(column_name="user_id")

UUIDV7PKPlugin

Adds a UUIDv7 primary key column using PostgreSQL 18’s uuidv7() as the server default. UUIDv7 values are time-ordered, making them friendlier to B-tree indexes than random UUIDv4 values.

Requires PostgreSQL 18 or later (declared via @requires(MinPGVersion(18))). Use check_pg_version() to validate the server version before applying DDL.

Produces: pk_columns

Requires: MinPGVersion(18)

Singleton group: __pk__

Parameters:

column_name

Name for the PK column (default "id").

Example:

from pgcraft.plugins.pk import UUIDV7PKPlugin

# Default: adds "id UUID PRIMARY KEY DEFAULT uuidv7()"
UUIDV7PKPlugin()

# Custom column name
UUIDV7PKPlugin(column_name="ticket_id")

To validate the server version at runtime:

from pgcraft.plugin import check_pg_version

with engine.connect() as conn:
    major = conn.dialect.server_version_info[0]
    check_pg_version(major, factory.ctx.plugins)

CreatedAtPlugin

Adds a created_at timestamp column with a server-side default of now(). Used by append-only and EAV dimension types.

Produces: created_at_column

Example:

from pgcraft.plugins.created_at import CreatedAtPlugin

CreatedAtPlugin()

SimpleTablePlugin

Creates a single backing table by combining the PK columns and schema items.

Produces: "primary" (via table_key), "__root__"

Requires: "pk_columns"

Singleton group: __table__

Parameters:

table_key

Context key to store the table under (default "primary").

Example:

from sqlalchemy import Column, MetaData, String

from pgcraft.factory import PGCraftSimple

metadata = MetaData()

PGCraftSimple(
    "users", "public", metadata,
    schema_items=[
        Column("name", String, nullable=False),
        Column("email", String),
    ],
)

This creates public.users with columns id, name, email.

PostgRESTView

Note

PostgRESTView lives in the PostgREST extension package (pgcraft.extensions.postgrest). Install the extension and register it on your PGCraftConfig before use.

API views are created separately from the factory using PostgRESTView. This creates a PostgREST-facing view and registers the API resource for role/grant generation.

Note

The PostgREST extension must be registered on your PGCraftConfig for roles and grants to be generated. See the setup snippet in the first example below.

Grants drive triggers: INSTEAD OF triggers are only created for the DML operations listed in grants. A ["select"]-only view has no triggers and is read-only.

Parameters:

source

The factory instance to create the API view for.

schema

Schema for the API view (default "api").

grants

PostgREST privileges (default ["select"]). Determines which INSTEAD OF triggers are created.

query

Optional callable (query, source_table) -> Select for SQLAlchemy-style view customisation (joins, column filtering).

columns

List of column names to include in the view. When None (the default), all table columns are selected. Mutually exclusive with exclude_columns.

exclude_columns

List of column names to hide from the view. Mutually exclusive with columns.

Default behaviour — SELECT *

from pgcraft.config import PGCraftConfig
from pgcraft.extensions.postgrest import (
    PostgRESTExtension,
    PostgRESTView,
)
from pgcraft.factory import PGCraftSimple

config = PGCraftConfig()
config.use(PostgRESTExtension())

users = PGCraftSimple(
    "users", "public", metadata,
    schema_items=[
        Column("name", String, nullable=False),
        Column("email", String),
    ],
    config=config,
)

# Exposes all columns, SELECT only (read-only, no triggers)
PostgRESTView(source=users)

# Full CRUD — creates INSERT, UPDATE, DELETE triggers
PostgRESTView(
    source=users,
    grants=["select", "insert", "update", "delete"],
)

Column selection

Expose a subset of columns — useful when the backing table has internal columns that should not be visible through the API:

PostgRESTView(source=users, columns=["id", "name"])

The generated view:

SELECT p.id, p.name
FROM public.users AS p

Excluding columns

Alternatively, specify which columns to hide — all others are included automatically:

PostgRESTView(
    source=users,
    exclude_columns=["internal_notes"],
)

This is often more convenient than columns when you only want to hide one or two columns from a large table.

Query customisation

Use query= for full SQLAlchemy control — joins, column transforms, or any valid Select:

from pgcraft.views.view import PGCraftView

stats = PGCraftView(
    "order_stats", "public", metadata,
    query=select(
        orders.c.customer_id,
        func.count().label("order_count"),
    ).group_by(orders.c.customer_id),
)

_s = stats.table

PostgRESTView(
    source=customers,
    grants=["select", "insert", "update"],
    query=lambda q, t: (
        select(t.c.id, t.c.name, _s.c.order_count)
        .select_from(t)
        .outerjoin(_s, t.c.id == _s.c.customer_id)
    ),
)

Triggers still operate on the base table’s dimension columns; joined columns are read-only.

StatisticsViewPlugin

Creates statistics views from PGCraftStatisticsView schema items and stores join info for the API view to consume. Included in every dimension factory’s default plugins — a no-op when no statistics items are present.

Produces: "joins" (via joins_key)

Requires: "primary" (via table_key), "pk_columns"

Parameters:

joins_key

Context key to store the view info dict under (default "joins").

table_key

Context key for the source table (default "primary").

How it works

  1. For each PGCraftStatisticsView in ctx.schema_items, compiles the SQLAlchemy query to SQL and creates a view named {tablename}_{name}_statistics.

  2. Stores a dict of JoinedView in ctx[joins_key] for downstream plugins.

  3. The join key column is automatically excluded from the API select list — only the aggregate columns appear.

  4. Each view’s schema defaults to the dimension’s schema but can be overridden per-view.

SimpleTriggerPlugin

Registers INSTEAD OF INSERT / UPDATE / DELETE triggers on the API view, forwarding writes to the backing table. This is an internal plugin used by PostgRESTView — not typically configured directly by users.

Requires: "primary" (via table_key), "api" (via view_key)

Parameters:

columns

Writable column subset. When None, uses all dim columns.

permitted_operations

Which DML operations get triggers ("insert", "update", "delete"). When None, creates all three.

RawTableProtectionPlugin

Prevents direct DML on raw backing tables by installing BEFORE triggers that raise an exception when called outside a trigger context. Included automatically in each factory’s _INTERNAL_PLUGINS.

Requires: the table keys passed to its constructor.

TableCheckPlugin

Resolves PGCraftCheck items into real CHECK constraints on the backing table.

Requires: "__root__"

Example:

from pgcraft.check import PGCraftCheck
from pgcraft.factory import PGCraftSimple

products = PGCraftSimple(
    "products", "public", metadata,
    schema_items=[
        Column("price", Integer),
        PGCraftCheck(
            "{price} > 0", name="positive_price"
        ),
    ],
)

TriggerCheckPlugin

Resolves PGCraftCheck items into trigger- based enforcement (RAISE EXCEPTION in INSTEAD OF triggers). Used with EAV dimensions where table-level checks cannot reference the pivot view.

Parameters:

table_key

Which view’s triggers to add checks to (default varies by dimension type).

TableIndexPlugin

Resolves PGCraftIndex items into real sqlalchemy.Index objects on the backing table. Both simple column references ("{col}") and functional expressions ("lower({col})") are supported. Extra keyword arguments on the PGCraftIndex are passed through to the underlying sqlalchemy.Index (e.g. postgresql_using, postgresql_where).

Requires: "primary" (via table_key)

Parameters:

table_key

Context key for the target table (default "primary"). Append-only dimensions use "attributes".

Example:

from sqlalchemy import Column, Integer, String

from pgcraft.factory import PGCraftSimple
from pgcraft.index import PGCraftIndex

products = PGCraftSimple(
    "products", "public", metadata,
    schema_items=[
        Column("name", String, nullable=False),
        Column("price", Integer, nullable=False),
        PGCraftIndex("idx_products_name", "{name}"),
        PGCraftIndex(
            "idx_products_price",
            "{price}",
            unique=True,
        ),
        PGCraftIndex(
            "idx_products_lower_name",
            "lower({name})",
            postgresql_using="btree",
        ),
    ],
)

TableFKPlugin

Resolves PGCraftFK items into ForeignKeyConstraint objects on the backing table.

Local columns use {column_name} markers. Exactly one of references or raw_references must be provided:

  • references"dimension.column" strings resolved via the dimension registry.

  • raw_references"schema.table.column" strings passed through directly to SQLAlchemy.

The dimension registry is populated automatically when factories run — each factory registers its FK-targetable table (the root table for append-only dimensions, the primary table for simple dimensions).

Requires: "primary" (via table_key)

Parameters:

table_key

Context key for the target table (default "primary"). Append-only dimensions use "attributes".

Example (resolved references):

from sqlalchemy import Column, Integer, String

from pgcraft.factory import PGCraftSimple
from pgcraft.fk import PGCraftFK

customers = PGCraftSimple(
    "customers", "public", metadata,
    schema_items=[
        Column("name", String, nullable=False),
    ],
)

orders = PGCraftSimple(
    "orders", "public", metadata,
    schema_items=[
        Column("customer_id", Integer, nullable=False),
        Column("total", Integer, nullable=False),
        PGCraftFK(
            references={
                "{customer_id}": "customers.id"
            },
            name="fk_orders_customer",
            ondelete="CASCADE",
        ),
    ],
)

"customers.id" is resolved to the physical table via the dimension registry. If customers is append-only, this resolves to the root table automatically.

Example (raw references):

PGCraftFK(
    raw_references={
        "{org_id}": "public.organizations.id"
    },
    name="fk_orders_org",
)

Use raw_references for tables outside pgcraft or when you want full control over the FK target.

See Constraints and indices for a walkthrough of the generated SQL.

AppendOnlyTablePlugin

Creates the root table and attributes table for an append-only (SCD Type 2) dimension.

Produces: "root_table", "attributes"

Requires: "pk_columns"

Singleton group: __table__

AppendOnlyViewPlugin

Creates a join view that presents the current state by joining the root table to the latest attributes row.

Produces: "primary"

Requires: "root_table", "attributes"

AppendOnlyTriggerPlugin

Registers INSTEAD OF triggers that insert new attribute rows on update (preserving history) and handle deletes.

Requires: "root_table", "attributes", "api" (optional)

Example:

from sqlalchemy import Column, ForeignKey, String

from pgcraft.factory import PGCraftAppendOnly

students = PGCraftAppendOnly(
    "students", "private", metadata,
    schema_items=[
        Column("name", String),
        Column(
            "user_id",
            ForeignKey("public.users.id"),
        ),
    ],
)

This creates private.students (root), private.students_log (attributes), and a join view at private.students_current.

To expose via PostgREST:

from pgcraft.extensions.postgrest import (
    PostgRESTView,
)

PostgRESTView(source=students)

EAVTablePlugin

Creates the entity and attribute tables for an EAV dimension. Attributes are stored as typed rows (string_value, integer_value, etc.) with a check constraint enforcing exactly one non-null value per row.

Produces: "entity", "attribute", "eav_mappings"

Requires: "pk_columns"

Singleton group: __table__

EAVViewPlugin

Creates a pivot view that reconstructs the familiar columnar layout from the EAV rows.

Produces: "primary"

Requires: "entity", "attribute", "eav_mappings"

EAVTriggerPlugin

Registers INSTEAD OF triggers that decompose columnar inserts/ updates into individual EAV attribute rows.

Requires: "entity", "attribute", "eav_mappings", "api" (optional)

Example:

from sqlalchemy import Column, Float, Integer, String

from pgcraft.check import PGCraftCheck
from pgcraft.factory import PGCraftEAV

products = PGCraftEAV(
    "products", "private", metadata,
    schema_items=[
        Column("color", String),
        Column("weight", Float),
        Column("price", Integer),
        PGCraftCheck(
            "{price} > 0", name="positive_price"
        ),
    ],
)

This creates private.products_entity, private.products_attribute, and a pivot view. Check constraints are enforced in the INSTEAD OF triggers.

To expose via PostgREST:

from pgcraft.extensions.postgrest import (
    PostgRESTView,
)

PostgRESTView(source=products)

Plugin execution order

The factory topologically sorts plugins by their produces() / requires() declarations. A typical simple dimension pipeline runs:

SerialPKPlugin              -> pk_columns
SimpleTablePlugin           -> primary, __root__
TableCheckPlugin            (reads __root__)
TableIndexPlugin            (reads primary)
TableFKPlugin               (reads primary)
RawTableProtectionPlugin    (reads primary)

API views and triggers are created separately via PostgRESTView, which is part of the optional PostgREST extension (pgcraft.extensions.postgrest). It is not included in the core plugin pipeline.

All context key names are overridable via constructor arguments, so two independent pipelines can coexist in one factory.