Contents
Getting Started
Installation
From Source
Prerequisites:
- Rust 1.88+ (install via rustup)
- PostgreSQL 15-17 with development headers
- LLVM/Clang development libraries (for pgrx bindgen)
- pkg-config, build-essential (or equivalent)
On Debian/Ubuntu:
sudo apt-get install -y postgresql-16 postgresql-server-dev-16 \
libpq-dev build-essential pkg-config libclang-dev clang
On Fedora/RHEL:
sudo dnf install -y postgresql16-server postgresql16-devel \
clang-devel llvm-devel pkg-config
On macOS (Homebrew):
brew install postgresql@16 llvm pkg-config
Then build and install:
git clone https://github.com/gburd/pg_mentat.git
cd pg_mentat
# Install cargo-pgrx (the PostgreSQL extension build tool)
cargo install --locked cargo-pgrx --version 0.19.3
# Tell pgrx where your PostgreSQL is installed
cargo pgrx init --pg16 $(which pg_config)
# Build and install the extension into your PostgreSQL
cd pg_mentat
cargo pgrx install --release --no-default-features --features pg16
Docker
docker build -t pg_mentat .
docker run -d --name pg_mentat \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
pg_mentat
Or use Docker Compose for the full stack (pg_mentat + mentatd + Prometheus + Grafana):
docker compose -f docker/docker-compose.yml up -d
From Source (Nix)
If you use Nix, the provided flake handles all dependencies:
git clone https://github.com/gburd/pg_mentat.git
cd pg_mentat
nix develop
# Everything is pre-configured — just build
cd pg_mentat
cargo pgrx install --release --no-default-features --features pg16
Quick Start
1. Create the Extension
CREATE EXTENSION pg_mentat;
This creates the mentat schema with all required tables, types, indexes, and functions.
A superuser or (since 1.17.0) any role with CREATE on the database can run it;
see multi-tenancy for the
privileges a non-superuser installer gets and how to grant them to others.
2. Define Schema Attributes
Schema attributes describe the shape of your data. Every attribute needs an ident (namespaced keyword), a value type, and a cardinality.
SELECT mentat.t('[
{:db/ident :person/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}
{:db/ident :person/age
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one}
{:db/ident :person/email
:db/valueType :db.type/string
:db/cardinality :db.cardinality/many}
{:db/ident :person/friends
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}
]');
3. Transact Data
Use tempids (string identifiers) to create new entities. References between entities in the same transaction resolve automatically.
SELECT mentat.t('[
{:db/id "alice"
:person/name "Alice"
:person/age 30
:person/email "alice@example.com"}
{:db/id "bob"
:person/name "Bob"
:person/age 25
:person/friends "alice"}
]');
The return value is a JSON report containing the transaction ID, timestamp, and tempid resolution map.
4. Query with Datalog
-- Find all people over 25
SELECT mentat.q('
[:find ?name ?age
:where [?e :person/name ?name]
[?e :person/age ?age]
[(> ?age 25)]]
');
-- Find friends-of-friends (with input binding)
SELECT mentat.q('
[:find ?friend-name
:in $ ?name
:where [?e :person/name ?name]
[?e :person/friends ?f]
[?f :person/name ?friend-name]]
', '["Alice"]');
5. Pull Entity Data
-- Pull all attributes for entity 10001
SELECT mentat.pull('[*]', 10001);
-- Pull specific attributes with nested navigation
SELECT mentat.pull(
'[:person/name :person/age {:person/friends [:person/name]}]',
10001
);
6. Time Travel
-- Query the database as it was at transaction 1000
SELECT mentat.q('[:find ?name :where [?e :person/name ?name]]', '[]', 1000, NULL);
-- View transaction log
SELECT mentat.log('default', 1000, 1010);
-- What changed between two transactions?
SELECT mentat.diff('default', 1000, 1005);
Next Steps
- Architecture – understand how pg_mentat stores and queries data
- Datalog Query Language – complete query syntax reference
- SQL Function Reference – all available SQL functions
- Schema Reference – attribute definitions and value types