Liking cljdoc? Tell your friends :D

datahike.pg.sql.params

Prepared-statement parameter substitution and PG OID inference.

Three concerns live together here because they all deal with the ? / $N placeholder lifecycle:

  1. ParamRef record + param-ref?. Emitted by translators at Parse time inside tx-data / query structures, replaced by real values at Execute time via substitute-params.

  2. *bound-params* dynamic var: when bound to a 1-indexed vector of resolved values, translator branches (e.g. the JdbcParameter expression) resolve placeholders in-line instead of emitting ParamRef. This lets the same translator body serve both prepared-Parse (returns structured AST with ParamRefs) and re-translation-during-Execute (returns fully-bound form).

  3. OID inference (infer-param-oid-for-column, insert-param-oids, update-param-oids, where-param-oids) — walks a JSqlParser AST, maps each placeholder index to the PG type OID of the column it's bound against. Consumed by describeParams to populate the ParameterDescription message so pgjdbc / psycopg2 / etc. size their binary binds correctly.

*parse-db* is bound by parse-sql (higher-level) to the current db snapshot so OID inference can consult :pg/type metadata that (:schema db) doesn't surface.

Prepared-statement parameter substitution and PG OID inference.

Three concerns live together here because they all deal with the
`?` / `$N` placeholder lifecycle:

1. ParamRef record + `param-ref?`. Emitted by translators at Parse
   time inside tx-data / query structures, replaced by real values
   at Execute time via `substitute-params`.

2. `*bound-params*` dynamic var: when bound to a 1-indexed vector
   of resolved values, translator branches (e.g. the JdbcParameter
   expression) resolve placeholders in-line instead of emitting
   ParamRef. This lets the same translator body serve both
   prepared-Parse (returns structured AST with ParamRefs) and
   re-translation-during-Execute (returns fully-bound form).

3. OID inference (`infer-param-oid-for-column`,
   `insert-param-oids`, `update-param-oids`, `where-param-oids`) —
   walks a JSqlParser AST, maps each placeholder index to the PG
   type OID of the column it's bound against. Consumed by
   describeParams to populate the ParameterDescription message so
   pgjdbc / psycopg2 / etc. size their binary binds correctly.

`*parse-db*` is bound by parse-sql (higher-level) to the current
db snapshot so OID inference can consult :pg/type metadata that
(:schema db) doesn't surface.
raw docstring

*bound-params*clj

Dynamically bound at Execute time to a 1-indexed vector (or nil if no params). When set, translate-expr's JdbcParameter branch resolves placeholders to concrete values in-line; otherwise (Parse time) it emits ?pN in-param vars and records the index in ctx.

This split lets the same translator body serve both prepared-Parse and re-translation-during-Execute (UPDATE/DELETE keep where-expr as a JSqlParser AST and re-translate on each Execute).

Dynamically bound at Execute time to a 1-indexed vector (or nil if
no params). When set, translate-expr's JdbcParameter branch resolves
placeholders to concrete values in-line; otherwise (Parse time) it
emits `?pN` in-param vars and records the index in ctx.

This split lets the same translator body serve both prepared-Parse
and re-translation-during-Execute (UPDATE/DELETE keep where-expr as
a JSqlParser AST and re-translate on each Execute).
raw docstring

*from-bindings*clj

When bound (by build-update-tx handling UPDATE ... FROM (VALUES ...)), a map {alias-name → {col-name → literal}} used by the Column branches of translate-expr and eval-update-expr to substitute row-level values for references like __tmp.col to the VALUES alias.

When bound (by build-update-tx handling UPDATE ... FROM (VALUES ...)),
a map {alias-name → {col-name → literal}} used by the Column branches
of translate-expr and eval-update-expr to substitute row-level values
for references like `__tmp.col` to the VALUES alias.
raw docstring

*parse-db*clj

Bound by parse-sql to the live db snapshot so downstream helpers (e.g. pg-type-of-attr) can consult Datahike for attribute metadata that :schema doesn't surface (:pg/type and friends). Not meant to flow beyond the parse phase — clear it before dispatching to the execute path.

Bound by parse-sql to the live db snapshot so downstream helpers
(e.g. pg-type-of-attr) can consult Datahike for attribute metadata
that :schema doesn't surface (:pg/type and friends). Not meant to
flow beyond the parse phase — clear it before dispatching to the
execute path.
raw docstring

*parse-sql*clj

Bound by parse-sql to itself so top-level translate-* entries in datahike.pg.sql.stmt can seed :parse-sql into make-ctx without a cyclic require on sql.clj. Downstream expression translators call it to re-parse inner SQL strings for IN / EXISTS subqueries.

Bound by parse-sql to itself so top-level translate-* entries in
datahike.pg.sql.stmt can seed `:parse-sql` into make-ctx without a
cyclic require on sql.clj. Downstream expression translators call
it to re-parse inner SQL strings for IN / EXISTS subqueries.
raw docstring

ast-param-indicesclj

(ast-param-indices node)

Recursively walk a JSqlParser AST, returning a sorted set of 1-based parameter indices (? / $N placeholders).

Uses reflection to traverse any node's zero-arg getters. This is called once per Parse to determine how many parameters a prepared statement expects — the cost is bounded by AST size, which is tiny compared to query execution.

Recursively walk a JSqlParser AST, returning a sorted set of
1-based parameter indices (`?` / `$N` placeholders).

Uses reflection to traverse any node's zero-arg getters. This is
called once per Parse to determine how many parameters a prepared
statement expects — the cost is bounded by AST size, which is tiny
compared to query execution.
raw docstring

collect-table-aliasesclj

(collect-table-aliases from-item joins)

Given a FROM item and a sequence of JOINs, build a map {alias-name → real-table-name}. Tables without an alias still get an entry mapping the table name to itself (so lookup is uniform).

Given a FROM item and a sequence of JOINs, build a map
`{alias-name → real-table-name}`. Tables without an alias still get
an entry mapping the table name to itself (so lookup is uniform).
raw docstring

infer-param-oid-for-columnclj

(infer-param-oid-for-column schema table-ns col-name)
(infer-param-oid-for-column schema table-ns col-name db)

Given a schema and a (table-namespace, column-name), return the PG OID that matches the attribute's :db/valueType, or nil if we don't know. Used by describeParams to populate ParameterDescription so drivers can size buffers correctly for typed INSERT/UPDATE/WHERE placeholders.

:pg/type on the attr — recorded by translate-create-table for types that don't have a 1:1 Datahike mapping (date/time/timestamp all collapse to :db.type/instant; jsonb vs json both use :db.type/string) — takes precedence so we round-trip the original SQL type. Without this, pgjdbc Describes a DATE column's param as timestamp (1114) and rejects subsequent binds as "Can't change resolved type for param: 1 from 1082 to 1114".

db is optional — pass nil and the :pg/type refinement is skipped. Accepts schema either as the map {ident → props} returned by pgs/schema-of, or as a seq of {:db/ident ... :db/valueType ...} entries (the :db/add transaction form).

Given a schema and a (table-namespace, column-name), return the PG
OID that matches the attribute's :db/valueType, or nil if we don't
know. Used by describeParams to populate ParameterDescription so
drivers can size buffers correctly for typed INSERT/UPDATE/WHERE
placeholders.

:pg/type on the attr — recorded by translate-create-table for types
that don't have a 1:1 Datahike mapping (date/time/timestamp all
collapse to :db.type/instant; jsonb vs json both use :db.type/string)
— takes precedence so we round-trip the original SQL type. Without
this, pgjdbc Describes a DATE column's param as `timestamp` (1114)
and rejects subsequent binds as "Can't change resolved type for
param: 1 from 1082 to 1114".

`db` is optional — pass nil and the :pg/type refinement is skipped.
Accepts schema either as the map {ident → props} returned by
`pgs/schema-of`, or as a seq of `{:db/ident ... :db/valueType ...}`
entries (the `:db/add` transaction form).
raw docstring

insert-param-oidsclj

(insert-param-oids insert schema)

Walk an INSERT AST: for each VALUES (..., ?, ...) row, positional column i → attribute type → PG OID. Returns a map {param-index → oid}.

Only covers the flat single-row / multi-row VALUES case — which is what JDBC setObject/setString produces for the common ORM path.

