Liking cljdoc? Tell your friends :D

datahike.pg.sql.ctx

Translation context for the SQL → Datalog translator.

The ctx is an immutable map of per-translation atoms that every translate-* fn threads through. Atoms capture the side-effecting state (fresh-var counter, collected where clauses, entity-var bindings, prepared-statement placeholders, …) while the outer map stays shareable with nested sub-translations.

Fn inventory:

  • resolve-column / resolve-inherited-attr — map a JSqlParser Column reference to a Datahike attribute keyword, following the table-alias map and PostgreSQL INHERITS semantics.
  • make-ctx — build a fresh context.
  • fresh-var!, entity-var!, add-clause!, col-var! — primitives the translators call to allocate logic variables, assign entity bindings, append where clauses, and lazily produce get-else-backed column bindings.
  • materialize-arg! — bind a composite expression to a fresh var.
  • null-guard-clauses / make-columns-optional! — SQL 3-valued-logic helpers.
  • collect-vars — recursively gather ?v-style symbols out of a translated form.

Fns originally marked ^:private in sql.clj are promoted to public here so the extracted translate-* namespaces can reach them without re-exporting through the top-level sql ns.

Translation context for the SQL → Datalog translator.

The `ctx` is an immutable map of per-translation atoms that every
`translate-*` fn threads through. Atoms capture the side-effecting
state (fresh-var counter, collected where clauses, entity-var
bindings, prepared-statement placeholders, …) while the outer map
stays shareable with nested sub-translations.

Fn inventory:

- `resolve-column` / `resolve-inherited-attr` — map a JSqlParser
  `Column` reference to a Datahike attribute keyword, following the
  table-alias map and PostgreSQL INHERITS semantics.
- `make-ctx` — build a fresh context.
- `fresh-var!`, `entity-var!`, `add-clause!`, `col-var!` —
  primitives the translators call to allocate logic variables,
  assign entity bindings, append where clauses, and lazily
  produce `get-else`-backed column bindings.
- `materialize-arg!` — bind a composite expression to a fresh var.
- `null-guard-clauses` / `make-columns-optional!` — SQL
  3-valued-logic helpers.
- `collect-vars` — recursively gather `?v`-style symbols out of
  a translated form.

Fns originally marked `^:private` in sql.clj are promoted to
public here so the extracted translate-* namespaces can reach
them without re-exporting through the top-level sql ns.
raw docstring

*relation-namespaces*clj

{relation-name -> storage-namespace} for speculative relations — currently the CTEs in scope. Bound by the translator; consulted here so no FROM-clause site can forget it.

A CTE is stored as ordinary attributes in a speculative db, and its namespace used to be its own name, so a CTE named after a real table wrote into that table's namespace and the two merged instead of the CTE shadowing the table. Redirecting the NAME while leaving the ALIAS as the user wrote it routes the reference through the same machinery as FROM emp e, which already resolves an alias to a differently-named relation correctly.

`{relation-name -> storage-namespace}` for speculative relations —
currently the CTEs in scope. Bound by the translator; consulted here
so no FROM-clause site can forget it.

A CTE is stored as ordinary attributes in a speculative db, and its
namespace used to be its own name, so a CTE named after a real table
wrote into that table's namespace and the two merged instead of the
CTE shadowing the table. Redirecting the NAME while leaving the ALIAS
as the user wrote it routes the reference through the same machinery
as `FROM emp e`, which already resolves an alias to a differently-named
relation correctly.
sourceraw docstring

*strict-columns*clj

Set to false to suppress the unknown-column check for one translation. Bound by the catalog-probe path, where a client legitimately asks for pg_catalog columns we don't materialise.

Set to false to suppress the unknown-column check for one
translation. Bound by the catalog-probe path, where a client
legitimately asks for pg_catalog columns we don't materialise.
sourceraw docstring

add-clause!clj

(add-clause! ctx clause)

Append a Datalog clause to the context's where-clauses.

Append a Datalog clause to the context's where-clauses.
sourceraw docstring

attr-ofclj

(attr-of ctx resolved)

The Datahike attribute a resolve-column result denotes, with INHERITS resolution applied — or nil for the [:db-id …] form, which denotes an entity rather than an attribute.

resolve-column returns two attribute-bearing shapes, :ns/col and [:aliased alias :ns/col], and inheritance has to be resolved for BOTH: an INHERITS child stores its parent's columns under the PARENT namespace on the same entity (:par/pname, not :chi/pname), so a reference that keeps the child namespace binds nothing and reads as NULL.

Every consumer used to do this itself and every one of them handled only the keyword shape, so SELECT c.pname FROM child c — and any other aliased reference to an inherited column — silently returned NULL where PostgreSQL returns the value. Routing all of them through here is what keeps the two shapes from drifting apart again.

The Datahike attribute a `resolve-column` result denotes, with
INHERITS resolution applied — or nil for the `[:db-id …]` form,
which denotes an entity rather than an attribute.

`resolve-column` returns two attribute-bearing shapes, `:ns/col` and
`[:aliased alias :ns/col]`, and inheritance has to be resolved for
BOTH: an INHERITS child stores its parent's columns under the PARENT
namespace on the same entity (`:par/pname`, not `:chi/pname`), so a
reference that keeps the child namespace binds nothing and reads as
NULL.

Every consumer used to do this itself and every one of them handled
only the keyword shape, so `SELECT c.pname FROM child c` — and any
other aliased reference to an inherited column — silently returned
NULL where PostgreSQL returns the value. Routing all of them through
here is what keeps the two shapes from drifting apart again.
sourceraw docstring

bind-col-param!clj

(bind-col-param! ctx resolved pvar)

Translate col = $N (extended-protocol parameter) as a data pattern [?e :attr ?pN] whose value var is :in-bound at Execute — the engine seeks the index with the bound value instead of scanning a get-else binding + equality predicate (this was why pgbench -M prepared point lookups were 10x slower than interpolated literals). SQL col = NULL must yield zero rows: the (some? ?pN) guard enforces that — a nil :in binding degrades the pattern itself to a scan, but the guard then rejects every row. Same soundness rules as bind-col-value! (top-level conjunct, plain column). Returns true when handled.

Translate `col = $N` (extended-protocol parameter) as a data pattern
`[?e :attr ?pN]` whose value var is `:in`-bound at Execute — the
engine seeks the index with the bound value instead of scanning a
get-else binding + equality predicate (this was why `pgbench -M
prepared` point lookups were 10x slower than interpolated literals).
SQL `col = NULL` must yield zero rows: the `(some? ?pN)` guard
enforces that — a nil `:in` binding degrades the pattern itself to a
scan, but the guard then rejects every row. Same soundness rules as
bind-col-value! (top-level conjunct, plain column). Returns true
when handled.
sourceraw docstring

bind-col-value!clj

(bind-col-value! ctx resolved v)

Translate col = <constant> as a value-bound data pattern [?eid :attr v] — an indexable clause — instead of a get-else binding plus (= ?col v) predicate over every row. Only sound in a top-level conjunctive context (the caller guards that) and only emitted when the constant's runtime class matches the attribute's declared valueType, so pattern-equality can't diverge from predicate-equality on cross-type comparisons. Returns true when handled.

Translate `col = <constant>` as a value-bound data pattern
`[?eid :attr v]` — an indexable clause — instead of a get-else
binding plus `(= ?col v)` predicate over every row. Only sound in a
top-level conjunctive context (the caller guards that) and only
emitted when the constant's runtime class matches the attribute's
declared valueType, so pattern-equality can't diverge from
predicate-equality on cross-type comparisons. Returns true when
handled.
sourceraw docstring

col-var!clj

(col-var! ctx attr)

Get or create the logic variable for an attribute.

For ordinary columns, binds the var via get-else so rows without the column still flow through (bound to the :__null__ sentinel). This models SQL NULL semantics — a missing attribute is NULL, not a reason to drop the row — and the var is recorded in :nullable-vars so comparison predicates can wrap it in null-guards (three-valued logic).

Exception: when the entity is a LEFT JOIN right-side var (already registered in :left-join-evars), emit a plain data pattern instead. The LEFT JOIN or-join construction (run later in translate-select) identifies those patterns and relocates them into the matched branch, synthesising :__null__ via ground in the unmatched branch. A get-else with the LEFT JOIN sentinel entity-id would otherwise throw. These vars are still added to :nullable-vars so null-guards apply.

:db.type/ref columns: when ctx's :ref-targets map has an entry for the resolved attr, the returned var is the target-PK value (e.g. for :order/customer → the referenced customer's :customer/id), matching how a real PG FK column projects. Callers that need the raw entity-id (only the JOIN-condition rewriter) use ref-eid-var! instead.

Handles three forms of attr: [:db-id alias-key] → return entity var for the alias [:aliased alias-key kw] → aliased column (self-joins) :ns/col → regular column

Get or create the logic variable for an attribute.

For ordinary columns, binds the var via `get-else` so rows without the
column still flow through (bound to the `:__null__` sentinel). This
models SQL NULL semantics — a missing attribute is NULL, not a reason
to drop the row — and the var is recorded in `:nullable-vars` so
comparison predicates can wrap it in null-guards (three-valued logic).

Exception: when the entity is a LEFT JOIN right-side var (already
registered in `:left-join-evars`), emit a plain data pattern instead.
The LEFT JOIN or-join construction (run later in translate-select)
identifies those patterns and relocates them into the matched branch,
synthesising `:__null__` via `ground` in the unmatched branch. A
get-else with the LEFT JOIN sentinel entity-id would otherwise throw.
These vars are still added to `:nullable-vars` so null-guards apply.

`:db.type/ref` columns: when `ctx`'s `:ref-targets` map has an entry
for the resolved attr, the returned var is the *target-PK value*
(e.g. for `:order/customer` → the referenced customer's
`:customer/id`), matching how a real PG FK column projects. Callers
that need the raw entity-id (only the JOIN-condition rewriter) use
`ref-eid-var!` instead.

Handles three forms of attr:
  [:db-id alias-key]        → return entity var for the alias
  [:aliased alias-key kw]   → aliased column (self-joins)
  :ns/col                   → regular column
sourceraw docstring

collect-varsclj

(collect-vars form)

Collect all logic variables (symbols starting with ?) from a form.

Collect all logic variables (symbols starting with ?) from a form.
sourceraw docstring

entity-var!clj

(entity-var! ctx alias-key)

Get or create the entity variable for a table alias.

Get or create the entity variable for a table alias.
sourceraw docstring

extract-table-infoclj

(extract-table-info table)

Extract table name and alias from a FROM clause Table.

:name is the relation's STORAGE name and :alias the name the query refers to it by; they differ for an aliased table and for any relation in *relation-namespaces*.

Extract table name and alias from a FROM clause Table.

`:name` is the relation's STORAGE name and `:alias` the name the
query refers to it by; they differ for an aliased table and for any
relation in `*relation-namespaces*`.
sourceraw docstring

fresh-var!clj

(fresh-var! ctx)

Generate a fresh logic variable ?v1, ?v2, etc.

Generate a fresh logic variable ?v1, ?v2, etc.
sourceraw docstring

make-columns-optional!clj

(make-columns-optional! ctx vars)

Convert plain data patterns for the given variable symbols to get-else. Used by COALESCE, NULLIF, CASE — functions that handle NULLs explicitly. Skips patterns whose entity var is a LEFT JOIN right-side entity var, because those patterns will be moved inside the or-join where NULL synthesis is handled by the matched/unmatched branches.

Convert plain data patterns for the given variable symbols to get-else.
Used by COALESCE, NULLIF, CASE — functions that handle NULLs explicitly.
Skips patterns whose entity var is a LEFT JOIN right-side entity var,
because those patterns will be moved inside the or-join where NULL
synthesis is handled by the matched/unmatched branches.
sourceraw docstring

make-ctxclj

(make-ctx schema
          table-aliases
          default-table
          &
          [{:keys [db parse-sql hints derived-aliases ref-targets]}])

Create a fresh translation context. The options map may carry:

  • :db — live Datahike db snapshot (for schema inherits lookup, virtual catalog resolution, subquery execution)
  • :parse-sql — recursion hook: a fn of [sql schema db] that re-enters the parser to translate inner SQL strings (IN / EXISTS subqueries). Passed by datahike.pg.sql/parse-sql at top-level ctx construction so expression translators can recurse without a cyclic namespace load. Callers that don't need subquery support can omit it.
  • :hints — {attr-ident → hint-map} from datahike.pg.schema/schema-hints. Drives the :col-overrides lookup used by resolve-column so WHERE <renamed-col> and JOIN … ON … resolve hint- mapped columns to their real attribute keywords.
Create a fresh translation context. The options map may carry:
- :db        — live Datahike db snapshot (for schema inherits lookup,
               virtual catalog resolution, subquery execution)
