pg_mentat 1.3.0 — Lanzamiento de Datalog compatible con Datomic dentro de PostgreSQL
Me complace anunciar la primera versión pública de pg_mentat, una extensión para PostgreSQL que implementa íntegramente el modelo de datos de Datomic, incluyendo hechos inmutables (datoms), un esquema basado en atributos, un compilador completo de consultas Datalog, la API de pull, capacidades de navegación temporal y transacciones ACID, todo ello dentro del propio PostgreSQL.
pg_mentat ha sido desarrollado en Rust utilizando pgrx 0.17 y es compatible con PostgreSQL desde la versión 13 hasta la 18. La versión 1.3.0, denominada “Postgres Extension Family”, incorpora funciones where-fn para Datalog que facilitan la integración con extensiones como rum, pg_trgm, fuzzystrmatch, pgvector, pg_infer, PostGIS y otras, como dependencias opcionales. Ninguna de estas es obligatoria, aunque se activan automáticamente cuando están disponibles.
Asimismo, se ofrece un daemon opcional, mentatd, que implementa el protocolo de cliente de Datomic (EDN, Transit+JSON y Transit+MsgPack) a través de HTTP, destinado a aplicaciones que ya requieren dicho protocolo.
Aspectos destacados
- Lenguaje de consulta Datalog completo, que incluye especificaciones de búsqueda sobre relaciones, colecciones, tuplas y valores escalares; soporte para coincidencia de patrones, predicados y expresiones funcionales; operadores not, not-join, or y or-join; así como reglas con nombre y reglas recursivas con detección de ciclos.
- API de tipo Pull con soporte para comodines, referencias inversas, extracciones anidadas y recursivas, valores por defecto, renombrado de atributos y cláusulas de limitación, que permite obtener resultados en formato JSON de estructura arbitraria en una única interacción.
- Funcionalidad de navegación temporal, con consultas as-of, since, history y tx-range. Cada datom queda asociado a la marca temporal de su transacción; la base de datos constituye un registro de solo anexado (append-only) de hechos inmutables, lo que permite consultar cualquier estado histórico del sistema.
- Exclusión conforme al Reglamento General de Protección de Datos (GDPR): eliminación permanente de datoms, ya sea por entidad, atributo o valor, con supresión del historial de auditoría correspondiente.
- Suscripciones reactivas mediante el mecanismo LISTEN / NOTIFY de PostgreSQL.
- Modelo de almacenamiento: nueve tablas delgadas diferenciadas por tipo de datom (ref, long, string, bool, double, inst, kw, uuid y bytes), junto con los cuatro índices canónicos de Datomic (EAVT, AEVT, AVET y VAET), implementados mediante índices B-tree convencionales de PostgreSQL.
- Integraciones “soft” nuevas en la versión 1.3.0: rum-fulltext, similar-to (pg_trgm), levenshtein / soundex / metaphone / daitch-mokotoff (fuzzystrmatch), vector-near (pgvector), infer-near / infer-similar / infer-implies / infer-walk / infer-describe / infer-predict (pg_infer), geom-near / geom-within / geom-contains / geom-intersects (PostGIS), y funciones auxiliares de detección (mentat.has_<ext>()) para identificación de capacidades.
¿Por qué integrar EDN y Datalog junto a tablas relacionales?
Datalog y SQL no son redundantes, sino que cada uno responde eficazmente a distintos tipos de problemas. Integrarlos en una misma base de datos, compartiendo el mismo mecanismo de control de concurrencia (MVCC), el mismo registro de escritura anticipada (WAL), el mismo sistema de respaldos y el mismo pool de conexiones, permite habilitar cargas de trabajo que resultan complejas de abordar de forma aislada.
- Grafos de conocimiento que integran los datos de la empresa. Una ontología — que incluye categorías, jerarquías, taxonomías y relaciones tipadas — puede codificarse como datoms con referencias y consultarse mediante reglas Datalog. Las reglas recursivas permiten expresar el cierre transitivo en pocas líneas, sin recurrir a expresiones comunes de tabla (CTE) recursivas ni a lógica adicional para la detección de ciclos. Posteriormente, este grafo puede exponerse como una vista (VIEW) de PostgreSQL mediante mentat.mentat_query_view(…), e integrarse directamente con tablas relacionales mediante operaciones JOIN en SQL estándar. Una base de datos, una transacción, dos lenguajes de consulta.
- Esquema como datos — consultable como todo lo demás. Los atributos del esquema (:db/ident, :db/valueType, :db/cardinality, :db/unique, :db/index, :db/fulltext, :db/isComponent, :db/noHistory) se encuentran en el almacén de datoms. El esquema puede consultarse mediante el mismo lenguaje Datalog utilizado para los datos de la aplicación, en lugar de recurrir a la exploración de pg_catalog o information_schema.
- Auditoría y navegación temporal sin triggers. Debido a que los datoms son inmutables, “¿cómo se veía esta entidad en la transacción 1000005?” y “¿qué cambió entre las transacciones 1000003 y 1000007?” son consultas de primera clase. Sin tablas de auditoría, sin triggers, sin artificios de extensiones temporales. Procesos de cumplimiento normativo que en un esquema relacional tradicional pueden requerir semanas de adaptación se resuelven mediante una única consulta en pg_mentat.
- Transacciones especulativas. La función mentat.mentat_with(‘[ … ]’) ejecuta una transacción en memoria y devuelve un reporte completo que incluye la resolución de identificadores temporales (tempid), los nuevos datoms y los resultados de verificación de restricciones, sin realizar ninguna escritura en el sistema. Esto permite validar importaciones, previsualizar integraciones o detectar conflictos antes del commit.
- EDN como formato de intercambio de datos. Tanto el esquema como las transacciones y las consultas se expresan íntegramente en EDN. Los valores etiquetados (#inst, #uuid, keywords, sets, vectors) mantienen su integridad en operaciones de ida y vuelta sin recurrir a convenciones ad hoc basadas en JSON. El código existente de Clojure / Datomic, incluidos los patrones de pull y las expresiones Datalog, puede ejecutarse sobre pg_mentat con ajustes menores.
Un ejemplo sencillo: un recorrido de grafo que, en SQL estándar, requeriría más de 20 líneas utilizando una CTE recursiva:
SELECT mentat.q(' [:find ?name :in $ ?start :where [?start :person/name ?start-name] (reachable ?start ?friend) [?friend :person/name ?name] :rules [[(reachable ?a ?b) [?a :person/friends ?b]] [(reachable ?a ?b) [?a :person/friends ?c] (reachable ?c ?b)]]] ', '["Alice"]');
Datalog especifica qué se debe buscar. El compilador determina cómo hacerlo. El plan resultante es SQL estándar, ejecutado por PostgreSQL estándar, sobre páginas de PostgreSQL estándar.
Posicionamiento de pg_mentat frente a las alternativas
- Datomic / XTDB / Crux son sistemas de almacenamiento Datalog gestionados que, si bien ofrecen lenguajes de consulta altamente expresivos, presentan limitaciones operativas en entornos que ya han adoptado PostgreSQL como estándar. pg_mentat incorpora su modelo de datos dentro de la base de datos que una organización ya opera, respalda, supervisa y replica.
- pg_graphql / Apache AGE: capas de grafos sobre PostgreSQL que emplean Cypher y GraphQL respectivamente. Se trata de lenguajes de consulta y modelos de datos diferentes, sin semántica de navegación temporal, sin API de pull y sin un modelo integrado de hechos inmutables.
- hstore / jsonb: mecanismos de almacenamiento de esquema flexible que no disponen de un lenguaje de consulta diseñado para la realización de joins entre atributos. pg_mentat incorpora Datalog, referencias, reglas recursivas y la API de pull.
Nota para la comunidad de Datomic / Mentat / Datalog
En caso de haber desarrollado aplicaciones en Clojure utilizando Datomic, Datascript o la implementación original de Mentat (DEP), pg_mentat resultará familiar. El sistema mantiene conceptos equivalentes tales como esquemas en EDN, transacciones y consultas Datalog, la API de pull, capacidades de consulta temporal (as-of / since / history) y la unicidad basada en :db.unique/identity, lo cual simplifica las operaciones de upsert al eliminar la necesidad de construcciones como INSERT ON CONFLICT o MERGE. Las diferencias introducidas son deliberadas y responden a criterios prácticos: el almacenamiento se realiza en PostgreSQL, las transacciones son las de PG, el atributo de búsqueda de texto completo utiliza tsvector, y las herramientas operativas son las propias de PostgreSQL (pg_basebackup, pg_dump, pgBackRest, replicación lógica y WAL-G, entre otras). Me encantaría recibir feedback de cualquiera que tenga experiencia en entornos productivos basados en sistemas tipo Datomic interesados en una implementación de Datalog sobre PostgreSQL, en particular respecto a qué formas de Datalog actualmente utilizadas no están aún soportadas por pg_mentat, así como qué características específicas de Datomic no deberían preservarse. Las observaciones pueden remitirse mediante issues o correo electrónico.
Guía de inicio rápido
``` CREATE EXTENSION pg_mentat; 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} ]'); SELECT mentat.t('[{:person/name "Alice" :person/age 30}]'); SELECT mentat.q(' [:find ?name ?age :where [?e :person/name ?name] [?e :person/age ?age] [(> ?age 18)]]');
Nota sobre estabilidad y retroalimentación
pg_mentat es un software de reciente desarrollo. El esquema en disco, la superficie funcional, la codificación EDN y el dialecto Datalog se encuentran documentados y cubiertos por pruebas. La versión 1.3.0 incorpora diez integraciones de extensiones habilitadas mediante detección de capacidades; sin embargo, el proyecto se encuentra en una fase temprana de desarrollo, por lo que los errores que puedan encontrarse probablemente no hayan sido detectados previamente por otros usuarios. No se trata de una versión beta ni de un prototipo experimental, sino de software funcional publicado de manera anticipada, que debe utilizarse con la debida prudencia.
Se agradecen reportes de errores, solicitudes de mejora y pull requests, particularmente aquellos provenientes de usuarios con experiencia en Datalog capaces de identificar con rapidez posibles lagunas semánticas.
Enlaces
- Repositorio: https://github.com/gburd/pg_mentat
- Issues: https://github.com/gburd/pg_mentat/issues
- Integraciones: https://github.com/gburd/pg_mentat/blob/main/docs/INTEGRATIONS.md