Walk an INSERT AST: for each `VALUES (..., ?, ...)` row, positional
column i → attribute type → PG OID. Returns a map {param-index → oid}.

Only covers the flat single-row / multi-row VALUES case — which is
what JDBC setObject/setString produces for the common ORM path.
raw docstring

param-ref?clj

(param-ref? x)

True when x is a parameter placeholder emitted by translate-* during prepared-statement parsing. Appears in tx-data (INSERT/UPDATE VALUES) and nested inside parsed structures that the handler walks at Bind/ Execute time to substitute real values.

True when `x` is a parameter placeholder emitted by translate-* during
prepared-statement parsing. Appears in tx-data (INSERT/UPDATE VALUES)
and nested inside parsed structures that the handler walks at Bind/
Execute time to substitute real values.
raw docstring

substitute-paramsclj

(substitute-params x bound)

Walk x replacing every ParamRef with the corresponding bound value from bound (1-indexed: (->ParamRef 1)(bound 1) ... so bound is either a vector (we nth by idx-1) or a function idx→value). Leaves everything else untouched.

The wire layer calls this at Execute time to resolve placeholders inside INSERT tx-data.

Maps with nil values after substitution have those keys dissoc'd. An INSERT like INSERT INTO t (a, b) VALUES (?, ?) with setString(1, null) ends up as {:t/a nil :t/b "x"} here — d/transact rejects [:db/add eid :t/a nil] as :transact/syntax, but the correct PG behaviour for a nullable column is to simply not assert the attribute. The translate-time row-builder already drops nil literals (NullValue), but those land as ParamRef sentinels at parse time and only resolve to nil here.

Walk `x` replacing every ParamRef with the corresponding bound value
from `bound` (1-indexed: `(->ParamRef 1)` → `(bound 1)` ... so `bound`
is either a vector (we nth by idx-1) or a function idx→value).
Leaves everything else untouched.

The wire layer calls this at Execute time to resolve placeholders
inside INSERT tx-data.

Maps with nil values after substitution have those keys dissoc'd.
An INSERT like `INSERT INTO t (a, b) VALUES (?, ?)` with
`setString(1, null)` ends up as `{:t/a nil :t/b "x"}` here —
`d/transact` rejects `[:db/add eid :t/a nil]` as `:transact/syntax`,
but the correct PG behaviour for a nullable column is to simply
not assert the attribute. The translate-time row-builder already
drops nil literals (NullValue), but those land as ParamRef sentinels
at parse time and only resolve to nil here.
raw docstring

unquote-identclj

(unquote-ident s)

Strip SQL double-quote delimiters from an identifier. PostgreSQL uses double quotes for case-sensitive or reserved-word identifiers: '"MyTable"' → 'MyTable', 'my_table' → 'my_table'

Strip SQL double-quote delimiters from an identifier.
PostgreSQL uses double quotes for case-sensitive or reserved-word
identifiers: '"MyTable"' → 'MyTable', 'my_table' → 'my_table'
raw docstring

update-param-oidsclj

(update-param-oids update schema)

Walk an UPDATE AST: for each SET col = ?, map param index to the column attribute's PG OID.

Walk an UPDATE AST: for each SET col = ?, map param index to the
column attribute's PG OID.
raw docstring

where-param-oidsclj

(where-param-oids expr schema default-table-ns)
(where-param-oids expr schema default-table-ns aliases)

Walk an expression tree (a WHERE clause) and for each comparison col = ? / ? = col / col IN (?,?) / col BETWEEN ? AND ?, map the ? param index to the column's PG OID. Best-effort.

default-table-ns is used when a column has no explicit table qualifier. aliases is an optional {alias-name → real-table-name} map (derived by the caller from FROM/JOIN clauses) so JOIN t a ON a.col = ? resolves a to t's real schema.

Walk an expression tree (a WHERE clause) and for each comparison
`col = ?` / `? = col` / `col IN (?,?)` / `col BETWEEN ? AND ?`,
map the `?` param index to the column's PG OID. Best-effort.

`default-table-ns` is used when a column has no explicit table
qualifier. `aliases` is an optional `{alias-name → real-table-name}`
map (derived by the caller from FROM/JOIN clauses) so `JOIN t a ON
a.col = ?` resolves `a` to `t`'s real schema.
raw 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