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_nameName 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_nameName 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_nameName 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_keyContext 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:
sourceThe factory instance to create the API view for.
schemaSchema for the API view (default
"api").grantsPostgREST privileges (default
["select"]). Determines which INSTEAD OF triggers are created.queryOptional callable
(query, source_table) -> Selectfor SQLAlchemy-style view customisation (joins, column filtering).columnsList of column names to include in the view. When
None(the default), all table columns are selected. Mutually exclusive withexclude_columns.exclude_columnsList 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_keyContext key to store the view info dict under (default
"joins").table_keyContext key for the source table (default
"primary").
How it works¶
For each
PGCraftStatisticsViewinctx.schema_items, compiles the SQLAlchemy query to SQL and creates a view named{tablename}_{name}_statistics.Stores a dict of
JoinedViewinctx[joins_key]for downstream plugins.The join key column is automatically excluded from the API select list — only the aggregate columns appear.
Each view’s
schemadefaults 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:
columnsWritable column subset. When
None, uses all dim columns.permitted_operationsWhich DML operations get triggers (
"insert","update","delete"). WhenNone, 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_keyWhich 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_keyContext 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_keyContext 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.