ChDriver. Params
(ch_driver v0.1.0)
Copy Markdown
Turns an Elixir value into a ClickHouse query parameter: the declared
{name:Type} type name, the unquoted/unescaped literal text, and the
wire-level escaping depth that text needs.
This is the single place that mapping lives -- ChDriver.Protocol's
encode_query/2 calls text/1 and escape_rounds/1 to build the wire
bytes for a parameter's value, and
Ecto.Adapters.ClickHouse.Connection calls type/1 to build the
matching {name:Type} placeholder text. Keeping both halves of the
mapping in one module (rather than hand-synced across the two projects)
is what guarantees a value's declared type and its rendered text always
agree.
Summary
Functions
Turns an Elixir term into {type, text, rounds} in one call: the
ClickHouse type name for its {name:Type} placeholder (see type/1),
the literal text for its value (see text/1), and the escaping depth
that text needs on the wire (see escape_rounds/1).
The number of backslash/quote-escaping rounds the wire encoder
(ChDriver.Protocol.encode_query/2) must apply to bind value for it
to round-trip -- 1 for a list (the Array literal text text/1
renders for it already has its string elements \'-escaped once, see
array_element_text/1), 2 for everything else.
Renders an Elixir term as the unquoted, unescaped ClickHouse literal
text a query parameter's value should carry -- the same text you'd
type after a CAST(..., 'Type') for that value. ChDriver.Protocol's
encode_query/2 applies the wire-level quoting/escaping on top of this
(see quote_param_value/2); this function only handles turning the
value itself into ClickHouse literal syntax.
Maps an Elixir runtime value to the ClickHouse type name used in its
{name:Type} placeholder (e.g. 5 -> "Int64").
Functions
Turns an Elixir term into {type, text, rounds} in one call: the
ClickHouse type name for its {name:Type} placeholder (see type/1),
the literal text for its value (see text/1), and the escaping depth
that text needs on the wire (see escape_rounds/1).
@spec escape_rounds(term()) :: 1 | 2
The number of backslash/quote-escaping rounds the wire encoder
(ChDriver.Protocol.encode_query/2) must apply to bind value for it
to round-trip -- 1 for a list (the Array literal text text/1
renders for it already has its string elements \'-escaped once, see
array_element_text/1), 2 for everything else.
The server unescapes a scalar parameter's value text twice before
parsing it against the declared type, but only once for each string
inside an Array(String)/Map value's already-quoted element syntax.
A scalar value escaped only once loses backslashes asymmetrically (an
odd leftover backslash fuses with the next character into an
unintended escape, e.g. \b becomes a backspace byte), while escaping
it twice round-trips exactly. An Array(String) value escaped twice,
on the other hand, corrupts its already-\'-escaped elements, because
the array parses each element with a single-escape "Quoted" reader,
one level shallower than the plain scalar path
(ReplaceQueryParameterVisitor re-parses a scalar's already-unescaped
custom-setting value as escaped text a second time, but does not do so
for array elements). This is undocumented ClickHouse behavior, treated
here as a black-box wire fact rather than a designed feature.
Takes the same Elixir term text/1 would be called with, not its
rendered text.
Renders an Elixir term as the unquoted, unescaped ClickHouse literal
text a query parameter's value should carry -- the same text you'd
type after a CAST(..., 'Type') for that value. ChDriver.Protocol's
encode_query/2 applies the wire-level quoting/escaping on top of this
(see quote_param_value/2); this function only handles turning the
value itself into ClickHouse literal syntax.
There is no clause for nil: ClickHouse's query
parameters have no type-independent way to express NULL (a
Nullable(String)-typed NULL parameter fails to bind against, say, an
Int32 column), so callers should inline a literal NULL into the
query text instead of routing nil through this.
Maps an Elixir runtime value to the ClickHouse type name used in its
{name:Type} placeholder (e.g. 5 -> "Int64").
There's no clause for nil -- see the moduledoc-adjacent note on
text/1 for why.