Expression + predicate translation.
Converts JSqlParser expression nodes (arithmetic, comparisons, CASE, CAST, function calls, IN / BETWEEN / EXISTS subqueries, JSON operators, …) into Datalog find-element expressions and where-clause predicates.
Two families of entry points:
Expression side (translate-expr): evaluates to a value or
binds a var. Produces ?v symbols via materialization when the
result feeds into a SELECT projection, ORDER BY key, aggregate
argument, or outer SQL function call.
Predicate side (translate-predicate): produces a vec of
Datalog where-clauses that filter rows. Recursively handles
AND / OR / NOT, comparison ops with three-valued-logic null
guards, IN-list / IN-subquery, BETWEEN, IS NULL, EXISTS and
NOT EXISTS (including the correlated-subquery path with
entity-var unification + not-join fallback), LIKE / ILIKE,
JSON operators (@>, <@, ?, ?|, ?&), and regex match.
translate-expr and translate-predicate are mutually recursive
through translate-case-expr (WHEN predicates) and
translate-comparison (operand expressions). Everything lives in
one namespace so the recursion is a regular in-ns declare and
doesn't cross any ns boundary.
Back-edge to sql.clj: the EXISTS / IN-subquery branches need to
recursively re-enter the parser for sub-SQL. Rather than import
datahike.pg.sql (which would create a load cycle), we receive
parse-sql through the ctx's :parse-sql slot — populated by
datahike.pg.sql/parse-sql at top-level ctx construction and
inherited by sub-ctxs (for CTE / correlated subquery / derived
tables) when they copy from their outer ctx.
Every translate-* fn takes a ctx first-argument (built by
datahike.pg.sql.ctx/make-ctx) and returns either a value, a
fresh var, or a vector of where-clauses (for the predicate side).
Side effects flow through the ctx's atoms (:where-clauses,
:in-args, :param-placeholders, :entity-vars, :col->var).
Expression + predicate translation. Converts JSqlParser expression nodes (arithmetic, comparisons, CASE, CAST, function calls, IN / BETWEEN / EXISTS subqueries, JSON operators, …) into Datalog find-element expressions and where-clause predicates. Two families of entry points: - **Expression side** (`translate-expr`): evaluates to a value or binds a var. Produces `?v` symbols via materialization when the result feeds into a SELECT projection, ORDER BY key, aggregate argument, or outer SQL function call. - **Predicate side** (`translate-predicate`): produces a vec of Datalog where-clauses that filter rows. Recursively handles AND / OR / NOT, comparison ops with three-valued-logic null guards, IN-list / IN-subquery, BETWEEN, IS NULL, EXISTS and NOT EXISTS (including the correlated-subquery path with entity-var unification + not-join fallback), LIKE / ILIKE, JSON operators (@>, <@, ?, ?|, ?&), and regex match. `translate-expr` and `translate-predicate` are mutually recursive through `translate-case-expr` (WHEN predicates) and `translate-comparison` (operand expressions). Everything lives in one namespace so the recursion is a regular in-ns `declare` and doesn't cross any ns boundary. Back-edge to sql.clj: the EXISTS / IN-subquery branches need to recursively re-enter the parser for sub-SQL. Rather than import `datahike.pg.sql` (which would create a load cycle), we receive `parse-sql` through the ctx's `:parse-sql` slot — populated by `datahike.pg.sql/parse-sql` at top-level ctx construction and inherited by sub-ctxs (for CTE / correlated subquery / derived tables) when they copy from their outer ctx. Every translate-* fn takes a ctx first-argument (built by `datahike.pg.sql.ctx/make-ctx`) and returns either a value, a fresh var, or a vector of where-clauses (for the predicate side). Side effects flow through the ctx's atoms (`:where-clauses`, `:in-args`, `:param-placeholders`, `:entity-vars`, `:col->var`).
(flatten-json-chain je)Flatten a JsonExpression into {:base Expression :chain [[key op-str] ...]} pairs. JSqlParser encodes chained access recursively: data->'a'->>'b' is JsonExpression(base=data, idents=[JsonExpression(base='a', idents=['b'], ops=[->>])], ops=[->]) We flatten to {:base data-Column, :chain [['a' '->'] ['b' '->>']]}.
For a single-step access col->>'key': JsonExpression(base=col, idents=['key'], ops=[->>]) → {:base col-Column, :chain [['key' '->>']]}
Flatten a JsonExpression into {:base Expression :chain [[key op-str] ...]} pairs.
JSqlParser encodes chained access recursively: data->'a'->>'b' is
JsonExpression(base=data, idents=[JsonExpression(base='a', idents=['b'], ops=[->>])], ops=[->])
We flatten to {:base data-Column, :chain [['a' '->'] ['b' '->>']]}.
For a single-step access col->>'key':
JsonExpression(base=col, idents=['key'], ops=[->>])
→ {:base col-Column, :chain [['key' '->>']]}(interpret-form form bindings)Interpret a Clojure-like form against a variable bindings map. Handles: arithmetic (+, -, *, /, rem), comparisons (>, <, >=, <=, =, not=), boolean (and, or, not), nil checks (nil?, some?), identity, and str. Constants and unbound symbols pass through unchanged.
Interpret a Clojure-like form against a variable bindings map. Handles: arithmetic (+, -, *, /, rem), comparisons (>, <, >=, <=, =, not=), boolean (and, or, not), nil checks (nil?, some?), identity, and str. Constants and unbound symbols pass through unchanged.
(parse-timestamp-string s)Parse a timestamp string in various formats to java.util.Date. Handles: ISO-8601, PostgreSQL/JDBC timestamps with various timezone formats, date-only strings, and date+offset (e.g. '2000-09-07 -07'). Returns the original string if all parsing attempts fail.
Parse a timestamp string in various formats to java.util.Date. Handles: ISO-8601, PostgreSQL/JDBC timestamps with various timezone formats, date-only strings, and date+offset (e.g. '2000-09-07 -07'). Returns the original string if all parsing attempts fail.
(translate-binary-arith ctx expr op-sym)Translate a binary arithmetic expression. Materializes sub-expression operands. When operands are aggregate markers, returns a compound-agg descriptor instead of a Datalog form.
Uses fns/null-safe arithmetic so col + 1 evaluates to :__null__ when
col is NULL, matching SQL. The compound-agg path keeps the raw
op-sym — aggregate compound evaluation is numeric-only and runs
server-side after the query.
Translate a binary arithmetic expression. Materializes sub-expression operands. When operands are aggregate markers, returns a compound-agg descriptor instead of a Datalog form. Uses fns/null-safe arithmetic so `col + 1` evaluates to `:__null__` when `col` is NULL, matching SQL. The compound-agg path keeps the raw op-sym — aggregate compound evaluation is numeric-only and runs server-side after the query.
(translate-case-expr ctx case-expr)Translate a CASE WHEN expression by compiling a Clojure function and passing it as an :in parameter. Returns the result variable.
Supports both searched CASE (CASE WHEN cond THEN ...) and simple CASE (CASE expr WHEN val THEN ...).
The CASE function takes all referenced variables as arguments and evaluates the cond expression at query time.
Translate a CASE WHEN expression by compiling a Clojure function and passing it as an :in parameter. Returns the result variable. Supports both searched CASE (CASE WHEN cond THEN ...) and simple CASE (CASE expr WHEN val THEN ...). The CASE function takes all referenced variables as arguments and evaluates the cond expression at query time.
(translate-cast-expr ctx cast-expr)Translate a CAST expression to a Datalog function binding. For constant values, performs the cast at translation time. For variable references, adds a runtime cast binding.
Translate a CAST expression to a Datalog function binding. For constant values, performs the cast at translation time. For variable references, adds a runtime cast binding.
(translate-comparison ctx op left right)Translate a binary comparison to Datalog predicate clauses.
Per SQL three-valued logic, col op V when either operand is NULL
yields UNKNOWN, which WHERE treats as FALSE (row filtered). Our
nullable vars (from ctx/col-var!) may be bound to the :__null__
sentinel when the underlying attribute is absent. We emit an explicit
(not= v :__null__) guard for each nullable operand so rows with
NULL values are filtered regardless of the operator's own behaviour
on the sentinel — =/contains? would already filter them, but
not=/</>/<=/>= would either pass them through (wrong) or
throw (for numeric comparisons).
Returns a vector of clause forms; each appearing as its own entry in :where is AND-ed implicitly by Datalog.
Translate a binary comparison to Datalog predicate clauses. Per SQL three-valued logic, `col op V` when either operand is NULL yields UNKNOWN, which WHERE treats as FALSE (row filtered). Our nullable vars (from ctx/col-var!) may be bound to the `:__null__` sentinel when the underlying attribute is absent. We emit an explicit `(not= v :__null__)` guard for each nullable operand so rows with NULL values are filtered regardless of the operator's own behaviour on the sentinel — `=`/`contains?` would already filter them, but `not=`/`<`/`>`/`<=`/`>=` would either pass them through (wrong) or throw (for numeric comparisons). Returns a vector of clause forms; each appearing as its own entry in :where is AND-ed implicitly by Datalog.
(translate-expr ctx expr)Translate a JSqlParser Expression to a value, variable, or predicate form. Returns a Datalog-compatible value or variable symbol.
Translate a JSqlParser Expression to a value, variable, or predicate form. Returns a Datalog-compatible value or variable symbol.
(translate-function-call ctx f)Translate a non-aggregate SQL function to a Datalog function binding. Adds the binding clause to where-clauses and returns the result variable.
Translate a non-aggregate SQL function to a Datalog function binding. Adds the binding clause to where-clauses and returns the result variable.
(translate-predicate ctx expr)Translate a JSqlParser WHERE expression to Datalog :where clauses. Returns a vector of clause forms.
Translate a JSqlParser WHERE expression to Datalog :where clauses. Returns a vector of clause forms.
(translate-predicate-expr ctx expr)Translate a SQL predicate expression into a Clojure boolean form suitable for use inside a cond binding. Unlike translate-predicate which returns Datalog where clauses, this returns a single form.
Translate a SQL predicate expression into a Clojure boolean form suitable for use inside a cond binding. Unlike translate-predicate which returns Datalog where clauses, this returns a single form.
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 |