diff --git a/lib/ch/query.ex b/lib/ch/query.ex index ee44b87..a8b74cc 100644 --- a/lib/ch/query.ex +++ b/lib/ch/query.ex @@ -145,7 +145,7 @@ defimpl DBConnection.Query, for: Ch.Query do def decode(%Query{decode: false, command: command}, responses, _opts) when is_list(responses) do # TODO potentially fails on x-progress-headers [_status, headers | data] = responses - %Result{rows: data, command: command, headers: headers} + %Result{data: data, command: command, headers: headers} end def decode(%Query{command: command}, responses, opts) when is_list(responses) do @@ -163,7 +163,7 @@ defimpl DBConnection.Query, for: Ch.Query do %Result{num_rows: length(rows), rows: rows, command: command, headers: headers} _other -> - %Result{rows: data, command: command, headers: headers} + %Result{data: data, command: command, headers: headers} end end diff --git a/lib/ch/result.ex b/lib/ch/result.ex index 848444d..f45db2a 100644 --- a/lib/ch/result.ex +++ b/lib/ch/result.ex @@ -3,20 +3,19 @@ defmodule Ch.Result do Result struct returned from any successful query. Its fields are: * `command` - An atom of the query command, for example: `:select`, `:insert`; - * `rows` - The result set. One of: - - a list of lists, each inner list corresponding to a - row, each element in the inner list corresponds to a column; - - raw iodata when the response is not automatically decoded, e.g. `x-clickhouse-format: CSV` + * `rows` - A list of lists, each inner list corresponding to a row, each element in the inner list corresponds to a column * `num_rows` - The number of fetched or affected rows; * `headers` - The HTTP response headers + * `data` - The raw iodata from the response """ - defstruct [:command, :num_rows, :rows, :headers] + defstruct [:command, :num_rows, :rows, :headers, :data] @type t :: %__MODULE__{ command: Ch.Query.command(), num_rows: non_neg_integer | nil, - rows: [[term]] | iodata | nil, - headers: Mint.Types.headers() + rows: [[term]] | nil, + headers: Mint.Types.headers(), + data: iodata } end diff --git a/test/ch/connection_test.exs b/test/ch/connection_test.exs index bab1635..2d37c0a 100644 --- a/test/ch/connection_test.exs +++ b/test/ch/connection_test.exs @@ -161,7 +161,7 @@ defmodule Ch.ConnectionTest do end test "create", %{conn: conn} do - assert {:ok, %{num_rows: nil, rows: []}} = + assert {:ok, %{command: :create, num_rows: nil, rows: nil, data: []}} = Ch.query(conn, "create table create_example(a UInt8) engine = Memory") end @@ -320,7 +320,7 @@ defmodule Ch.ConnectionTest do settings = [allow_experimental_lightweight_delete: 1] - assert {:ok, %{rows: [], command: :delete}} = + assert {:ok, %{rows: nil, data: [], command: :delete}} = Ch.query(conn, "delete from delete_t where 1", [], settings: settings) end @@ -1002,11 +1002,10 @@ defmodule Ch.ConnectionTest do ] # to make our RowBinary is not garbage in garbage out we also test a text format response - assert conn - |> Ch.query!( + assert Ch.query!( + conn, "SELECT p, toTypeName(p) FROM geo_point ORDER BY p ASC FORMAT JSONCompact" - ) - |> Map.fetch!(:rows) + ).data |> Jason.decode!() |> Map.fetch!("data") == [ [[10, 10], "Point"], @@ -1040,7 +1039,7 @@ defmodule Ch.ConnectionTest do assert Ch.query!( conn, "SELECT r, toTypeName(r) FROM geo_ring ORDER BY r ASC FORMAT JSONCompact" - ).rows + ).data |> Jason.decode!() |> Map.fetch!("data") == [ [[[0, 0], [10, 0], [10, 10], [0, 10]], "Ring"], @@ -1088,7 +1087,7 @@ defmodule Ch.ConnectionTest do assert Ch.query!( conn, "SELECT pg, toTypeName(pg) FROM geo_polygon ORDER BY pg ASC FORMAT JSONCompact" - ).rows + ).data |> Jason.decode!() |> Map.fetch!("data") == [ [[[[0, 1], [10, 3.2]], [], [[2, 2]]], "Polygon"], @@ -1166,7 +1165,7 @@ defmodule Ch.ConnectionTest do assert Ch.query!( conn, "SELECT mpg, toTypeName(mpg) FROM geo_multipolygon ORDER BY mpg ASC FORMAT JSONCompact" - ).rows + ).data |> Jason.decode!() |> Map.fetch!("data") == [ [ diff --git a/test/ch/headers_test.exs b/test/ch/headers_test.exs index 52aa7c3..b3e98b1 100644 --- a/test/ch/headers_test.exs +++ b/test/ch/headers_test.exs @@ -7,7 +7,7 @@ defmodule Ch.HeadersTest do end test "can request gzipped response through headers", %{conn: conn} do - assert {:ok, %{rows: rows, headers: headers}} = + assert {:ok, %{data: data, headers: headers}} = Ch.query(conn, "select number from system.numbers limit 100", [], decode: false, settings: [enable_http_compression: 1], @@ -19,11 +19,11 @@ defmodule Ch.HeadersTest do assert :proplists.get_value("x-clickhouse-format", headers) == "RowBinaryWithNamesAndTypes" # https://en.wikipedia.org/wiki/Gzip - assert <<0x1F, 0x8B, _rest::bytes>> = IO.iodata_to_binary(rows) + assert <<0x1F, 0x8B, _rest::bytes>> = IO.iodata_to_binary(data) end test "can request lz4 response through headers", %{conn: conn} do - assert {:ok, %{rows: rows, headers: headers}} = + assert {:ok, %{data: data, headers: headers}} = Ch.query(conn, "select number from system.numbers limit 100", [], decode: false, settings: [enable_http_compression: 1], @@ -35,11 +35,11 @@ defmodule Ch.HeadersTest do assert :proplists.get_value("x-clickhouse-format", headers) == "RowBinaryWithNamesAndTypes" # https://en.wikipedia.org/wiki/LZ4_(compression_algorithm) - assert <<0x04, 0x22, 0x4D, 0x18, _rest::bytes>> = IO.iodata_to_binary(rows) + assert <<0x04, 0x22, 0x4D, 0x18, _rest::bytes>> = IO.iodata_to_binary(data) end test "can request zstd response through headers", %{conn: conn} do - assert {:ok, %{rows: rows, headers: headers}} = + assert {:ok, %{data: data, headers: headers}} = Ch.query(conn, "select number from system.numbers limit 100", [], decode: false, settings: [enable_http_compression: 1], @@ -51,6 +51,6 @@ defmodule Ch.HeadersTest do assert :proplists.get_value("x-clickhouse-format", headers) == "RowBinaryWithNamesAndTypes" # https://en.wikipedia.org/wiki/LZ4_(compression_algorithm) - assert <<0x28, 0xB5, 0x2F, 0xFD, _rest::bytes>> = IO.iodata_to_binary(rows) + assert <<0x28, 0xB5, 0x2F, 0xFD, _rest::bytes>> = IO.iodata_to_binary(data) end end