Derives virtual PostgreSQL table definitions from Datahike schemas.
Maps attribute namespace prefixes to table names and attribute local names to column names. For example: :person/name → table 'person', column 'name' :person/age → table 'person', column 'age'
Every virtual table gets an implicit 'db_id' column (the entity ID).
Derives virtual PostgreSQL table definitions from Datahike schemas. Maps attribute namespace prefixes to table names and attribute local names to column names. For example: :person/name → table 'person', column 'name' :person/age → table 'person', column 'age' Every virtual table gets an implicit 'db_id' column (the entity ID).
When non-nil, a map {:hints ihm :tables ihm} of two
IdentityHashMaps used to memoise schema-hints (keyed by db) and
derive-virtual-tables (keyed by schema, with inner keyed by
hints). Bound at the top of parse-sql's catalog enrichment block
so all catalog-data-for* calls in one query share the cache.
When non-nil, a map `{:hints ihm :tables ihm}` of two
IdentityHashMaps used to memoise `schema-hints` (keyed by db) and
`derive-virtual-tables` (keyed by schema, with inner keyed by
hints). Bound at the top of parse-sql's catalog enrichment block
so all `catalog-data-for*` calls in one query share the cache.(column-attnum schema table-name col-name)Return the 1-based PG attnum for a column, given the derived virtual-table map. The ordering MUST match pg_attribute row emission and RowDescription emission or pgjdbc's field-metadata JOIN (keyed on (tableOid, attnum)) won't resolve columns — so both sites call this helper.
Returns nil when the table or column isn't known.
Return the 1-based PG attnum for a column, given the derived virtual-table map. The ordering MUST match pg_attribute row emission and RowDescription emission or pgjdbc's field-metadata JOIN (keyed on (tableOid, attnum)) won't resolve columns — so both sites call this helper. Returns nil when the table or column isn't known.
(column-info schema table-name)(column-info schema table-name db)Return ordered column info for a specific table, prepending db_id. When db is provided, columns are ordered by schema entity ID (CREATE TABLE order) and :datahike.pg/* hints (column rename, hide, FK target) are applied to the derivation. Returns [{:name str :attr keyword :oid int ...} ...]
Return ordered column info for a specific table, prepending db_id.
When db is provided, columns are ordered by schema entity ID
(CREATE TABLE order) and :datahike.pg/* hints (column rename, hide,
FK target) are applied to the derivation.
Returns [{:name str :attr keyword :oid int ...} ...](column-order-from-db db table-name)Derive column creation order for a table from schema entity IDs. Returns [col-name ...] in the order attributes were transacted (CREATE TABLE order). Requires a Datahike db value. Falls back to alphabetical if db is nil.
Derive column creation order for a table from schema entity IDs. Returns [col-name ...] in the order attributes were transacted (CREATE TABLE order). Requires a Datahike db value. Falls back to alphabetical if db is nil.
(derive-ref-targets schema hints)For each :db.type/ref attribute, determine the target PK attribute
that SQL projection should dereference to. SELECT order.customer FROM order should return the customer's PK value (1, 2, 3) — what a real
PostgreSQL FK column stores — not the Datahike entity-id (10, 11, 12)
that the ref attribute physically holds.
Returns {ref-attr-ident → target-pk-attr-ident} (a subset of the
ref attrs in the schema; refs with no resolvable target are omitted
and fall back to projecting the raw entity-id).
Resolution order:
:datahike.pg/references K hint (authoritative).(name ref-attr) names a namespace, and the
:db.unique/identity attr in that namespace is the target.
Example: :order/customer (ref) → namespace customer →
:customer/id (the only :db.unique/identity in customer).
This matches the seed style most projects use, where ref attrs
are named after the table they point to.Both :db.cardinality/one AND :db.cardinality/many refs are
included. The result map's value is a target-pk-attr for the
one-cardinality case, OR a vector [target-pk-attr :many] for
many-cardinality refs — the translator branches on the shape:
get-else that derefs to a single
target-PK value (matching real PG INT FK columns)fns/pg-many-ref-array)
that boxes all target PKs into a PgArray (matching
int8[] columns)Pure schema-side derivation; for runtime data validation against the
actual entities a ref points to (polymorphism detection,
namespace-mismatch detection), see validate-ref-targets!.
Memoized — schema is value-typed and typically stable per conn, so recomputing per query is wasted work.
For each `:db.type/ref` attribute, determine the *target PK attribute*
that SQL projection should dereference to. `SELECT order.customer FROM
order` should return the customer's PK value (1, 2, 3) — what a real
PostgreSQL FK column stores — not the Datahike entity-id (10, 11, 12)
that the ref attribute physically holds.
Returns `{ref-attr-ident → target-pk-attr-ident}` (a subset of the
ref attrs in the schema; refs with no resolvable target are omitted
and fall back to projecting the raw entity-id).
Resolution order:
1. Explicit `:datahike.pg/references K` hint (authoritative).
2. Convention: `(name ref-attr)` names a namespace, and the
`:db.unique/identity` attr in that namespace is the target.
Example: `:order/customer` (ref) → namespace `customer` →
`:customer/id` (the only `:db.unique/identity` in `customer`).
This matches the seed style most projects use, where ref attrs
are named after the table they point to.
Both `:db.cardinality/one` AND `:db.cardinality/many` refs are
included. The result map's value is a `target-pk-attr` for the
one-cardinality case, OR a vector `[target-pk-attr :many]` for
many-cardinality refs — the translator branches on the shape:
- one → emit a chained `get-else` that derefs to a single
target-PK value (matching real PG INT FK columns)
- many → emit a per-row Clojure fn (`fns/pg-many-ref-array`)
that boxes all target PKs into a PgArray (matching
`int8[]` columns)
Pure schema-side derivation; for runtime data validation against the
actual entities a ref points to (polymorphism detection,
namespace-mismatch detection), see `validate-ref-targets!`.
Memoized — schema is value-typed and typically stable per conn, so
recomputing per query is wasted work.(derive-virtual-tables schema)(derive-virtual-tables schema hints)Derive virtual table definitions from a Datahike schema map.
Returns a map of table-name → {:columns [...] :attrs {...}} where each column is {:name str :attr keyword :oid int :valuetype keyword :cardinality keyword :unique keyword-or-nil :ref? bool :references keyword-or-nil} and :attrs maps column-name → attribute-ident.
Filters out internal (db.*) attributes by default.
When hints is supplied (a map {attr-ident → hint-map} typically
built by schema-hints), applies per-attribute customizations:
:datahike.pg/hidden true → attribute excluded entirely:datahike.pg/column "x" → column renamed to "x":datahike.pg/references K→ carried through on the col as :references
for the translator's FK-via-ref JOIN rewrite.Each catalog table referenced in a query calls this with the same
schema/hints. When *catalog-tx-cache* is bound, the result is
memoised across all calls in the current parse-sql so the schema
walk happens once per query (not once per catalog table).
Derive virtual table definitions from a Datahike schema map.
Returns a map of table-name → {:columns [...] :attrs {...}}
where each column is {:name str :attr keyword :oid int :valuetype keyword
:cardinality keyword :unique keyword-or-nil :ref? bool
:references keyword-or-nil}
and :attrs maps column-name → attribute-ident.
Filters out internal (db.*) attributes by default.
When `hints` is supplied (a map `{attr-ident → hint-map}` typically
built by `schema-hints`), applies per-attribute customizations:
- `:datahike.pg/hidden true` → attribute excluded entirely
- `:datahike.pg/column "x"` → column renamed to "x"
- `:datahike.pg/references K`→ carried through on the col as `:references`
for the translator's FK-via-ref JOIN rewrite.
Each catalog table referenced in a query calls this with the same
schema/hints. When `*catalog-tx-cache*` is bound, the result is
memoised across all calls in the current parse-sql so the schema
walk happens once per query (not once per catalog table).(ensure-hint-schema! conn)Idempotently install the :datahike.pg/* hint schema attrs on conn.
Safe to call multiple times. Called automatically by set-hint! and
by the server's ensure-pg-schema!. Exposed as public so callers
running under :schema-flexibility :read can prime the schema
before their first set-hint! or eager hint transaction.
Idempotently install the :datahike.pg/* hint schema attrs on `conn`. Safe to call multiple times. Called automatically by `set-hint!` and by the server's `ensure-pg-schema!`. Exposed as public so callers running under `:schema-flexibility :read` can prime the schema before their first `set-hint!` or eager hint transaction.
Datahike schema for the :datahike.pg/* hint entities. A hint is a dedicated entity (NOT attributes attached to the ident) that points at its target via :datahike.pg/for-ident. This sidesteps Datahike's schema-update guard, which forbids adding arbitrary attrs to an already-transacted schema entity — a necessary limit on real schema migration but one that makes ident-attached annotations impractical.
ensure-pg-schema! in server.clj installs this for every handler-
backed conn. Users can also transact it eagerly under
:schema-flexibility :read.
Datahike schema for the :datahike.pg/* hint entities. A hint is a dedicated entity (NOT attributes attached to the ident) that points at its target via :datahike.pg/for-ident. This sidesteps Datahike's schema-update guard, which forbids adding arbitrary attrs to an already-transacted schema entity — a necessary limit on real schema migration but one that makes ident-attached annotations impractical. `ensure-pg-schema!` in server.clj installs this for every handler- backed conn. Users can also transact it eagerly under `:schema-flexibility :read`.
(information-schema-columns-rows schema)(information-schema-columns-rows schema db-or-hints)Return rows for information_schema.columns query. Each row: [table_catalog table_schema table_name column_name ordinal_position data_type is_nullable column_default].
Accepts an optional db (or hints map) so :datahike.pg/column /
:datahike.pg/hidden are applied to the emitted row set. Callers that
don't have a db in scope can keep the 1-arity form.
Return rows for information_schema.columns query.
Each row: [table_catalog table_schema table_name column_name ordinal_position
data_type is_nullable column_default].
Accepts an optional `db` (or `hints` map) so :datahike.pg/column /
:datahike.pg/hidden are applied to the emitted row set. Callers that
don't have a db in scope can keep the 1-arity form.(make-catalog-tx-cache)Construct a fresh per-query cache. Caller bindings
catalog-tx-cache to the result around their unit of work.
Construct a fresh per-query cache. Caller `binding`s *catalog-tx-cache* to the result around their unit of work.
(next-table-oid db)Pick the next unused :pg/table-oid. We query via Datalog because the attribute is attached to the row-marker entity, not part of the :schema map.
Pick the next unused :pg/table-oid. We query via Datalog because the attribute is attached to the row-marker entity, not part of the :schema map.
(oid-for-valuetype vtype)Return the PostgreSQL type OID for a Datahike valueType keyword.
Return the PostgreSQL type OID for a Datahike valueType keyword.
(pg-type-name vtype)Return the PostgreSQL type name string for a Datahike valueType keyword.
Return the PostgreSQL type name string for a Datahike valueType keyword.
(ref-attrs-from-schema schema)Return the seq of :db.type/ref :db.cardinality/one attribute idents
in schema. Excludes internal namespaces.
Return the seq of `:db.type/ref :db.cardinality/one` attribute idents in `schema`. Excludes internal namespaces.
(row-marker-attr table-name)Return the row-existence marker attribute for a table. E.g., :person/db-row-exists for table 'person'.
Return the row-existence marker attribute for a table. E.g., :person/db-row-exists for table 'person'.
(schema-hints db)Return {attr-ident → {:column str? :hidden bool? :references kw? :table str?}}
by scanning the db for :datahike.pg/for-ident-rooted hint entities.
Nil-safe: returns an empty map when db is nil (pure-schema call sites).
When *catalog-tx-cache* is bound (within parse-sql's catalog
enrichment), reuses the result across all catalog-data-for* calls
in the current query.
Return `{attr-ident → {:column str? :hidden bool? :references kw? :table str?}}`
by scanning the db for :datahike.pg/for-ident-rooted hint entities.
Nil-safe: returns an empty map when `db` is nil (pure-schema call sites).
When `*catalog-tx-cache*` is bound (within parse-sql's catalog
enrichment), reuses the result across all `catalog-data-for*` calls
in the current query.(set-hint! conn target hint)Convenience: transact or upsert one hint entity. target is the
attribute ident the hint applies to (e.g. :widget/full_name).
hint is a map of :column / :hidden / :references / :table.
Idempotent — :datahike.pg/for-ident is :db.unique/identity, so
repeated calls upsert the same hint entity. Ensures the hint schema
is installed first so callers needn't pre-install.
Convenience: transact or upsert one hint entity. `target` is the attribute ident the hint applies to (e.g. `:widget/full_name`). `hint` is a map of `:column` / `:hidden` / `:references` / `:table`. Idempotent — `:datahike.pg/for-ident` is :db.unique/identity, so repeated calls upsert the same hint entity. Ensures the hint schema is installed first so callers needn't pre-install.
(table-names schema)Return sorted list of virtual table names for a schema.
Return sorted list of virtual table names for a schema.
(table-oid db table-name)The :pg/table-oid attached to this table's row-marker entity, or nil
if the table doesn't exist yet or was created before we started
allocating OIDs. Datahike's (:schema db) only surfaces schema-level
attrs (:db/valueType etc.); custom attrs on the ident entity require
a Datalog lookup against the db.
The :pg/table-oid attached to this table's row-marker entity, or nil if the table doesn't exist yet or was created before we started allocating OIDs. Datahike's `(:schema db)` only surfaces schema-level attrs (:db/valueType etc.); custom attrs on the ident entity require a Datalog lookup against the db.
(validate-ref-targets! db schema ref-targets)Cross-check ref-targets (from derive-ref-targets) against the
actual data in db. Drops:
:datahike.pg/references hint per polymorphic ref.:X but
named the ref attr after a different concept (:order/buyer ref
to customer). Hint should override.Each drop emits a one-shot stderr warning. Returns the validated
subset of ref-targets. Refs with no current data (empty set)
pass through unchanged — the convention is the best guess until
data appears.
Also warns about ref attrs that have NO ref-targets entry at all (no hint, no namespace match) so the user knows to set a hint if they want SQL-FK projection on that column.
Cross-check `ref-targets` (from `derive-ref-targets`) against the actual data in `db`. Drops: - polymorphic refs — entities span multiple namespaces; convention can't pick a single target safely. User must add an explicit `:datahike.pg/references` hint per polymorphic ref. - namespace-mismatched refs — convention picked target X but the data points to namespace Y. Often means the user intended `:X` but named the ref attr after a different concept (`:order/buyer` ref to `customer`). Hint should override. Each drop emits a one-shot stderr warning. Returns the validated subset of `ref-targets`. Refs with no current data (empty set) pass through unchanged — the convention is the best guess until data appears. Also warns about ref attrs that have NO ref-targets entry at all (no hint, no namespace match) so the user knows to set a hint if they want SQL-FK projection on that column.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |