SQL → Datahike Datalog translator.
Parses SQL strings using JSqlParser and translates the AST into Datahike
Datalog queries that can be executed by datahike.api/q.
The core mapping: attribute namespace prefixes become virtual table names. :person/name → table 'person', column 'name' :person/age → table 'person', column 'age'
Main entry points: (parse-sql sql schema) → {:type :select :query {...} :args [...]} | {:type :insert :tx-data [...]} | {:type :system :result QueryResult} | {:type :error :message str}
SQL → Datahike Datalog translator.
Parses SQL strings using JSqlParser and translates the AST into Datahike
Datalog queries that can be executed by `datahike.api/q`.
The core mapping: attribute namespace prefixes become virtual table names.
:person/name → table 'person', column 'name'
:person/age → table 'person', column 'age'
Main entry points:
(parse-sql sql schema) → {:type :select :query {...} :args [...]}
| {:type :insert :tx-data [...]}
| {:type :system :result QueryResult}
| {:type :error :message str}Server-wide JSqlParser AST cache, keyed on the preprocessed SQL string. Tests can rebind to an isolated map; nil disables caching.
Server-wide JSqlParser AST cache, keyed on the preprocessed SQL string. Tests can rebind to an isolated map; nil disables caching.
Tests can rebind this to an isolated java.util.Map to keep their
data from polluting the global cache (or vice versa). Nil disables
caching entirely. Defaults to the server-wide cache.
Tests can rebind this to an isolated `java.util.Map` to keep their data from polluting the global cache (or vice versa). Nil disables caching entirely. Defaults to the server-wide cache.
Server-wide cache for parse-sql results. Tests can rebind to an isolated map; nil disables caching entirely.
Server-wide cache for parse-sql results. Tests can rebind to an isolated map; nil disables caching entirely.
(cte-namespace n cte-name)The synthetic storage namespace for the nth CTE named cte-name.
Deterministic — a pure function of the SQL text — because the parse
caches key on [sql (hash schema)], and a gensym/nanoTime namespace
would change the enriched schema on every parse and turn every CTE
query into a total cache miss.
The __cte prefix is reserved: datahike.pg.schema/internal-attr?
filters it so a CTE can never surface as a table in pg_class or
information_schema.
The synthetic storage namespace for the `n`th CTE named `cte-name`. Deterministic — a pure function of the SQL text — because the parse caches key on `[sql (hash schema)]`, and a gensym/nanoTime namespace would change the enriched schema on every parse and turn every CTE query into a total cache miss. The `__cte` prefix is reserved: `datahike.pg.schema/internal-attr?` filters it so a CTE can never surface as a table in pg_class or information_schema.
(enrich-db-with-catalogs db schema catalog-names)Materialise the given catalog tables' schema + data on top of db,
returning the enriched db (its :schema carries the catalog attrs).
Returns db unchanged when catalog-names is empty.
Only catalog schema fragments are cached. Catalog rows are derived from
this exact input DB and materialized on top of it on every call. Thus a
catalog join cannot accidentally inherit another call's user rows.
schema is db's user schema.
Materialise the given catalog tables' schema + data on top of `db`, returning the enriched db (its `:schema` carries the catalog attrs). Returns `db` unchanged when `catalog-names` is empty. Only catalog schema fragments are cached. Catalog rows are derived from this exact input DB and materialized on top of it on every call. Thus a catalog join cannot accidentally inherit another call's user rows. `schema` is `db`'s user schema.
(invalidate-catalog-cache!)Clear cached catalog schema fragments. Rows and enriched database snapshots are not cached: catalog extensions may read arbitrary current user data.
Clear cached catalog schema fragments. Rows and enriched database snapshots are not cached: catalog extensions may read arbitrary current user data.
(invalidate-parse-cache!)Clear the server-wide parse-sql result cache. Called from every DDL exec branch (via server/invalidate-schema-cache!). Exact cache keys also detect native catalog changes. The AST cache is untouched: JSqlParser output depends only on SQL text.
Clear the server-wide parse-sql result cache. Called from every DDL exec branch (via server/invalidate-schema-cache!). Exact cache keys also detect native catalog changes. The AST cache is untouched: JSqlParser output depends only on SQL text.
(parse-sql sql schema)(parse-sql sql schema db)Parse a SQL statement and return a translation result.
Returns one of: {:type :select :query <datalog-map> :find-aliases [...] ...} {:type :insert :tx-data [...] :count N} {:type :update :table str :ns str :assignments [...] :where-expr expr} {:type :delete :table str :ns str :where-expr expr} {:type :ddl-create :tx-data [...]} {:type :system :system-type keyword} {:type :error :message str}
Optional db parameter enables subquery execution during translation.
Three cache levels stack:
*parse-cache*) — for SQL strings re-issued
verbatim (pgjdbc unnamed prepared statements, ORM select-by-id).INSERT INTO t [(cols)] VALUES (lit, …) shapes the literals are
captured, the SQL is normalised to (? , …), and the resulting
AST is reused across all rows of the same shape. Translation
still runs per row with *bound-params* bound so JdbcParameter
nodes resolve to concrete values inline (no ParamRef closure
captures).*ast-cache*) — covers everything else that
hits JSqlParser, repeated or not.Parse a SQL statement and return a translation result.
Returns one of:
{:type :select :query <datalog-map> :find-aliases [...] ...}
{:type :insert :tx-data [...] :count N}
{:type :update :table str :ns str :assignments [...] :where-expr expr}
{:type :delete :table str :ns str :where-expr expr}
{:type :ddl-create :tx-data [...]}
{:type :system :system-type keyword}
{:type :error :message str}
Optional db parameter enables subquery execution during translation.
Three cache levels stack:
- **Result cache** (`*parse-cache*`) — for SQL strings re-issued
verbatim (pgjdbc unnamed prepared statements, ORM select-by-id).
- **Lexical INSERT-VALUES templating** + AST cache — for `INSERT
INTO t [(cols)] VALUES (lit, …)` shapes the literals are
captured, the SQL is normalised to `(? , …)`, and the resulting
AST is reused across all rows of the same shape. Translation
still runs per row with `*bound-params*` bound so JdbcParameter
nodes resolve to concrete values inline (no ParamRef closure
captures).
- **AST cache** (`*ast-cache*`) — covers everything else that
hits JSqlParser, repeated or not.(simple-query-param-error sql)An {:type :error} map when sql uses a $N placeholder in the
SIMPLE query protocol, else nil.
Simple Query has no Bind step, so there is nothing a placeholder
could refer to and PostgreSQL raises 42P02. We used to translate the
statement anyway and let the unresolved placeholder reach the client:
SELECT $1 answered with the internal ParamRef record rendered as
{"idx":1} — leaking a representation detail as if it were data.
Only reachable from the simple path; the extended path resolves
placeholders at Bind. Gated on an indexOf so statements without a
$ never pay for tokenising, and driven off the tokeniser's :param
classification so a $ inside a string, a dollar-quoted body or a
quoted identifier doesn't count.
An `{:type :error}` map when `sql` uses a `$N` placeholder in the
SIMPLE query protocol, else nil.
Simple Query has no Bind step, so there is nothing a placeholder
could refer to and PostgreSQL raises 42P02. We used to translate the
statement anyway and let the unresolved placeholder reach the client:
`SELECT $1` answered with the internal ParamRef record rendered as
`{"idx":1}` — leaking a representation detail as if it were data.
Only reachable from the simple path; the extended path resolves
placeholders at Bind. Gated on an indexOf so statements without a
`$` never pay for tokenising, and driven off the tokeniser's `:param`
classification so a `$` inside a string, a dollar-quoted body or a
quoted identifier doesn't count.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 |