API reference¶
Plugin system¶
Plugin base class and helpers for pgcraft factory extensions.
- class Dynamic(attr)[source]¶
Reference to an instance attribute that holds the actual ctx key.
Use inside
produces()/requires()decorators when the ctx key name is determined at construction time rather than being a fixed string:@produces(Dynamic("table_key")) @requires("raw") class MyPlugin(Plugin): def __init__(self, table_key: str = "result") -> None: self.table_key = table_key def run(self, ctx: FactoryContext) -> None: ctx[self.table_key] = build(ctx["raw"])
The factory resolves each
Dynamicviagetattr(instance, attr)when building the dependency graph.
- class MinPGVersion(version)[source]¶
Minimum PostgreSQL version requirement for a plugin.
Use inside
requires()to declare that a plugin needs a specific PostgreSQL major version:@requires(MinPGVersion(18)) @produces("pk_columns") class UUIDV7PKPlugin(Plugin): ...
The factory stores the requirement as
min_pg_versionon the class. Callcheck_pg_version()with the connected server’s major version to validate before applying DDL.
- class Plugin[source]¶
Base class for pgcraft factory plugins.
Each plugin implements
runto perform its work. Execution order is determined by topological sort using theproduces()andrequires()class decorators.Declaring dependencies:
@produces(Dynamic("out_key")) @requires("primary") class MyPlugin(Plugin): def __init__(self, out_key: str = "result") -> None: self.out_key = out_key def run(self, ctx: FactoryContext) -> None: ctx[self.out_key] = transform(ctx["primary"])
Plugins communicate through
ctxusing string keys. Use thesingleton()decorator to declare that at most one plugin of a given group may appear in any resolved plugin list.- resolved_produces()[source]¶
Return the ctx keys this plugin writes, with Dynamic refs resolved.
Reads the
_produceslist set by theproduces()decorator and substitutes eachDynamicwithgetattr(self, attr).
- resolved_requires()[source]¶
Return the ctx keys this plugin reads, with Dynamic refs resolved.
Reads the
_requireslist set by therequires()decorator and substitutes eachDynamicwithgetattr(self, attr).
- run(ctx)[source]¶
Execute this plugin’s work against ctx.
The factory calls this once per plugin, after topological sorting by
produces()/requires().- Parameters:
ctx (
FactoryContext) – The factory context.- Return type:
- check_pg_version(server_version, plugins)[source]¶
Raise if any plugin requires a newer PostgreSQL version.
Call this with the server’s major version (e.g.
conn.dialect.server_version_info[0]) to get an early, clear error instead of a cryptic “function does not exist” from PostgreSQL.- Parameters:
- Raises:
PGCraftValidationError – When a plugin’s
min_pg_versionexceeds server_version.- Return type:
- produces(*keys)[source]¶
Declare the ctx keys this plugin’s
runmethod writes.Applied as a class decorator, alongside
requires()andsingleton():@produces(Dynamic("table_key")) class MyTablePlugin(Plugin): ...
- Parameters:
*keys (
str|Dynamic) – Ctx key strings orDynamicreferences to instance attributes that hold the actual key names.- Return type:
Callable[[TypeVar(T, bound=type[Plugin])],TypeVar(T, bound=type[Plugin])]- Returns:
A class decorator that attaches
_producesto the class.- Raises:
TypeError – If a Dynamic attr name is not an
__init__parameter.
- requires(*keys)[source]¶
Declare the ctx keys this plugin’s
runmethod reads.Applied as a class decorator, alongside
produces()andsingleton(). AcceptsMinPGVersionsentinels to declare a minimum PostgreSQL version requirement:@requires(MinPGVersion(18), "pk_columns") class MyPlugin(Plugin): ...
- Parameters:
*keys (
str|Dynamic|MinPGVersion) – Ctx key strings,Dynamicreferences, orMinPGVersionversion requirements.- Return type:
Callable[[TypeVar(T, bound=type[Plugin])],TypeVar(T, bound=type[Plugin])]- Returns:
A class decorator that attaches
_requiresto the class and setsmin_pg_versionif anyMinPGVersionsentinel is present.- Raises:
TypeError – If a Dynamic attr name is not an
__init__parameter.
- singleton(group)[source]¶
Declare that at most one plugin of group may appear.
The factory raises
PGCraftValidationErrorat construction time if two plugins with the same group name are present in the resolved plugin list.Example:
@singleton("__pk__") class MyPKPlugin(Plugin): ...
- Parameters:
group (
str) – Arbitrary group identifier. By convention, built-in groups use dunder names ("__pk__","__table__").- Return type:
Callable[[TypeVar(T, bound=type[Plugin])],TypeVar(T, bound=type[Plugin])]- Returns:
A class decorator that sets
singleton_groupon the class and registers the singleton validator.
Core ResourceFactory: plugin runner.
- class ResourceFactory(tablename, schemaname, metadata, schema_items, *, config=None, plugins=None, extra_plugins=None)[source]¶
Core factory: resolves plugins and runs them in dependency order.
Subclasses declare
DEFAULT_PLUGINSfor user-facing defaults and_INTERNAL_PLUGINSfor always-present built-in logic. Callers can override or extend the plugin list viaplugins/extra_plugins, and inject global plugins viaconfig.Plugin execution order is determined by each plugin’s
producesandrequiresdeclarations. Plugins with no declared dependencies run in the order they appear in the list.Resolution order:
global_plugins + user_plugins + internal_plugins, then topological sort.If no user or global plugin produces
pk_columnsand internal plugins are present, aSerialPKPluginis auto-prepended.- Parameters:
tablename (
str) – Name of the dimension table.schemaname (
str) – PostgreSQL schema for all generated objects.metadata (
MetaData) – SQLAlchemyMetaDatathe objects are bound to.schema_items (
list[SchemaItem|PGCraftCheck|PGCraftFK|PGCraftIndex|PGCraftStatisticsView]) – Column and constraint definitions. Must not include a primary key column.config (
object|None) – Optional global config supplying prepended plugins.plugins (
list[Plugin] |None) – If given, replacesDEFAULT_PLUGINSentirely.extra_plugins (
list[Plugin] |None) – Appended to the resolved plugin list.
- Raises:
PGCraftValidationError – If any schema item fails validation, two plugins share a singleton group, two plugins produce the same ctx key, or a plugin dependency cycle is detected.
- ctx: FactoryContext¶
The factory context after plugin execution.
Downstream view factories (e.g.
PostgRESTView) read this to access tables, columns, and other plugin outputs.
- table: FromClause¶
The root selectable created by the factory.
This is the
__root__context value set by the table plugin, exposing the column metadata for use in queries, foreign key references, and ledger event lambdas.
Factory context dataclass.
- class FactoryContext(tablename, schemaname, metadata, schema_items, plugins)[source]¶
Carries inputs and accumulates plugin outputs.
Typed input fields (set by the factory, read-only for plugins):
tablename,schemaname,metadata,schema_items,plugins– as passed to the factory constructor.
Plugin store (read/write via item syntax):
Plugins communicate by storing and retrieving arbitrary values using string keys. The key names a plugin reads and writes are explicit constructor arguments on that plugin (with sensible defaults), so multiple independent pipelines can coexist by using distinct keys.
ctx["key"] = valueStore a value. Raises
KeyErrorif key is already set – two plugins writing the same key is almost certainly a mistake. Usectx.set("key", value, force=True)to override intentionally.ctx["key"]Retrieve a value. Raises
KeyErrorwith a plugin-ordering hint when the key is absent."key" in ctxTest whether a key has been set without raising.
Injected columns (append-only list):
Plugins that provide columns for table construction (e.g.
CreatedAtPlugin,UUIDEntryIDPlugin,DoubleEntryPlugin) appendColumnobjects toctx.injected_columns. Table plugins spread this list into the table definition alongside PK and dimension columns.- property columns: list[Column]¶
Return only
Columninstances from schema_items.Useful when a plugin needs to iterate over column definitions (e.g. to extract column names or types).
- property dim_column_names: list[str]¶
Return writable (non-PK, non-computed) column names.
Filters out primary-key and computed columns from
schema_items, leaving only the user-defined dimension columns that a plugin should read or write. Equivalent to the_dim_column_nameshelper that was previously duplicated across multiple plugin modules.
- property pk_column_name: str¶
Return the primary key column name.
Shorthand for
ctx["pk_columns"].first_key. Requires a PK plugin (e.g.SerialPKPlugin) to have run first.- Raises:
KeyError – If
pk_columnshas not been set yet.
- set(key, value, *, force=False)[source]¶
Store value under key, with optional override.
- Parameters:
- Raises:
KeyError – If key is already set and
forceisFalse.- Return type:
- property table_items: list[SchemaItem]¶
Return schema items suitable for table creation.
Filters out
PGCraftCheck(which are handled by dedicated check plugins) but keeps all real SQLAlchemySchemaItemobjects: columns, constraints, indexes, computed columns, etc.
Global pgcraft configuration.
- class PGCraftConfig(plugins=<factory>, extensions=<factory>, auto_discover=True, utility_schema='pgcraft')[source]¶
Global plugin and extension registry.
Plugins registered here are prepended to every factory’s resolved plugin list, so they run before factory-specific plugins.
Extensions bundle plugins, metadata hooks, Alembic hooks, and CLI commands into a single unit.
Example:
from pgcraft.extensions.postgrest import ( PostgRESTExtension, ) config = PGCraftConfig() config.use(PostgRESTExtension()) config.register(TimestampPlugin(), TenantPlugin()) PGCraftSimple( "users", "public", metadata, ..., config=config )
- Parameters:
plugins (
list[Plugin]) – Global plugins prepended to every factory.extensions (
list[PGCraftExtension]) – Manually registered extension instances.auto_discover (
bool) – Whether to discover extensions via entry points. Defaults toTrue.utility_schema (
str) – PostgreSQL schema for pgcraft-managed utility functions (e.g.ledger_apply_state). Defaults to"pgcraft". Override only if your project already uses a schema named"pgcraft".
- property all_plugins: list[Plugin]¶
Return extension plugins + direct plugins.
Extension plugins are prepended before direct plugins.
- Returns:
Combined plugin list.
- register(*plugins)[source]¶
Register one or more plugins globally.
- Parameters:
*plugins (
Plugin) – Plugin instances to add.- Return type:
- Returns:
selffor chaining.
Check constraint support for pgcraft dimensions.
Provides PGCraftCheck, a declarative check constraint that uses
{column_name} markers in its expression. Plugins resolve these
markers to the appropriate column references depending on the
dimension type (table-level for simple/append-only, NEW.col for
EAV trigger-based enforcement).
- class PGCraftCheck(expression, name)[source]¶
A declarative check constraint with
{col}markers.- Parameters:
- collect_checks(schema_items)[source]¶
Filter
PGCraftCheckinstances from a schema items list.- Parameters:
schema_items (
list) – Mixed list ofColumn,PGCraftCheck, and other schema items.- Return type:
- Returns:
Only the
PGCraftCheckitems, in their original order.
Index support for pgcraft dimensions.
Provides PGCraftIndex, a declarative index definition
that mirrors sqlalchemy.Index and uses {column_name}
markers for column references.
- class PGCraftIndex(name, *expressions, unique=False, **kw)[source]¶
A declarative index definition with
{col}markers.Mirrors the
sqlalchemy.Indexconstructor signature:PGCraftIndex("idx_name", "{col1}", "{col2}", unique=True, postgresql_using="btree")
Simple column references (
"{name}") and functional expressions ("lower({name})") are both supported. Extra keyword arguments are passed through to the underlyingsqlalchemy.Index.- Parameters:
- collect_indices(schema_items)[source]¶
Filter
PGCraftIndexinstances from schema items.- Parameters:
schema_items (
list) – Mixed list of schema items.- Return type:
- Returns:
Only the
PGCraftIndexitems, in original order.
Foreign key support for pgcraft dimensions.
Provides PGCraftFK, a declarative foreign key constraint
definition that pairs {column_name} markers with their target
references in a single dict.
Two reference modes are supported:
references— a dict mapping{local_col}markers to"dimension.column"strings, resolved at factory time via the dimension registry inmetadata.info["pgcraft_dimensions"].raw_references— a dict mapping{local_col}markers to"schema.table.column"strings, passed through to SQLAlchemy directly.
Exactly one of the two must be provided.
- class DimensionRef(schema, table)[source]¶
Registry entry for a dimension’s FK-targetable table.
Stored in
metadata.info["pgcraft_dimensions"]keyed by dimension name (tablename).
- class PGCraftFK(references=<factory>, raw_references=<factory>, name='', ondelete=None, onupdate=None)[source]¶
A declarative foreign key constraint.
Each entry in the dict maps a local
{column}marker to its target reference. Exactly one ofreferencesorraw_referencesmust be provided:references— targets use"dimension.column"format, resolved via the dimension registry:PGCraftFK( references={"{customer_id}": "customers.id"}, name="fk_orders_customer", )
raw_references— targets use"schema.table.column"format, passed through directly:PGCraftFK( raw_references={"{org_id}": "public.orgs.id"}, name="fk_orders_org", )
- Parameters:
- Raises:
PGCraftValidationError – If both or neither of
referencesandraw_referencesare provided.
- register_dimension(metadata, name, ref)[source]¶
Register a dimension for FK resolution.
- Parameters:
metadata (
MetaData) – SQLAlchemy MetaData instance.name (
str) – Dimension name (tablename).ref (
DimensionRef) – The dimension’s FK target info.
- Return type:
- resolve_fk_reference(metadata, reference)[source]¶
Resolve a
"dimension.column"reference.Looks up the dimension name in the registry and expands it to
"schema.table.column".- Parameters:
- Return type:
- Returns:
Fully qualified
"schema.table.column"string.- Raises:
PGCraftValidationError – If the reference does not contain exactly one dot, or names an unknown dimension.
Shared column-reference validation and marker helpers.
All pgcraft constraint types (PGCraftCheck,
PGCraftIndex, PGCraftFK)
use {column_name} markers. This module provides the regex, extraction,
resolution, and validation helpers they share.
- COLUMN_MARKER_RE = re.compile('\\{(\\w+)\\}')¶
Regex matching
{column_name}markers in expressions.
- validate_column_references(label, columns, known_columns)[source]¶
Raise if any column is not in known_columns.
Shared by check, index, and foreign-key plugins to ensure that user-provided column names actually exist on the target table or view.
Statistics view support for pgcraft dimensions.
Provides PGCraftStatisticsView, a declarative statistics
view definition that can be joined into the API view as read-only
fields.
- class JoinedView(view_name, join_key, column_names)[source]¶
A view to LEFT JOIN into the API view.
Produced by plugins (e.g.
StatisticsViewPlugin) and consumed byPostgRESTPluginwhen building the API view SQL.
- class PGCraftStatisticsView(name, query, materialized=False, join_key=None, schema=None)[source]¶
A declarative statistics view definition.
The
queryis a SQLAlchemySelectwhose selected columns define both the view body and the column names exposed through the API. The join key column (used to join back to the primary table) should be included in the query but is excluded from the API select list automatically.The view is named
{tablename}_{name}_statistics— pass just the source name (e.g."orders"), not the full suffix.- Parameters:
name (
str) – Source name for the statistics view. The view will be named{tablename}_{name}_statistics(e.g."orders"→"customer_orders_statistics").query (
Select) – A SQLAlchemyselect()expression.materialized (
bool) – Whether to create a materialized view.join_key (
str|None) – Column name used to join back to the primary table. Defaults to the PK column name at runtime.schema (
str|None) – PostgreSQL schema for the view. Defaults to the dimension’s schema at runtime.
Example:
from sqlalchemy import func, select orders = Table("orders", metadata, ...) PGCraftStatisticsView( name="orders", query=select( orders.c.customer_id, func.count().label("order_count"), ).group_by(orders.c.customer_id), join_key="customer_id", )
- collect_statistics(schema_items)[source]¶
Filter
PGCraftStatisticsViewfrom a schema items list.- Parameters:
schema_items (
list) – Mixed list ofColumn,PGCraftStatisticsView, and other schema items.- Return type:
- Returns:
Only the
PGCraftStatisticsViewitems, in original order.
Cave exception types.
Schema item validators for dimension factories.
- is_schema_item_not_primary_key(item)[source]¶
Return True if the item is not a primary key column.
- Parameters:
item (
SchemaItem|PGCraftCheck|PGCraftFK|PGCraftIndex) – The schema item to inspect.- Return type:
- Returns:
Trueif item is not a primary key column.
- validate_schema_items(items, *, validators=None)[source]¶
Validate a list of SchemaItems against the given validators.
- Parameters:
- Raises:
PGCraftValidationError – If any item fails a validator.
- Return type:
Decorator for applying pgcraft plugins to plain classes.
Table creation is fully deferred to pgcraft’s plugin pipeline — the
decorated class does NOT inherit from a SQLAlchemy declarative base
and no __table__ is created until plugins run.
After decoration the class is imperatively mapped so that
select(User) and other ORM operations work normally.
- register(*, base=None, metadata=None, schema=None, config=None, plugins=None, extra_plugins=None, api=None)[source]¶
Apply pgcraft plugins to a plain class and optionally ORM-map.
The class declares columns as plain
Columnattributes. pgcraft’s plugin pipeline handles table creation, PK generation, and any other registered work. After plugins run the class is imperatively mapped (when base is provided) so thatselect(cls)works.Usage:
@register( base=Base, api={"grants": ["select", "insert", "update"]}, ) class User: __tablename__ = "users" __table_args__ = {"schema": "public"} name = Column(String) email = Column(String, nullable=True)
After decoration:
User.__table__is the pgcraft-created table.select(User)works (when base is given).An API view with the specified grants is registered.
- Parameters:
base (
type[DeclarativeBase] |None) – A SQLAlchemyDeclarativeBasesubclass. When provided, the class is imperatively mapped viabase.registryandbase.metadatais used as the target metadata.metadata (
MetaData|None) – ExplicitMetaDatainstance. Required when base is not given. Ignored when base is provided.schema (
str|None) – PostgreSQL schema name. Overrides__table_args__["schema"]when given.config (
object|None) – OptionalPGCraftConfigproviding global plugins.plugins (
list[Plugin] |None) – Behaviour-modifying plugins (e.g.UUIDV4PKPlugin).extra_plugins (
list[Plugin] |None) – Appended to the resolved plugin list.api (
dict[str,Any] |None) – When provided, creates aPostgRESTViewafter table creation. Accepts keyword arguments forwarded toPostgRESTView(e.g.{"grants": ["select"]}).
- Return type:
- Returns:
A class decorator.
- Raises:
PGCraftValidationError – If required attributes are missing or plugin validation fails.
Dimension resource factories¶
Simple dimension resource factory.
- class PGCraftSimple(tablename, schemaname, metadata, schema_items, *, config=None, plugins=None, extra_plugins=None)[source]¶
Create a simple dimension: one table with optional checks.
Internal plugins (always present):
SimpleTablePlugin– backing table.TableCheckPlugin– materializesPGCraftCheckitems.TableIndexPlugin– materializesPGCraftIndexitems.TableFKPlugin– materializesPGCraftFKitems.
A
SerialPKPluginis auto-added when no user plugin producespk_columns.Use
PostgRESTViewto expose this table through a PostgREST API view with CRUD triggers.- Parameters:
tablename (
str) – Name of the dimension table.schemaname (
str) – PostgreSQL schema for generated objects.metadata (
MetaData) – SQLAlchemyMetaDatato register on.schema_items (
list[SchemaItem|PGCraftCheck|PGCraftFK|PGCraftIndex|PGCraftStatisticsView]) – Column and constraint definitions.plugins (
list[Plugin] |None) – Behaviour-modifying plugins (e.g.UUIDV4PKPlugin).extra_plugins (
list[Plugin] |None) – Appended to the resolved plugin list.
- TRIGGER_PLUGIN_CLS¶
alias of
SimpleTriggerPlugin
Append-only dimension resource factory.
- class PGCraftAppendOnly(tablename, schemaname, metadata, schema_items, *, config=None, plugins=None, extra_plugins=None)[source]¶
Create an append-only (SCD Type 2) dimension.
Internal plugins (always present):
CreatedAtPlugin–created_atcolumn name.AppendOnlyTablePlugin– root + attributes tables.AppendOnlyViewPlugin– join view proxy.TableCheckPlugin– materializesPGCraftCheckitems.TableIndexPlugin– materializesPGCraftIndexitems.TableFKPlugin– materializesPGCraftFKitems.
A
SerialPKPluginis auto-added when no user plugin producespk_columns.Use
PostgRESTViewto expose this table through a PostgREST API view with CRUD triggers.- TRIGGER_PLUGIN_CLS¶
alias of
AppendOnlyTriggerPlugin
EAV dimension resource factory.
- class PGCraftEAV(tablename, schemaname, metadata, schema_items, *, config=None, plugins=None, extra_plugins=None)[source]¶
Create an EAV (Entity-Attribute-Value) dimension.
Internal plugins (always present):
CreatedAtPlugin–created_atcolumn name.EAVTablePlugin– entity + attribute tables.EAVViewPlugin– pivot view proxy.TriggerCheckPlugin– trigger-based checks on the pivot view.
A
SerialPKPluginis auto-added when no user plugin producespk_columns.Use
PostgRESTViewto expose this table through a PostgREST API view with CRUD triggers.- TRIGGER_PLUGIN_CLS¶
alias of
EAVTriggerPlugin
Ledger resource factory¶
Ledger resource factory.
- class PGCraftLedger(tablename, schemaname, metadata, schema_items, *, events=None, config=None, plugins=None, extra_plugins=None)[source]¶
Create a ledger: append-only table with a value column.
Internal plugins (always present):
UUIDEntryIDPlugin– UUID entry ID for correlating related entries.CreatedAtPlugin–created_atcolumn name.LedgerTablePlugin– backing table with value column.
A
SerialPKPluginis auto-added when no user plugin producespk_columns.Use
PostgRESTViewto expose this table through a PostgREST API view with INSERT triggers. UseBalanceView,LatestView, andLedgerActionsfor derived views and event functions.- Parameters:
events (
list|None) – Deprecated. UseLedgerActionsinstead.
- TRIGGER_PLUGIN_CLS¶
alias of
LedgerTriggerPlugin
View factories¶
PostgREST extension for pgcraft.
- class PostgRESTExtension(name='postgrest', schema='api')[source]¶
Wire PostgREST roles and grants into the pgcraft lifecycle.
When registered on a
PGCraftConfig, this extension callsregister_roles()during metadata configuration so that PostgREST roles and per-resource grants are emitted by Alembic autogenerate.Without this extension, no roles or grants are registered.
Example:
from pgcraft.config import PGCraftConfig from pgcraft.extensions.postgrest import ( PostgRESTExtension, PostgRESTView, ) config = PGCraftConfig() config.use(PostgRESTExtension())
- Parameters:
- class PostgRESTPlugin(schema='api', grants=None, table_key='primary', view_key='api', columns=None, exclude_columns=None, joins_key='joins')[source]¶
Create a PostgREST-facing view and register its grants.
Reads
ctx[table_key]to build a view query, registers the resulting view, stores it asctx[view_key], and registers the API resource for role/grant generation.- Parameters:
schema (
str) – Schema for the API view (default"api").grants (
list[Literal['select','insert','update','delete']] |None) – PostgREST privileges (default["select"]).table_key (
str) – Key inctxto read the source table or view proxy from (default"primary").view_key (
str) – Key inctxto store the created view under (default"api").columns (
list[str] |None) – Column names to include in the view. Mutually exclusive withexclude_columns.exclude_columns (
list[str] |None) – Column names to exclude from the view. Mutually exclusive withcolumns.joins_key (
str) – Key inctxholding a dict ofJoinedViewentries. LEFT JOINs each view into the API view when the key exists and is non-empty.
- class PostgRESTView(source, schema='api', grants=None, query=None, *, columns=None, exclude_columns=None)[source]¶
Create a PostgREST-facing view with auto-selected triggers.
Reads from a table factory’s context to create an API view and INSTEAD OF triggers. The trigger strategy is auto-selected based on the factory type (
source.TRIGGER_PLUGIN_CLS).Grants drive triggers: only operations listed in grants get INSTEAD OF triggers.
"select"-only views have no triggers and are read-only.- Parameters:
source (
ResourceFactory) – AResourceFactoryinstance whose table/context to expose.schema (
str) – Schema for the API view (default"api").grants (
list[Literal['select','insert','update','delete']] |None) – PostgREST privileges (default["select"]). Determines which INSTEAD OF triggers are created.query (
Callable[[Select,Table],Select] |None) – Optional callable(query, source_table) -> Selectfor SQLAlchemy-style view customization (joins, column filtering, etc.).columns (
list[str] |None) – Column names to include. Mutually exclusive withexclude_columns.exclude_columns (
list[str] |None) – Column names to exclude. Mutually exclusive withcolumns.
Generic view factories.
- class PGCraftMaterializedView(name, schema, metadata, query)[source]¶
Create a materialized view with an auto-generated refresh.
After construction,
self.tableis a joinable SQLAlchemyTablewhose columns mirror the query.
- class PGCraftView(name, schema, metadata, query)[source]¶
Create a plain PostgreSQL view from a SQLAlchemy select.
After construction,
self.tableis a joinable SQLAlchemyTablewhose columns mirror the query.
BalanceView: ledger balance aggregation view.
- class BalanceView(source, dimensions)[source]¶
Create a view showing current balances per dimension group.
Generates
SELECT dim_cols, SUM(value) AS balance FROM ledger GROUP BY dim_cols.- Parameters:
source (
PGCraftLedger) – APGCraftLedgerinstance.dimensions (
list[str]) – Column names to group by. Must be a non-empty list.
- Raises:
PGCraftValidationError – If dimensions is empty.
LatestView: latest ledger entry per dimension group.
- class LatestView(source, dimensions)[source]¶
Create a view showing the most recent row per dimension.
Uses
DISTINCT ONordered bycreated_at DESC.- Parameters:
source (
PGCraftLedger) – APGCraftLedgerinstance.dimensions (
list[str]) – Column names to partition by. Must be a non-empty list.
- Raises:
PGCraftValidationError – If dimensions is empty.
LedgerActions: generate PostgreSQL functions for ledger events.
- class LedgerActions(source, events)[source]¶
Generate PostgreSQL functions for ledger events.
Each
LedgerEventbecomes a PostgreSQL function that inserts rows into the ledger through the API view.- Parameters:
source (
PGCraftLedger) – APGCraftLedgerinstance.events (
list[LedgerEvent]) – List ofLedgerEventinstances to generate functions for.
Ledger events¶
LedgerEvent configuration and helpers.
A LedgerEvent declares a named operation on a ledger. The
user provides lambdas that produce SQLAlchemy selects; the plugin
compiles them into a single PostgreSQL function per event.
Two modes are supported:
Simple mode (
inputonly): the input select is inserted directly into the ledger via the API view.Diff mode (
input+desired+existing): the desired state is diffed against the existing state and only the correcting deltas are inserted.
- class LedgerEvent(name, input, desired=None, existing=None, diff_keys=<factory>)[source]¶
Declare a named ledger operation.
Each event compiles into a single PostgreSQL function that inserts rows into the ledger API view and returns the inserted rows via
RETURNING *.Simple mode — provide only
input. The input select’s columns are inserted directly.Diff mode — provide
input,desired,existing, anddiff_keys. The desired and existing selects are unioned and only non-zero deltas are inserted.- Parameters:
name (
str) – Unique event name within the ledger.input (
Callable[[ParamCollector],Select]) – Lambda(p) -> Selectthat builds the input CTE.pis aParamCollector.desired (
Callable[[FromClause],Select] |None) – Lambda(pginput) -> Selectthat builds the desired-state CTE from the input CTE reference.existing (
Callable[[Table,FromClause],Select] |None) – Lambda(table, desired) -> Selectthat builds the existing-state CTE. Useledger_balances()for the common pattern.diff_keys (
list[str]) – Column names used for grouping in diff mode. Required whendesiredis set.
- class ParamCollector[source]¶
Collect SQL function parameters during lambda evaluation.
Usage inside an
inputlambda:lambda p: select( p("warehouse", String).label("warehouse"), p("sku", String).label("sku"), )
Each call to
p(name, sa_type)records the parameter and returns aliteral_column()reference (p_name) suitable for embedding in a select.- Parameters:
None.
- ledger_balances(*keys)[source]¶
Return an
existingcallable for common balance lookup.Produces a select that negates the current balances for each diff-key group present in the desired CTE:
SELECT key1, key2, SUM(value) * -1 AS value FROM ledger_table WHERE (key1, key2) IN (SELECT key1, key2 FROM desired) GROUP BY key1, key2
- Parameters:
*keys (
str) – Dimension column names to group by.- Return type:
Callable[[Table,FromClause],Select]- Returns:
A callable suitable for
LedgerEvent(existing=...).
Plugin that generates PostgreSQL functions for ledger events.
- class LedgerActionsPlugin(events, view_key='api')[source]¶
Generate PostgreSQL functions for ledger events.
Processes each
LedgerEventand registers a PostgreSQL function onctx.metadata.Must run after
LedgerTablePlugin(needs"__root__") andPostgRESTPlugin(needs the API view key).- Parameters:
events (
list[LedgerEvent]) – List ofLedgerEventinstances to process.view_key (
str) – Context key for the API view (default"api").
- Raises:
PGCraftValidationError – At run time if any event fails validation against the ledger schema.
- run(ctx)[source]¶
Validate events and generate SQL functions.
- Parameters:
ctx (
FactoryContext) – The factory context.- Return type:
Built-in plugins¶
Primary key plugins.
- class SerialPKPlugin(column_name='id')[source]¶
Provide an auto-increment integer primary key column.
- Parameters:
column_name (
str) – Name of the PK column (default"id").
- class UUIDV4PKPlugin(column_name='id')[source]¶
Provide a UUIDv4 primary key column.
Uses PostgreSQL’s
gen_random_uuid()as the server default so rows get a unique identifier without client-side generation.- Parameters:
column_name (
str) – Name of the PK column (default"id").
- class UUIDV7PKPlugin(column_name='id')[source]¶
Provide a UUIDv7 primary key column.
Uses PostgreSQL 18’s
uuidv7()as the server default to generate time-ordered UUIDs. These sort chronologically, making them friendlier to B-tree indexes than random UUIDv4 values.Requires PostgreSQL 18 or later (declared via
@requires(MinPGVersion(18))). Usecheck_pg_version()to validate the server version before applying DDL.- Parameters:
column_name (
str) – Name of the PK column (default"id").
Plugin that provides a created_at timestamp column name.
- class CreatedAtPlugin(column_name='created_at')[source]¶
Provide the
created_atcolumn name for table plugins.Stores the column name as a string in
ctx["created_at_column"]. Each table plugin that needs acreated_atcolumn is responsible for constructing it from this name (seeAppendOnlyTablePlugin,EAVTablePlugin,LedgerTablePlugin).- Parameters:
column_name (
str) – Name of the timestamp column (default"created_at").
Entry ID plugin for correlating related ledger entries.
- class UUIDEntryIDPlugin(column_name='entry_id')[source]¶
Provide a UUIDv4 entry ID column for ledger tables.
Stores a
Columninctx["entry_id_column"]that downstream table plugins (e.g.LedgerTablePlugin) splice into the table definition. The column uses PostgreSQL’sgen_random_uuid()as a server default so callers can omit it for single-entry inserts while still providing an explicit value to correlate multi-row entries.- Parameters:
column_name (
str) – Name of the entry ID column (default"entry_id").
Plugins for simple (single-table) dimensions.
- class SimpleTablePlugin(table_key='primary')[source]¶
Create a single backing table for a simple dimension.
Combines
ctx["pk_columns"]andctx.schema_itemsinto one table.- Parameters:
table_key (
str) – Key under which the created table is stored inctx(default"primary").
- class SimpleTriggerPlugin(table_key='primary', view_key='api', columns=None, permitted_operations=None)[source]¶
Register INSERT/UPDATE/DELETE INSTEAD OF triggers on a view.
- Parameters:
Plugins for append-only (SCD Type 2) dimensions.
- class AppendOnlyTablePlugin(root_key='root_table', attributes_key='attributes')[source]¶
Create the root and attributes tables for an append-only dim.
- Parameters:
- class AppendOnlyTriggerPlugin(root_key='root_table', attributes_key='attributes', view_key='api', columns=None)[source]¶
Register INSTEAD OF triggers for an append-only dimension.
Registers identical trigger logic on the private join view and, if present, the view stored at
view_key.- Parameters:
root_key (
str) – Key inctxfor the entity root table (default"root_table").attributes_key (
str) – Key inctxfor the attributes log (default"attributes").view_key (
str) – Key inctxfor the additional trigger target, e.g. the API view (default"api"). If the key is absent fromctxno second trigger is registered.
- class AppendOnlyViewPlugin(root_key='root_table', attributes_key='attributes', primary_key='primary')[source]¶
Create the join view for an append-only dimension.
- Parameters:
root_key (
str) – Key inctxfor the entity root table (default"root_table").attributes_key (
str) – Key inctxfor the attributes log (default"attributes").primary_key (
str) – Key inctxto store the view proxy under, for downstream plugins such asPostgRESTPlugin(default"primary").
Check constraint plugins for pgcraft dimensions.
TableCheckPlugin converts PGCraftCheck
items into real SQLAlchemy CheckConstraint objects on a table
(for simple and append-only dimensions).
TriggerCheckPlugin generates INSTEAD OF trigger functions
that enforce checks before the main dimension triggers (for EAV
dimensions where columns are virtual).
- class TableCheckPlugin(table_key='primary')[source]¶
Materialize
PGCraftCheckas table constraints.Reads
PGCraftCheckitems fromctx.schema_items, resolves{col}markers to plain column names (identity), and appends realCheckConstraintobjects to the target table.- Parameters:
table_key (
str) – Key inctxfor the target table (default"primary").
- class TriggerCheckPlugin(table_key='primary')[source]¶
Enforce checks via INSTEAD OF triggers (EAV dimensions).
Generates a single trigger function per view per operation (INSERT/UPDATE) that validates all
PGCraftCheckitems. Triggers use a_check_prefix to fire before the main dimension triggers (Postgres fires multiple INSTEAD OF triggers in alphabetical order by name).- Parameters:
table_key (
str) – Key inctxfor the trigger target view (default"primary").
Index plugin for pgcraft dimensions.
TableIndexPlugin converts PGCraftIndex
items into real SQLAlchemy Index objects on a table.
- class TableIndexPlugin(table_key='primary')[source]¶
Materialize
PGCraftIndexas table indexes.Reads
PGCraftIndexitems fromctx.schema_items, validates column names, and createsIndexobjects on the target table. Extra keyword arguments on eachPGCraftIndexare passed through to the underlyingsqlalchemy.Index.- Parameters:
table_key (
str) – Key inctxfor the target table (default"primary").
Foreign key plugin for pgcraft dimensions.
TableFKPlugin converts PGCraftFK
items into real SQLAlchemy ForeignKeyConstraint objects on a
table. Two-part references ("dimension.column") are resolved
via the dimension registry in metadata.info.
- class TableFKPlugin(table_key='primary')[source]¶
Materialize
PGCraftFKas FK constraints.Reads
PGCraftFKitems fromctx.schema_items, validates local column names, resolves references via the dimension registry, and createsForeignKeyConstraintobjects on the target table.- Parameters:
table_key (
str) – Key inctxfor the target table (default"primary").
Plugins for EAV (Entity-Attribute-Value) dimensions.
- class EAVTablePlugin(entity_key='entity', attribute_key='attribute', mappings_key='eav_mappings')[source]¶
Create entity and attribute tables for an EAV dimension.
- Parameters:
- class EAVTriggerPlugin(entity_key='entity', attribute_key='attribute', mappings_key='eav_mappings', view_key='api')[source]¶
Register INSTEAD OF triggers for an EAV dimension.
Registers on the private pivot view and, if present, the view at
view_key.- Parameters:
entity_key (
str) – Key inctxfor the entity root table (default"entity").attribute_key (
str) – Key inctxfor the attribute log (default"attribute").mappings_key (
str) – Key inctxfor the EAV mappings list (default"eav_mappings").view_key (
str) – Key inctxfor the additional trigger target (default"api"). Skipped if absent.
- class EAVViewPlugin(entity_key='entity', attribute_key='attribute', mappings_key='eav_mappings', primary_key='primary')[source]¶
Create the pivot view for an EAV dimension.
- Parameters:
entity_key (
str) – Key inctxfor the entity root table (default"entity").attribute_key (
str) – Key inctxfor the attribute log (default"attribute").mappings_key (
str) – Key inctxfor the EAV mappings list (default"eav_mappings").primary_key (
str) – Key inctxto store the view proxy under (default"primary").
Plugins for ledger (append-only value) tables.
- class DoubleEntryPlugin(column_name='direction')[source]¶
Add debit/credit semantics to a ledger table.
Adds a
directioncolumn ('debit'or'credit') to the schema items so thatLedgerTablePluginincludes it in the table. Also registers an AFTER INSERT constraint trigger that validates all rows sharing anentry_idhave equal total debits and credits.This plugin must appear before
LedgerTablePluginin the plugin list so its column is included in the table definition.- Parameters:
column_name (
str) – Name of the direction column (default"direction").
- class DoubleEntryTriggerPlugin(table_key='primary')[source]¶
Register an AFTER INSERT trigger enforcing balanced entries.
Validates that for every
entry_idin the inserted batch, the sum of debit values equals the sum of credit values. Raises a PostgreSQL exception if any entry is unbalanced.Uses a statement-level trigger with a
REFERENCING NEW TABLEtransition table so that multi-row inserts are checked as a whole, not row-by-row.Must run after
LedgerTablePlugin(needs the table) and afterDoubleEntryPlugin(needs column name).- Parameters:
table_key (
str) – Key inctxfor the backing table (default"primary").
- class LedgerBalanceCheckPlugin(dimensions, min_balance=0, table_key='primary')[source]¶
Enforce a minimum balance per dimension group.
Registers an
AFTER INSERT FOR EACH STATEMENTtrigger that checksSUM(value) >= min_balancefor every dimension group affected by the inserted rows. If any group violates the constraint the entire statement is rejected.Uses the same
REFERENCING NEW TABLEtransition-table pattern asDoubleEntryTriggerPlugin.- Parameters:
- Raises:
PGCraftValidationError – If dimensions is empty.
- class LedgerBalanceViewPlugin(dimensions, table_key='primary', balance_view_key='balance_view')[source]¶
Create a view that shows current balances per dimension group.
Generates
SELECT dim_cols, SUM(value) AS balance FROM ledger GROUP BY dim_colsand registers it as a view.- Parameters:
- Raises:
PGCraftValidationError – If dimensions is empty.
- class LedgerLatestViewPlugin(dimensions, table_key='primary', latest_view_key='latest_view')[source]¶
Create a view showing the most recent row per dimension group.
Uses PostgreSQL
DISTINCT ONto select the latest row (bycreated_at) for each unique combination of dimension values. Useful for status-tracking ledgers where you care about current state, not historical sums.- Parameters:
- Raises:
PGCraftValidationError – If dimensions is empty.
- class LedgerTablePlugin(value_type='integer', table_key='primary')[source]¶
Create a ledger table with a value column.
Combines
ctx["pk_columns"],ctx.injected_columns(provided by upstream plugins likeUUIDEntryIDPlugin,CreatedAtPlugin, andDoubleEntryPlugin), avaluecolumn, andctx.table_items(dimension columns) into a single append-only table.- Parameters:
- Raises:
PGCraftValidationError – If value_type is not a recognised type.
- class LedgerTriggerPlugin(table_key='primary', view_key='api')[source]¶
Register an INSERT INSTEAD OF trigger on a ledger view.
Only INSERT is supported – ledger entries are immutable. UPDATE and DELETE on the API view will raise a PostgreSQL error naturally (no INSTEAD OF trigger defined).
- Parameters:
Statistics view plugin: creates views from PGCraftStatisticsView.
- class StatisticsViewPlugin(joins_key='joins', table_key='primary')[source]¶
Create statistics views from PGCraftStatisticsView items.
For each
PGCraftStatisticsViewinctx.schema_items, creates a view (or materialized view) and stores aJoinedViewentry inctx[joins_key]for the API plugin to LEFT JOIN.- Parameters:
Plugin that prevents direct DML on raw backing tables.
Direct INSERT/UPDATE/DELETE on raw backing tables bypasses the INSTEAD OF triggers on the API views, which can corrupt dimension state (e.g. breaking SCD Type 2 history in append-only dimensions, leaving orphaned EAV rows).
RawTableProtectionPlugin installs BEFORE triggers on every raw
table it is given. The triggers raise an exception if called outside a
trigger context (pg_trigger_depth() = 0), forcing all mutations to go
through the API view. When a mutation arrives via the API view, the INSTEAD
OF trigger fires first (pg_trigger_depth() = 1), so the protection
triggers see depth > 0 and allow the operation.
- class RawTableProtectionPlugin(*table_keys)[source]¶
Prevent direct DML on raw backing tables.
Installs BEFORE INSERT/UPDATE/DELETE triggers on every raw table specified by table_keys. The triggers raise an exception when called at trigger depth 0 (i.e. directly, not from within another trigger), so mutations must go through the API view.
All mutations through the API view arrive via an INSTEAD OF trigger at depth >= 1, which the protection triggers allow through.
- Parameters:
*table_keys (
str) – One or morectxkeys whose values are the rawTableobjects to protect.
Example:
RawTableProtectionPlugin("root_table", "attributes")
Alembic integration¶
- pgcraft_alembic_hook(config=None)[source]¶
Register pgcraft’s alembic extensions.
Call before importing models.
Usage in
env.py:from pgcraft.alembic.register import ( pgcraft_alembic_hook, pgcraft_configure_metadata, pgcraft_process_revision_directives, ) pgcraft_alembic_hook() # ... import models / build metadata ... pgcraft_configure_metadata(target_metadata)
Then pass
pgcraft_process_revision_directivestocontext.configure(process_revision_directives=...).- Parameters:
config (
PGCraftConfig|None) – Optional config providing extensions whoseconfigure_alembic()hooks will be called.- Return type:
- pgcraft_configure_metadata(metadata, config=None)[source]¶
Register schemas and extension hooks on metadata.
Roles and grants are no longer registered automatically. Register
PostgRESTExtensionon your config to enable PostgREST roles.- Parameters:
metadata (
MetaData) – The SQLAlchemyMetaDatato configure.config (
PGCraftConfig|None) – Optional config providing extensions. IfNone, falls back tometadata.info["pgcraft_config"].
- Return type:
- class EntityIdentifier(schema='public', name=None, phase=None)[source]¶
Identifies a database entity within a migration’s dependency graph.
nameisNonefor schema-level entities (i.e. the entity is the schema). For tables, views, and functions,nameholds the unqualified object name andschemaholds its containing schema.phasedistinguishes drop and create ops for the same entity when anUpdate*Ophas been expanded."drop"ops are ordered before"create"ops for the same entity.
- build_fk_graph_from_metadata(metadata)[source]¶
Build a table FK dependency map from SQLAlchemy metadata.
Uses the metadata’s knowledge of foreign key relationships rather than parsing column objects from migration ops.
- expand_update_ops(migration_ops)[source]¶
Split
Update*OpintoDrop*Op+Create*Op.CREATE OR REPLACE VIEWfails when another view that depends on this one has an incompatible column list. Splitting into separate drop/create operations lets the topological sort interleave them correctly: drop dependents first, then drop and recreate dependencies, then recreate dependents.- Return type:
list[MigrateOperation|MigrateOp]
- sort_migration_ops(migration_ops, *, fk_graph=None)[source]¶
Return migration_ops topologically sorted by entity dependencies.
Dependency edges are derived from the ops themselves:
A table depends on its schema.
A table depends on tables it references via foreign keys.
A replaceable entity (view, function, …) depends on its schema and on every schema-qualified table or view referenced in its SQL definition.
Only dependencies between ops in the current migration produce edges; references to already-existing objects are ignored.
Edge direction is determined per-op by phase: drop-phase ops reverse their edges (dependents dropped first), create-phase ops use normal direction (dependencies created first).
- Parameters:
- Return type:
list[MigrateOperation|MigrateOp]- Returns:
A new list containing the same ops in dependency order.
Automatic schema discovery for alembic autogenerate.
Scans a MetaData instance for schemas referenced by
tables and views, then registers them with
sqlalchemy-declarative-extensions so its built-in schema comparator
emits the appropriate CREATE SCHEMA / DROP SCHEMA ops.
- register_schemas(metadata)[source]¶
Populate
metadata.info["schemas"]from tables and views.Merges with any schemas already registered on the metadata. Safe to call multiple times — existing entries are preserved.
- Return type:
Custom alembic renderers that format SQL with pglast.
PostgREST / API¶
API resource registration for PostgREST grant generation.
- class APIResource(name, schema='api', grants=<factory>)[source]¶
A database object exposed via PostgREST.
- register_api_resource(metadata, resource)[source]¶
Register an API resource on metadata.
- Return type:
PostgREST role and grant declarations.
- register_roles(metadata)[source]¶
Register PostgREST roles and grants on metadata.
Reads
APIResourceobjects from the metadata and generates per-resource grants.- Return type: