Ecto.Adapters.ClickHouse.Connection (clickhouse_adapter_ecto v0.1.0)

Copy Markdown

Implements Ecto.Adapters.SQL.Connection on top of ChDriver.

Parameter binding

SQL is generated the normal Ecto way with ? placeholders (see expr/3, insert/8), matching every other Ecto.Adapters.SQL.Connection implementation. Rather than inlining the corresponding runtime values as SQL literals, %ChDriver.Query{}'s DBConnection.Query implementation (see ch_driver/lib/ch_driver/query.ex) rewrites each ? into a ClickHouse native {pN:Type} parameter placeholder and sends the actual value alongside the query through ChDriver's query-parameters wire mechanism (see ChDriver.Protocol.encode_query/2) -- the value's bytes never pass through the SQL text at all, so there's nothing to escape and no ? inside a string literal or raw fragment can be mistaken for a bind placeholder (the one-time lexer this drives, ChDriver.Query's lex_placeholders/1, tracks quoted regions while scanning).

ClickHouse's parameter mechanism has no type-independent way to express NULL (see ChDriver.Params.text/1), so a nil value is the one exception: it's inlined directly as the literal NULL token, which carries no injection risk since it's a fixed constant.

This module itself no longer does any of that lexing/binding -- it just builds a %ChDriver.Query{statement: sql} and lets DBConnection's normal parse/encode/execute flow (driven by Ecto.Adapters.SQL's own query cache) do it, once per distinct prepared query shape rather than on every execute. See ChDriver.Query's moduledoc for the full rationale.

Summary

Functions

Builds a %ChDriver.Stream{} (see its moduledoc, and ChDriver.DBConnection's "Cursors" section, for exactly how this stays genuinely incremental -- one ClickHouse wire-protocol Data block at a time -- rather than buffering the whole result first) for statement, mirroring every other Ecto.Adapters.SQL.Connection's stream/4 implementation (e.g. Postgrex.Connection.stream/4, which just calls Postgrex.stream/4).

Functions

stream(conn, statement, params, opts)

Builds a %ChDriver.Stream{} (see its moduledoc, and ChDriver.DBConnection's "Cursors" section, for exactly how this stays genuinely incremental -- one ClickHouse wire-protocol Data block at a time -- rather than buffering the whole result first) for statement, mirroring every other Ecto.Adapters.SQL.Connection's stream/4 implementation (e.g. Postgrex.Connection.stream/4, which just calls Postgrex.stream/4).

conn here is always already a checked-out %DBConnection{conn_mode: :transaction} by the time this is called -- Ecto.Adapters.SQL's reduce/6 (which backs Repo.stream/2) guards on exactly that mode before ever reaching here (see ecto_sql/lib/ecto/adapters/sql.ex), the same requirement every SQL adapter's Repo.stream/2 has (Postgres included).

KNOWN LIMITATION: unlike Postgres, ChDriver.DBConnection has no handle_begin/2 (see its moduledoc -- ClickHouse's native protocol as used here has no session transaction support), so Repo.transaction/2 itself cannot succeed on this adapter yet, which means Repo.stream/2 cannot be driven end-to-end through Ecto.Repo's transaction/2 the normal way. This function, and the underlying ChDriver.Stream machinery, are fully real and independently tested (ch_driver/test/ ch_driver/stream_test.exs, clickhouse_adapter_ecto/test/integration/stream_test.exs) via DBConnection.run/3 directly (which only needs a plain checkout, not a transaction) -- adding real ClickHouse-transaction support to unblock Repo.transaction/2/Repo.stream/2 end-to-end is tracked separately (see the adapter-level stream test's moduledoc).