Documentation
Install instructions, and every function pgdmn installs, with its arguments.
Worked examples, with models and datasets you can download and run, are on the Examples page.
Install
pgdmn is a PostgreSQL extension. To install it, you build it from source, then copy the compiled extension into place. Both are done with pgrx, the framework pgdmn is built on.
Step one: set up pgrx for your database
Install the pgrx command-line tool. It needs a C compiler and a few build libraries; the pgrx system requirements list them for each platform.
cargo install --locked cargo-pgrxNote: to run pgrx you need Cargo, the Rust package manager. If you do not have Cargo, you will need to install Rust.
Then initialise pgrx. Here you choose whether pgrx manages a PostgreSQL for you, or uses one you already run.
If you want pgrx to manage PostgreSQL, it downloads and builds its own—the quickest way to try pgdmn end to end. cargo pgrx run builds pgdmn, installs it into that instance, and drops
you into a psql shell:
# Download and build a PostgreSQL 17 that pgrx manages.
cargo pgrx init --pg17 download
# Build pgdmn, install it into that PostgreSQL, and open psql.
cargo pgrx run pg17To use a PostgreSQL you already run, point pgrx at its pg_config, then build and copy the extension into the directories that pg_config reports (you may need write permission there):
# Point pgrx at your server's pg_config.
cargo pgrx init --pg17 $(which pg_config)
# Build pgdmn and copy it into that PostgreSQL's install.
cargo pgrx install --releaseStep two: enable and verify
In a database on that server, enable the extension and check it works:
CREATE EXTENSION pgdmn;
-- Execute a simple function from the extension.
SELECT feel_eval('1 + 2');
-- 3DMN functions
dmn_load(xml text) RETURNS dmnmodel- Parse DMN XML into a
dmnmodelvalue. This is the entry point for everything else. The result is an ordinary PostgreSQL value: store it in a column, pass it around, join against it. It displays asnamespace::name. dmn_eval(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS jsonb- Evaluate a named decision, business knowledge model, or decision service. Decisions that depend on other decisions are resolved for you—ask for the one you want, and pgdmn works out what it needs first.
dmn_record_eval(model dmnmodel, invocable text, input record DEFAULT NULL) RETURNS jsonb- The same, but the input is a composite-type record rather than JSONB, so table columns map straight onto model inputs with no JSON in between.
Typed variants
dmn_eval returns JSONB, so a decision that produces the string Approved comes back as "Approved", a quoted JSONB string. Unwrapping
that by hand means dmn_eval(…) #>> '{}', and a numeric decision means
(dmn_eval(…) #>> '{}')::numeric.
These take the same arguments and hand back a native PostgreSQL type instead. Each errors if the decision returns something else.
dmn_eval_text(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS text- A decision that returns a string, unquoted.
dmn_eval_numeric(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS numeric- A decision that returns a number, ready for arithmetic without a cast:
round(dmn_eval_numeric(…), 2). dmn_eval_bool(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS boolean- A decision that returns a boolean—usable directly in a
WHEREclause or aCHECKconstraint. dmn_eval_date(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS date- A decision that returns a date.
dmn_eval_timestamp(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS timestamp- A decision that returns a date and time.
dmn_eval_interval(model dmnmodel, invocable text, input jsonb DEFAULT NULL) RETURNS interval- A decision that returns a duration. Both flavours convert: years and months, and days and time.
Introspection functions
dmn_invocables(model dmnmodel) RETURNS setof (name text, kind text)- Every invocable element in the model, as rows.
kindisdecision,business_knowledge_model, ordecision_service. Useful for discovering what a model you were handed can actually answer. dmn_info(model dmnmodel) RETURNS jsonb- Model metadata: name, namespace, a count of each element type, and the list of invocable names.
dmn_xml(model dmnmodel) RETURNS text- The original XML source, byte for byte.
dmn_name(model dmnmodel) RETURNS text- The model's name.
dmn_namespace(model dmnmodel) RETURNS text- The model's namespace.
FEEL functions
FEEL is the expression language DMN is built on. These evaluate it directly, with no model involved. These utility functions aren't usually what you should use, but are here in case you want to cross check how something evaluates.
feel_eval(expression text, context jsonb DEFAULT NULL) RETURNS jsonb- Evaluate a FEEL expression. Keys of the context become variables in scope.
feel_record_eval(expression text, context record DEFAULT NULL) RETURNS jsonb- The same, with a composite-type record as the context. Columns become variables.
Typed variants
Each returns a native PostgreSQL type instead of JSONB, so the result drops into a typed column or a comparison without a cast. Each raises an error if the expression returns something else.
feel_eval_numeric(expression text, context jsonb DEFAULT NULL) RETURNS numeric- A FEEL number, as
numeric. feel_eval_bool(expression text, context jsonb DEFAULT NULL) RETURNS boolean- A FEEL boolean—usable directly in a
WHEREclause or aCHECKconstraint. feel_eval_text(expression text, context jsonb DEFAULT NULL) RETURNS text- A FEEL string, unquoted.
feel_eval_date(expression text, context jsonb DEFAULT NULL) RETURNS date- A FEEL date, as
date. feel_eval_timestamp(expression text, context jsonb DEFAULT NULL) RETURNS timestamp- A FEEL date and time, as
timestamp. feel_eval_numrange(expression text, context jsonb DEFAULT NULL) RETURNS numrange- A FEEL range of numbers, as
numrange. feel_eval_interval(expression text, context jsonb DEFAULT NULL) RETURNS interval- A FEEL duration, as
interval. Both flavours convert: years and months, and days and time.
Performance
Every function above is IMMUTABLE and PARALLEL SAFE. PostgreSQL is free to spread a decision table across as many parallel workers as
it likes, and to skip repeated calls with identical arguments.
A model is not re-parsed per row. Parsed models are cached, keyed by the XML itself, so evaluating one model against a whole table parses it once.