Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- move rows payload (RowBinary, CSV, etc.) to SQL statement and remove pseudo-positional binds, making param names explicit https://github.com/plausible/ch/pull/143
- drop `:headers` from `%Ch.Result{}` but add `:data` https://github.com/plausible/ch/pull/144
- fix query string escaping for `\t`, `\\`, and `\n` https://github.com/plausible/ch/pull/147

## 0.2.2 (2023-12-23)

Expand Down
10 changes: 9 additions & 1 deletion lib/ch/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,15 @@ defimpl DBConnection.Query, for: Ch.Query do

defp encode_param(n) when is_integer(n), do: Integer.to_string(n)
defp encode_param(f) when is_float(f), do: Float.to_string(f)
defp encode_param(b) when is_binary(b), do: escape_param([{"\t", "\\t"}, {"\n", "\\n"}], b)

# TODO possibly speed up
# For more info see
# https://clickhouse.com/docs/en/interfaces/http#tabs-in-url-parameters
# "escaped" format is the same as https://clickhouse.com/docs/en/interfaces/formats#tabseparated-data-formatting
defp encode_param(b) when is_binary(b) do
escape_param([{"\\", "\\\\"}, {"\t", "\\\t"}, {"\n", "\\\n"}], b)
end

defp encode_param(b) when is_boolean(b), do: Atom.to_string(b)
defp encode_param(%Decimal{} = d), do: Decimal.to_string(d, :normal)
defp encode_param(%Date{} = date), do: Date.to_iso8601(date)
Expand Down
23 changes: 23 additions & 0 deletions test/ch/query_string_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Ch.QueryStringTest do
use ExUnit.Case, async: true

setup do
{:ok, conn: start_supervised!({Ch, database: Ch.Test.database()})}
end

# For more info see
# https://clickhouse.com/docs/en/interfaces/http#tabs-in-url-parameters
# "escaped" format is the same as https://clickhouse.com/docs/en/interfaces/formats#tabseparated-data-formatting
test "binaries are escaped properly", %{conn: conn} do
for s <- ["\t", "\n", "\\", "'", "\b", "\f", "\r", "\0"] do
assert Ch.query!(conn, "select {s:String}", %{"s" => s}).rows == [[s]]
end

# example from https://clickhouse.com/docs/en/interfaces/http#tabs-in-url-parameters
assert Ch.query!(conn, "select splitByChar('\t', 'abc\t123')").rows ==
[[["abc", "123"]]]

assert Ch.query!(conn, "select splitByChar('\t', {arg1:String})", %{"arg1" => "abc\t123"}).rows ==
[[["abc", "123"]]]
end
end