Skip to content

Commit

Permalink
Fix query string escaping (#147)
Browse files Browse the repository at this point in the history
* add failing test

* fix query string escaping

* changelog
  • Loading branch information
ruslandoga authored Jan 4, 2024
1 parent 6965b03 commit 4b0092e
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
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

0 comments on commit 4b0092e

Please sign in to comment.