- :parse-sql — recursion hook: a fn of [sql schema db] that re-enters
               the parser to translate inner SQL strings (IN /
               EXISTS subqueries). Passed by `datahike.pg.sql/parse-sql`
               at top-level ctx construction so expression translators
               can recurse without a cyclic namespace load. Callers
               that don't need subquery support can omit it.
- :hints     — `{attr-ident → hint-map}` from `datahike.pg.schema/schema-hints`.
               Drives the `:col-overrides` lookup used by `resolve-column`
               so `WHERE <renamed-col>` and `JOIN … ON …` resolve hint-
               mapped columns to their real attribute keywords.
sourceraw docstring

materialize-arg!clj

(materialize-arg! ctx arg)

If arg is a compound form (seq), bind it to a fresh var via a function-binding clause and return the var. Otherwise return arg.

If arg is a compound form (seq), bind it to a fresh var via a
function-binding clause and return the var. Otherwise return arg.
sourceraw docstring

null-guard-clausesclj

(null-guard-clauses ctx vars)

Return a vector of predicate clauses that assert none of the supplied vars is the :__null__ sentinel. Used to make comparison predicates null-safe per SQL's three-valued logic: col op V when col IS NULL yields UNKNOWN, and WHERE treats UNKNOWN as FALSE (row filtered).

Only vars that were emitted via col-var! (and therefore recorded in :nullable-vars) need guarding — other vars cannot be :__null__.

Return a vector of predicate clauses that assert none of the supplied
vars is the `:__null__` sentinel. Used to make comparison predicates
null-safe per SQL's three-valued logic: `col op V` when col IS NULL
yields UNKNOWN, and WHERE treats UNKNOWN as FALSE (row filtered).

Only vars that were emitted via col-var! (and therefore recorded in
`:nullable-vars`) need guarding — other vars cannot be `:__null__`.
sourceraw docstring

ref-eid-var!clj

(ref-eid-var! ctx attr)

Get/create the logic variable bound to the raw entity-id a :db.type/ref attribute holds, bypassing the SQL-projection dereference that col-var! applies for ref columns.

Used only by the JOIN-condition rewriter in translate-join, which unifies the right alias's entity-var with the ref's value (the target entity-id) — an optimization that turns JOIN c ON p.fk = c.pk into a single direct entity binding instead of two passes (deref + value equality).

Forms accepted: same as col-var!. For non-ref attrs this returns the same var that col-var! does — there's nothing to dereference.

Cached separately from col-var! (key [alias-key attr :__eid__]) so projection sites and JOIN sites can both fetch their respective binding without invalidating each other.

Get/create the logic variable bound to the *raw entity-id* a
`:db.type/ref` attribute holds, bypassing the SQL-projection
dereference that `col-var!` applies for ref columns.

Used only by the JOIN-condition rewriter in `translate-join`, which
unifies the right alias's entity-var with the ref's value (the
target entity-id) — an optimization that turns `JOIN c ON p.fk =
c.pk` into a single direct entity binding instead of two passes
(deref + value equality).

Forms accepted: same as `col-var!`. For non-ref attrs this returns
the same var that `col-var!` does — there's nothing to dereference.

Cached separately from `col-var!` (key `[alias-key attr :__eid__]`)
so projection sites and JOIN sites can both fetch their respective
binding without invalidating each other.
sourceraw docstring

resolve-columnclj

(resolve-column col table-aliases default-table)
(resolve-column col table-aliases default-table col-overrides)
(resolve-column col table-aliases default-table col-overrides derived-aliases)
(resolve-column col
                table-aliases
                default-table
                col-overrides
                derived-aliases
                ci)

Resolve a column reference to a Datahike attribute keyword. Uses table-aliases map {alias → table-name} and schema-tables for lookup. Handles table inheritance: if a column doesn't exist in the child table's namespace but does exist in an inherited parent's namespace, resolves to the parent namespace (PostgreSQL INHERITS semantics).

Optional 4th arg col-overrides is {table-name → {hinted-col-name → attr-ident}} from make-ctx; when a column name appears there, the override wins over the default (keyword table-name col-name) construction. Makes :datahike.pg/column renames resolve on the read side too.

Returns either: [:db-id alias-key] — for db_id references [:aliased alias-key kw] — for aliased column references (self-joins) :ns/col — for regular column references

