Datomic Compatibility

pg_mentat implements the core Datomic data model and query language as a PostgreSQL extension. This document reflects the actual implementation status of the current codebase.

Overview

pg_mentat provides Datomic-compatible schema, transactions, and Datalog queries via SQL functions. The companion mentatd server exposes an HTTP API compatible with Datomic client libraries.


Transaction Operations

FeatureDatomicpg_mentatStatus
:db/addYesYesComplete
:db/retractYesYesComplete
:db/retractEntityYesYesComplete -- cascade via :db/isComponent
:db.fn/casYesYesComplete -- compare-and-swap
with (speculative)YesYesComplete -- mentat_with()
Map notationYesYesComplete
TempidsYesYesComplete
Lookup refsYesYesComplete
Upsert (unique/identity)YesYesComplete
Tx metadata (:db/txInstant)YesYesComplete
Negative tempidsYesNoN/A -- use string tempids
:db/fn (database functions)YesNoNot Planned -- security risk

Query Operations

Find Specifications

FeatureDatomicpg_mentatStatus
Relation [:find ?x ?y]YesYesComplete
Collection [:find [?x ...]]YesYesComplete
Tuple [:find [?x ?y]]YesYesComplete
Scalar [:find ?x .]YesYesComplete

Where Clauses

FeatureDatomicpg_mentatStatus
Basic patternsYesYesComplete
NOT clausesYesYesComplete -- NOT EXISTS subquery
NOT-JOINYesYesComplete
OR clausesYesYesComplete -- UNION
OR-JOINYesYesComplete

Input Bindings

FeatureDatomicpg_mentatStatus
Scalar :in ?xYesYesComplete
Collection :in [?x ...]YesYesComplete
Tuple :in [?x ?y]YesYesComplete
Relation :in [[?x ?y]]YesYesComplete

Query Functions

FunctionDatomicpg_mentatStatus
get-elseYesYesComplete -- LEFT JOIN + COALESCE
missing?YesYesComplete -- NOT EXISTS subquery
groundYesYesComplete -- constant binding
get-someYesNoNot implemented
tupleYesNoNot implemented
untupleYesNoNot implemented

Predicates

FeatureDatomicpg_mentatStatus
=, !=YesYesComplete
<, >, <=, >=YesYesComplete
Arithmetic (+, -, *, /)YesYesComplete
like / ilikeN/AYesExtension -- PostgreSQL LIKE/ILIKE
Type-safety guardYesYesComplete -- mismatched types produce empty set

Aggregates

AggregateDatomicpg_mentatStatus
countYesYesComplete
count-distinctYesYesComplete
sumYesYesComplete
minYesYesComplete
maxYesYesComplete
avgYesYesComplete
sampleYesYesComplete
medianYesNoNot implemented
varianceYesNoNot implemented
stddevYesNoNot implemented

Rules

FeatureDatomicpg_mentatStatus
Named rulesYesYesComplete
Recursive rulesYesYesComplete -- WITH RECURSIVE CTE
Rule sets (multiple heads)YesYesComplete
Rules with predicatesYesYesComplete

Pull API

FeatureDatomicpg_mentatStatus
Attribute selectionYesYesComplete
Wildcard [*]YesYesComplete
Reverse lookup [:_attr]YesYesComplete
Nested/map specsYesYesComplete
Recursion {:attr ...}YesYesComplete -- cycle detection
Bounded recursion {:attr N}YesYesComplete
Default valuesYesYesComplete
Rename (:as)YesYesComplete
Limit (:limit N)YesYesComplete
Component auto-expandYesYesComplete
pull-manyYesYesComplete
Namespace wildcardYesNoNot implemented

Schema

FeatureDatomicpg_mentatStatus
:db/identYesYesComplete
:db/valueTypeYesYesComplete (9 types)
:db/cardinalityYesYesComplete (one/many)
:db/unique (value)YesYesComplete
:db/unique (identity)YesYesComplete + upsert
:db/indexYesYesComplete
:db/fulltextYesYesComplete -- BM25 scoring
:db/isComponentYesYesComplete -- cascade retraction
:db/noHistoryYesYesComplete
:db/docYesYesComplete
Schema alterationYesYesComplete

Value Types

TypeDatomicpg_mentatStorage
:db.type/booleanYesYesBOOLEAN in datoms_boolean_new
:db.type/longYesYesBIGINT in datoms_long_new
:db.type/doubleYesYesDOUBLE PRECISION in datoms_double_new
:db.type/stringYesYesTEXT in datoms_text_new
:db.type/keywordYesYesTEXT in datoms_keyword_new
:db.type/refYesYesBIGINT in datoms_ref_new
:db.type/instantYesYesTIMESTAMPTZ in datoms_instant_new
:db.type/uuidYesYesUUID in datoms_uuid_new
:db.type/bytesYesYesBYTEA in datoms_bytes_new
:db.type/bigintYesNoNot implemented
:db.type/bigdecYesNoNot implemented
:db.type/uriYesNoNot Planned -- use string
:db.type/tupleYesNoNot Planned -- Datomic Cloud only

Time-Travel

FeatureDatomicpg_mentatStatus
as-ofYesYesComplete -- mentat_as_of()
sinceYesYesComplete -- mentat_since()
historyYesYesComplete -- mentat_history()
tx-rangeYesYesComplete -- mentat_tx_range()
Basis-tYesYesComplete -- MAX(tx) query

Excision

FeatureDatomicpg_mentatStatus
Entity excisionYesYesComplete -- mentat_excise()
Partition gatingYesYesComplete -- allow_excision flag
Schema protectionYesYesComplete -- entid < 10000 blocked
Dangling ref checkYesYesComplete
Excision logN/AYesExtension -- audit trail

Subscriptions / tx-report-queue

FeatureDatomicpg_mentatStatus
tx-report-queueYesYesComplete -- LISTEN/NOTIFY triggers
Subscribe by queryNoYesExtension -- mentat_subscribe()
UnsubscribeYesYesComplete -- mentat_unsubscribe()

mentatd HTTP Protocol

FeatureDatomic ClientmentatdStatus
list-databasesYesYesComplete
create-databaseYesYesComplete
delete-databaseYesYesComplete
connectYesYesComplete
transactYesYesComplete
queryYesYesComplete
pullYesYesComplete
entityYesYesComplete
EDN serializationYesYesComplete
Transit+JSONYesYesComplete
CORSN/AYesComplete
HTTP/2YesNoNot implemented
Streaming responsesYesNoNot implemented

Known Incompatibilities

FeatureReasonWorkaround
:db/fnSecurity risk -- arbitrary code executionApplication logic
Peer modeArchitecture incompatible (embedded JVM)Use mentatd HTTP API
Datomic AnalyticsProprietaryPostgreSQL analytics tools
Negative tempidsNot supportedUse string tempids
Peer cacheNo equivalentApplication-layer caching

Migration from Datomic

Schema definitions transfer directly. Remove #db/id partition references:

;; Datomic
[{:db/id #db/id[:db.part/db]
  :db/ident :person/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}]

;; pg_mentat (same minus partition boilerplate)
[{:db/ident :person/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one}]

Queries work unchanged. Transactions work unchanged. Pull patterns work unchanged.