Resolve a column reference to a Datahike attribute keyword.
Uses table-aliases map {alias → table-name} and schema-tables for lookup.
Handles table inheritance: if a column doesn't exist in the child table's
namespace but does exist in an inherited parent's namespace, resolves to
the parent namespace (PostgreSQL INHERITS semantics).

Optional 4th arg `col-overrides` is `{table-name → {hinted-col-name → attr-ident}}`
from `make-ctx`; when a column name appears there, the override wins
over the default `(keyword table-name col-name)` construction. Makes
`:datahike.pg/column` renames resolve on the read side too.

Returns either:
  [:db-id alias-key]       — for db_id references
  [:aliased alias-key kw]  — for aliased column references (self-joins)
  :ns/col                  — for regular column references
sourceraw docstring

resolve-inherited-attrclj

(resolve-inherited-attr attr schema db)

For INHERITS support: check if an attribute exists in the table's schema. If not, walk up the inheritance chain to find it in a parent. Returns the resolved keyword (possibly in parent namespace) or the original.

For INHERITS support: check if an attribute exists in the table's schema.
If not, walk up the inheritance chain to find it in a parent.
Returns the resolved keyword (possibly in parent namespace) or the original.
sourceraw docstring

unify-inner-equijoin!clj

(unify-inner-equijoin! ctx l-resolved r-resolved)

Try to translate an INNER-JOIN equality between two plain columns as shared-variable data patterns (see comment above). Returns true when handled; nil when the caller must fall back to the predicate path (ref-deref columns, derived tables, expressions, both vars already bound).

Try to translate an INNER-JOIN equality between two plain columns as
shared-variable data patterns (see comment above). Returns true when
handled; nil when the caller must fall back to the predicate path
(ref-deref columns, derived tables, expressions, both vars already
bound).
sourceraw docstring

validate-column!clj

(validate-column! ctx attr)

Raise 42703 when attr names a column that does not exist on a table that does.

PostgreSQL rejects an unknown column at parse-analyze. Translating it into the same get-else … :__null__ binding a real column gets meant SELECT nosuchcol FROM t returned a row of NULLs and WHERE nosuchcol = 1 returned no rows — a typo reading as data.

Every condition below is a case where the name might legitimately resolve to something other than a column of this table, and each one was learned from a false positive rather than reasoned out in advance:

  • exact-schema? — under :schema-flexibility :read a real column need not be in the schema at all;
  • catalog-attr? — driver introspection probes columns we don't materialise;
  • :derived-aliases — a derived table's columns live in a speculative schema this ctx may not carry;
  • relation-in-scope? — a bare name matching a table alias is a whole-row reference in PostgreSQL;
  • and the table itself must be known, since a namespace with no attributes at all is an unknown or unmaterialised RELATION, which is a different error raised elsewhere.

Must run AFTER inheritance resolution — an INHERITS child resolves into its parent's namespace — which is why callers pass the output of attr-of rather than the raw attribute.

Raise 42703 when `attr` names a column that does not exist on a table
that does.

PostgreSQL rejects an unknown column at parse-analyze. Translating it
into the same `get-else … :__null__` binding a real column gets meant
`SELECT nosuchcol FROM t` returned a row of NULLs and
`WHERE nosuchcol = 1` returned no rows — a typo reading as data.

Every condition below is a case where the name might legitimately
resolve to something other than a column of this table, and each one
was learned from a false positive rather than reasoned out in
advance:

  - `exact-schema?` — under :schema-flexibility :read a real column
    need not be in the schema at all;
  - `catalog-attr?` — driver introspection probes columns we don't
    materialise;
  - `:derived-aliases` — a derived table's columns live in a
    speculative schema this ctx may not carry;
  - `relation-in-scope?` — a bare name matching a table alias is a
    whole-row reference in PostgreSQL;
  - and the table itself must be known, since a namespace with no
    attributes at all is an unknown or unmaterialised RELATION, which
    is a different error raised elsewhere.

Must run AFTER inheritance resolution — an INHERITS child resolves
into its parent's namespace — which is why callers pass the output of
`attr-of` rather than the raw attribute.
sourceraw docstring

with-resolved-attrclj

(with-resolved-attr ctx resolved)

resolved with its attribute replaced by attr-of, preserving the shape. Use when the caller needs to pass the whole resolve-column result onward rather than just the attribute.

`resolved` with its attribute replaced by `attr-of`, preserving the
shape. Use when the caller needs to pass the whole resolve-column
result onward rather than just the attribute.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close