• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

plausible / ch / 47ef9d0205e34f4d2b2e013b47a0f5eb2a9331fa-PR-321

07 May 2026 05:33PM UTC coverage: 92.383% (+0.007%) from 92.376%
47ef9d0205e34f4d2b2e013b47a0f5eb2a9331fa-PR-321

Pull #321

github

ruslandoga
Use identifier params in BFloat16 table queries
Pull Request #321: add bfloat16

14 of 15 new or added lines in 2 files covered. (93.33%)

11 existing lines in 2 files now uncovered.

946 of 1024 relevant lines covered (92.38%)

78887.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.93
/lib/ch/row_binary.ex
1
defmodule Ch.RowBinary do
2
  @moduledoc "Helpers for working with ClickHouse [RowBinary](https://clickhouse.com/docs/en/interfaces/formats/RowBinary) format."
3

4
  # @compile {:bin_opt_info, true}
5
  @dialyzer :no_improper_lists
6

7
  import Bitwise
8

9
  @epoch_gregorian_seconds 62_167_219_200
10
  @epoch_gregorian_days 719_528
11

12
  @doc false
13
  def encode_names_and_types(names, types) do
6✔
14
    [encode(:varint, length(names)), encode_many(names, :string), encode_types(types)]
15
  end
16

17
  defp encode_types([type | types]) do
18
    encoded =
18✔
19
      case type do
20
        _ when is_binary(type) -> type
×
21
        _ -> Ch.Types.encode(type)
18✔
22
      end
23

24
    [encode(:string, encoded) | encode_types(types)]
25
  end
26

27
  defp encode_types([] = done), do: done
6✔
28

29
  @doc """
30
  Encodes a single row to [RowBinary](https://clickhouse.com/docs/en/interfaces/formats/RowBinary) as iodata.
31

32
  Examples:
33

34
      iex> encode_row([], [])
35
      []
36

37
      iex> encode_row([1], ["UInt8"])
38
      [1]
39

40
      iex> encode_row([3, "hello"], ["UInt8", "String"])
41
      [3, [5 | "hello"]]
42

43
  """
44
  def encode_row(row, types) do
45
    _encode_row(row, encoding_types(types))
26✔
46
  end
47

48
  defp _encode_row([el | els], [type | types]), do: [encode(type, el) | _encode_row(els, types)]
116✔
49
  defp _encode_row([] = done, []), do: done
26✔
50

51
  @doc """
52
  Encodes multiple rows to [RowBinary](https://clickhouse.com/docs/en/interfaces/formats/RowBinary) as iodata.
53

54
  Examples:
55

56
      iex> encode_rows([], [])
57
      []
58

59
      iex> encode_rows([[1]], ["UInt8"])
60
      [1]
61

62
      iex> encode_rows([[3, "hello"], [4, "hi"]], ["UInt8", "String"])
63
      [3, [5 | "hello"], 4, [2 | "hi"]]
64

65
  """
66
  def encode_rows(rows, types) do
67
    _encode_rows(rows, encoding_types(types))
325✔
68
  end
69

70
  @doc false
71
  def _encode_rows([row | rows], types), do: _encode_rows(row, types, rows, types)
2,004,238✔
72
  def _encode_rows([] = done, _types), do: done
325✔
73

74
  defp _encode_rows([el | els], [t | ts], rows, types) do
2,008,471✔
75
    [encode(t, el) | _encode_rows(els, ts, rows, types)]
76
  end
77

78
  defp _encode_rows([], [], rows, types), do: _encode_rows(rows, types)
2,004,238✔
79

80
  @doc false
81
  def encoding_types([type | types]) do
750✔
82
    [encoding_type(type) | encoding_types(types)]
83
  end
84

85
  def encoding_types([] = done), do: done
351✔
86

87
  defp encoding_type(type) when is_binary(type) do
88
    encoding_type(Ch.Types.decode(type))
617✔
89
  end
90

91
  defp encoding_type(t)
92
       when t in [
93
              :string,
94
              :binary,
95
              :json,
96
              :dynamic,
97
              :boolean,
98
              :uuid,
99
              :date,
100
              :datetime,
101
              :date32,
102
              :time,
103
              :ipv4,
104
              :ipv6,
105
              :point,
106
              :nothing,
107
              :bf16
108
            ],
109
       do: t
353✔
110

111
  defp encoding_type({:datetime = d, "UTC"}), do: d
×
112

113
  defp encoding_type({:datetime, tz}) do
114
    raise ArgumentError, "can't encode DateTime with non-UTC timezone: #{inspect(tz)}"
×
115
  end
116

117
  defp encoding_type({:fixed_string, _len} = t), do: t
13✔
118

119
  for size <- [8, 16, 32, 64, 128, 256] do
120
    defp encoding_type(unquote(:"u#{size}") = u), do: u
329✔
121
    defp encoding_type(unquote(:"i#{size}") = i), do: i
42✔
122
  end
123

124
  for size <- [32, 64] do
125
    defp encoding_type(unquote(:"f#{size}") = f), do: f
6✔
126
  end
127

128
  defp encoding_type({:array = a, t}), do: {a, encoding_type(t)}
26✔
129

130
  defp encoding_type({:tuple = t, ts}) do
4✔
131
    {t, Enum.map(ts, &encoding_type/1)}
132
  end
133

134
  defp encoding_type({:variant = v, ts}) do
4✔
135
    {v, Enum.map(ts, &encoding_type/1)}
136
  end
137

138
  defp encoding_type({:map = m, kt, vt}) do
139
    {m, encoding_type(kt), encoding_type(vt)}
9✔
140
  end
141

142
  defp encoding_type({:nullable = n, t}), do: {n, encoding_type(t)}
16✔
143
  defp encoding_type({:low_cardinality, t}), do: encoding_type(t)
4✔
144

145
  defp encoding_type({:decimal, p, s}) do
146
    case decimal_size(p) do
×
147
      32 -> {:decimal32, s}
×
148
      64 -> {:decimal64, s}
×
149
      128 -> {:decimal128, s}
×
150
      256 -> {:decimal256, s}
×
151
    end
152
  end
153

154
  defp encoding_type({d, _scale} = t)
155
       when d in [:decimal32, :decimal64, :decimal128, :decimal256],
156
       do: t
6✔
157

158
  defp encoding_type({:datetime64 = t, p}), do: {t, time_unit(p)}
2✔
159

160
  defp encoding_type({:datetime64 = t, p, "UTC"}), do: {t, time_unit(p)}
×
161

162
  defp encoding_type({:datetime64, _, tz}) do
163
    raise ArgumentError, "can't encode DateTime64 with non-UTC timezone: #{inspect(tz)}"
×
164
  end
165

166
  defp encoding_type({:time64 = t, p}), do: {t, time_unit(p)}
8✔
167

168
  defp encoding_type({e, mappings}) when e in [:enum8, :enum16] do
4✔
169
    {e, Map.new(mappings)}
170
  end
171

172
  defp encoding_type({:simple_aggregate_function, _f, t}), do: encoding_type(t)
×
173

174
  defp encoding_type(:ring), do: {:array, :point}
2✔
175
  defp encoding_type(:polygon), do: {:array, {:array, :point}}
2✔
176
  defp encoding_type(:multipolygon), do: {:array, {:array, {:array, :point}}}
2✔
177

178
  defp encoding_type(type) do
179
    raise ArgumentError, "unsupported type for encoding: #{inspect(type)}"
×
180
  end
181

182
  @doc false
183
  def encode(type, value)
184

185
  def encode(:varint, i) when is_integer(i) and i < 128, do: i
388✔
186
  def encode(:varint, i) when is_integer(i), do: encode_varint_cont(i)
8✔
187

188
  def encode(type, str) when type in [:string, :binary] do
189
    case str do
329✔
190
      _ when is_binary(str) -> [encode(:varint, byte_size(str)) | str]
278✔
191
      _ when is_list(str) -> [encode(:varint, IO.iodata_length(str)) | str]
47✔
192
      nil -> 0
1✔
193
    end
194
  end
195

196
  def encode(:json, json) do
197
    # assuming it can be sent as text and not "native" binary JSON
198
    # i.e. assumes `settings: [input_format_binary_read_json_as_string: 1]`
199
    # TODO
200
    encode(:string, Jason.encode_to_iodata!(json))
6✔
201
  end
202

203
  def encode({:fixed_string, size}, str) when byte_size(str) == size do
204
    str
15✔
205
  end
206

207
  def encode({:fixed_string, size}, str) when byte_size(str) < size do
208
    to_pad = size - byte_size(str)
6✔
209
    [str | <<0::size(to_pad * 8)>>]
210
  end
211

212
  def encode({:fixed_string, size}, nil), do: <<0::size(size * 8)>>
1✔
213

214
  # UInt8 — [0 : 255]
215
  def encode(:u8, u) when is_integer(u) and u >= 0 and u <= 255, do: u
4,137✔
216
  def encode(:u8, nil), do: 0
5✔
217

218
  def encode(:u8, term) do
219
    raise ArgumentError, "invalid UInt8: #{inspect(term)}"
3✔
220
  end
221

222
  # Int8 — [-128 : 127]
223
  def encode(:i8, i) when is_integer(i) and i >= 0 and i <= 127, do: i
15✔
224
  def encode(:i8, i) when is_integer(i) and i < 0 and i >= -128, do: <<i::signed>>
3✔
225
  def encode(:i8, nil), do: 0
1✔
226

227
  def encode(:i8, term) do
228
    raise ArgumentError, "invalid Int8: #{inspect(term)}"
3✔
229
  end
230

231
  for size <- [16, 32, 64, 128, 256] do
232
    def encode(unquote(:"u#{size}"), u) when is_integer(u) do
233
      <<u::unquote(size)-little>>
2,000,066✔
234
    end
235

236
    def encode(unquote(:"i#{size}"), i) when is_integer(i) do
237
      <<i::unquote(size)-little-signed>>
56✔
238
    end
239

240
    def encode(unquote(:"u#{size}"), nil), do: <<0::unquote(size)>>
3✔
241
    def encode(unquote(:"i#{size}"), nil), do: <<0::unquote(size)>>
3✔
242
  end
243

244
  for size <- [32, 64] do
245
    type = :"f#{size}"
246

247
    def encode(unquote(type), f) when is_number(f) do
248
      <<f::unquote(size)-little-float>>
71✔
249
    end
250

251
    def encode(unquote(type), nil), do: <<0::unquote(size)>>
2✔
252
  end
253

254
  def encode(:bf16, f) when is_number(f) do
255
    <<float_to_bf16(f)::16-little>>
4,030✔
256
  end
257

258
  def encode(:bf16, nil), do: <<0::16>>
1✔
259

260
  def encode({:decimal, precision, scale}, decimal) do
UNCOV
261
    type =
×
262
      case decimal_size(precision) do
263
        32 -> :decimal32
×
UNCOV
264
        64 -> :decimal64
×
UNCOV
265
        128 -> :decimal128
×
UNCOV
266
        256 -> :decimal256
×
267
      end
268

269
    encode({type, scale}, decimal)
×
270
  end
271

272
  for size <- [32, 64, 128, 256] do
273
    type = :"decimal#{size}"
274

275
    def encode({unquote(type), scale} = t, %Decimal{sign: sign, coef: coef, exp: exp} = d) do
276
      cond do
24✔
277
        scale == -exp ->
278
          i = sign * coef
17✔
279
          <<i::unquote(size)-little>>
17✔
280

281
        exp >= 0 ->
7✔
282
          i = sign * coef * Integer.pow(10, exp + scale)
1✔
283
          <<i::unquote(size)-little>>
1✔
284

285
        true ->
6✔
286
          encode(t, Decimal.round(d, scale))
6✔
287
      end
288
    end
289

290
    def encode({unquote(type), _scale}, nil), do: <<0::unquote(size)>>
4✔
291
  end
292

293
  def encode(:boolean, true), do: 1
4✔
294
  def encode(:boolean, false), do: 0
4✔
295
  def encode(:boolean, nil), do: 0
1✔
296

297
  def encode({:array, type}, [_ | _] = l) do
50✔
298
    [encode(:varint, length(l)) | encode_many(l, type)]
299
  end
300

301
  def encode({:array, _type}, []), do: 0
13✔
302
  def encode({:array, _type}, nil), do: 0
4✔
303

304
  def encode({:map, k, v}, [_ | _] = m) do
14✔
305
    [encode(:varint, length(m)) | encode_many_kv(m, k, v)]
306
  end
307

308
  def encode({:map, _k, _v} = t, m) when is_map(m), do: encode(t, Map.to_list(m))
16✔
309
  def encode({:map, _k, _v}, []), do: 0
8✔
310
  def encode({:map, _k, _v}, nil), do: 0
3✔
311

312
  def encode({:tuple, _types} = t, v) when is_tuple(v) do
313
    encode(t, Tuple.to_list(v))
8✔
314
  end
315

316
  def encode({:tuple, types}, values) when is_list(types) and is_list(values) do
317
    encode_row(values, types)
8✔
318
  end
319

320
  def encode({:tuple, types}, nil) when is_list(types) do
321
    Enum.map(types, fn type -> encode(type, nil) end)
×
322
  end
323

324
  def encode({:variant, _types}, nil), do: 255
4✔
325

326
  def encode({:variant, types}, value) do
327
    try_encode_variant(types, 0, value)
10✔
328
  end
329

330
  def encode(:datetime, %NaiveDateTime{} = datetime) do
331
    {seconds, _micros} = NaiveDateTime.to_gregorian_seconds(datetime)
18✔
332
    <<seconds - @epoch_gregorian_seconds::32-little>>
18✔
333
  end
334

335
  def encode(:datetime, %DateTime{} = datetime) do
336
    <<DateTime.to_unix(datetime, :second)::32-little>>
10✔
337
  end
338

339
  def encode(:datetime, nil), do: <<0::32>>
1✔
340

341
  def encode({:datetime64, time_unit}, %NaiveDateTime{} = datetime) do
342
    {seconds, micros} = NaiveDateTime.to_gregorian_seconds(datetime)
8✔
343

344
    <<(seconds - @epoch_gregorian_seconds) * time_unit + div(micros * time_unit, 1_000_000)::64-little-signed>>
8✔
345
  end
346

347
  def encode({:datetime64, time_unit}, %DateTime{} = datetime) do
348
    <<DateTime.to_unix(datetime, time_unit)::64-little-signed>>
4✔
349
  end
350

351
  def encode({:datetime64, _time_unit}, nil), do: <<0::64>>
1✔
352

353
  def encode(:date, %Date{} = date) do
354
    <<Date.to_gregorian_days(date) - @epoch_gregorian_days::16-little>>
13✔
355
  end
356

357
  def encode(:date, nil), do: <<0::16>>
1✔
358

359
  def encode(:date32, %Date{} = date) do
360
    <<Date.to_gregorian_days(date) - @epoch_gregorian_days::32-little-signed>>
8✔
361
  end
362

363
  def encode(:date32, nil), do: <<0::32>>
1✔
364

365
  def encode(:time, %Time{} = time) do
366
    {s, _micros} = Time.to_seconds_after_midnight(time)
10✔
367
    <<s::32-little-signed>>
10✔
368
  end
369

370
  def encode(:time, nil), do: <<0::32>>
×
371

372
  def encode({:time64, time_unit}, %Time{} = time) do
373
    {s, micros} = Time.to_seconds_after_midnight(time)
34✔
374

375
    micros_as_ticks =
34✔
376
      cond do
377
        time_unit < 1_000_000 -> div(micros, time_unit)
12✔
378
        time_unit == 1_000_000 -> micros
22✔
379
        true -> micros * div(time_unit, 1_000_000)
10✔
380
      end
381

382
    ticks = s * time_unit + micros_as_ticks
34✔
383
    <<ticks::64-little-signed>>
34✔
384
  end
385

386
  def encode({:time64, _time_unit}, nil), do: <<0::64>>
×
387

388
  def encode(:uuid, <<u1::64, u2::64>>), do: <<u1::64-little, u2::64-little>>
9✔
389

390
  def encode(
391
        :uuid,
392
        <<a1, a2, a3, a4, a5, a6, a7, a8, ?-, b1, b2, b3, b4, ?-, c1, c2, c3, c4, ?-, d1, d2, d3,
393
          d4, ?-, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12>>
394
      ) do
395
    raw =
1✔
396
      <<d(a1)::4, d(a2)::4, d(a3)::4, d(a4)::4, d(a5)::4, d(a6)::4, d(a7)::4, d(a8)::4, d(b1)::4,
397
        d(b2)::4, d(b3)::4, d(b4)::4, d(c1)::4, d(c2)::4, d(c3)::4, d(c4)::4, d(d1)::4, d(d2)::4,
398
        d(d3)::4, d(d4)::4, d(e1)::4, d(e2)::4, d(e3)::4, d(e4)::4, d(e5)::4, d(e6)::4, d(e7)::4,
399
        d(e8)::4, d(e9)::4, d(e10)::4, d(e11)::4, d(e12)::4>>
400

401
    encode(:uuid, raw)
1✔
402
  end
403

404
  def encode(:uuid, nil), do: <<0::128>>
1✔
405

406
  def encode(:ipv4, {a, b, c, d}), do: [d, c, b, a]
4✔
407
  def encode(:ipv4, nil), do: <<0::32>>
×
408

409
  def encode(:ipv6, {b1, b2, b3, b4, b5, b6, b7, b8}) do
410
    <<b1::16, b2::16, b3::16, b4::16, b5::16, b6::16, b7::16, b8::16>>
4✔
411
  end
412

413
  def encode(:ipv6, <<_::128>> = encoded), do: encoded
×
414
  def encode(:ipv6, nil), do: <<0::128>>
×
415

416
  def encode(:point, {x, y}), do: [encode(:f64, x) | encode(:f64, y)]
28✔
417
  def encode(:point, nil), do: <<0::128>>
1✔
418
  def encode(:ring, points), do: encode({:array, :point}, points)
1✔
419
  def encode(:polygon, rings), do: encode({:array, :ring}, rings)
1✔
420
  def encode(:multipolygon, polygons), do: encode({:array, :polygon}, polygons)
1✔
421

422
  # TODO
423
  def encode(:dynamic, value) do
424
    case value do
18✔
425
      _ when is_binary(value) -> [0x15 | encode(:string, value)]
3✔
426
      _ when is_integer(value) and value >= 0 -> [0x04 | encode(:u64, value)]
5✔
427
      _ when is_integer(value) -> [0x0A | encode(:i64, value)]
2✔
428
      _ when is_float(value) -> [0x0E | encode(:f64, value)]
3✔
429
      %Date{} -> [0x0F | encode(:date, value)]
3✔
430
      %NaiveDateTime{} -> [0x11 | encode(:datetime, value)]
2✔
431
      [] -> [0x1E, 0x00]
×
432
    end
433
  end
434

435
  # TODO enum8 and enum16 nil
436
  for size <- [8, 16] do
437
    enum_t = :"enum#{size}"
438
    int_t = :"i#{size}"
439

440
    def encode({unquote(enum_t), mapping}, e) do
441
      i =
12✔
442
        case e do
443
          _ when is_integer(e) ->
444
            e
4✔
445

446
          _ when is_binary(e) ->
447
            case Map.fetch(mapping, e) do
8✔
448
              {:ok, res} ->
449
                res
8✔
450

451
              :error ->
452
                raise ArgumentError,
×
453
                      "enum value #{inspect(e)} not found in mapping: #{inspect(mapping)}"
454
            end
455
        end
456

457
      encode(unquote(int_t), i)
12✔
458
    end
459
  end
460

461
  def encode({:nullable, _type}, nil), do: 1
12✔
462

463
  def encode({:nullable, type}, value) do
464
    case encode(type, value) do
11✔
465
      e when is_list(e) or is_binary(e) -> [0 | e]
11✔
466
      e -> [0, e]
×
467
    end
468
  end
469

470
  defp float_to_bf16(f) do
471
    <<bits::32>> = <<f::32-float>>
4,030✔
472

473
    upper = bits >>> 16
4,030✔
474
    lower = bits &&& 0xFFFF
4,030✔
475

476
    if lower > 0x8000 or (lower == 0x8000 and (upper &&& 1) == 1) do
4,030✔
NEW
UNCOV
477
      upper + 1
×
478
    else
479
      upper
4,030✔
480
    end
481
  end
482

483
  defp bfloat16_to_float(bits) when (bits &&& 0x7F80) == 0x7F80, do: nil
6✔
484

485
  defp bfloat16_to_float(bits) do
486
    <<f::32-float>> = <<bits::16, 0::16>>
4,430✔
487
    f
4,430✔
488
  end
489

490
  defp encode_varint_cont(i) when i < 128, do: <<i>>
8✔
491

492
  defp encode_varint_cont(i) do
12✔
493
    [(i &&& 0b0111_1111) ||| 0b1000_0000 | encode_varint_cont(i >>> 7)]
494
  end
495

496
  defp encode_many([el | rest], type), do: [encode(type, el) | encode_many(rest, type)]
130✔
497
  defp encode_many([] = done, _type), do: done
56✔
498

499
  defp encode_many_kv([{key, value} | rest], key_type, value_type) do
19✔
500
    [
501
      encode(key_type, key),
502
      encode(value_type, value)
503
      | encode_many_kv(rest, key_type, value_type)
504
    ]
505
  end
506

507
  defp encode_many_kv([] = done, _key_type, _value_type), do: done
14✔
508

509
  # TODO find a better way than try/rescue
510
  defp try_encode_variant([type | types], idx, value) do
511
    try do
18✔
512
      encode(type, value)
18✔
513
    else
514
      encoded -> [idx | encoded]
10✔
515
    rescue
516
      _e -> try_encode_variant(types, idx + 1, value)
8✔
517
    end
518
  end
519

520
  defp try_encode_variant([], _idx, value) do
521
    raise ArgumentError, "no matching type found for encoding #{inspect(value)} as Variant"
×
522
  end
523

524
  @compile {:inline, d: 1}
525

526
  defp d(?0), do: 0
1✔
527
  defp d(?1), do: 1
×
528
  defp d(?2), do: 2
1✔
529
  defp d(?3), do: 3
1✔
530
  defp d(?4), do: 4
×
531
  defp d(?5), do: 5
1✔
532
  defp d(?6), do: 6
1✔
533
  defp d(?7), do: 7
×
534
  defp d(?8), do: 8
1✔
535
  defp d(?9), do: 9
1✔
536
  defp d(?A), do: 10
×
537
  defp d(?B), do: 11
×
538
  defp d(?C), do: 12
×
539
  defp d(?D), do: 13
×
540
  defp d(?E), do: 14
×
541
  defp d(?F), do: 15
×
542
  defp d(?a), do: 10
1✔
543
  defp d(?b), do: 11
1✔
544
  defp d(?c), do: 12
1✔
545
  defp d(?d), do: 13
1✔
546
  defp d(?e), do: 14
1✔
547
  defp d(?f), do: 15
1✔
548

549
  varints = [
550
    {_pattern = quote(do: <<0::1, v1::7>>), _value = quote(do: v1)},
551
    {quote(do: <<1::1, v1::7, 0::1, v2::7>>), quote(do: (v2 <<< 7) + v1)},
552
    {quote(do: <<1::1, v1::7, 1::1, v2::7, 0::1, v3::7>>),
553
     quote(do: (v3 <<< 14) + (v2 <<< 7) + v1)},
554
    {quote(do: <<1::1, v1::7, 1::1, v2::7, 1::1, v3::7, 0::1, v4::7>>),
555
     quote(do: (v4 <<< 21) + (v3 <<< 14) + (v2 <<< 7) + v1)},
556
    {quote(do: <<1::1, v1::7, 1::1, v2::7, 1::1, v3::7, 1::1, v4::7, 0::1, v5::7>>),
557
     quote(do: (v5 <<< 28) + (v4 <<< 21) + (v3 <<< 14) + (v2 <<< 7) + v1)},
558
    {quote(do: <<1::1, v1::7, 1::1, v2::7, 1::1, v3::7, 1::1, v4::7, 1::1, v5::7, 0::1, v6::7>>),
559
     quote(do: (v6 <<< 35) + (v5 <<< 28) + (v4 <<< 21) + (v3 <<< 14) + (v2 <<< 7) + v1)},
560
    {quote do
561
       <<1::1, v1::7, 1::1, v2::7, 1::1, v3::7, 1::1, v4::7, 1::1, v5::7, 1::1, v6::7, 0::1,
562
         v7::7>>
563
     end,
564
     quote do
565
       (v7 <<< 42) + (v6 <<< 35) + (v5 <<< 28) + (v4 <<< 21) + (v3 <<< 14) + (v2 <<< 7) + v1
566
     end},
567
    {quote do
568
       <<1::1, v1::7, 1::1, v2::7, 1::1, v3::7, 1::1, v4::7, 1::1, v5::7, 1::1, v6::7, 1::1,
569
         v7::7, 0::1, v8::7>>
570
     end,
571
     quote do
572
       (v8 <<< 49) + (v7 <<< 42) + (v6 <<< 35) + (v5 <<< 28) + (v4 <<< 21) + (v3 <<< 14) +
573
         (v2 <<< 7) + v1
574
     end}
575
  ]
576

577
  @doc false
578
  @spec decode_header(binary()) ::
579
          {:ok, names :: [String.t()], types :: [term], rest :: binary} | :more
580
  def decode_header(row_binary_with_names_and_types)
581

582
  for {pattern, value} <- varints do
583
    def decode_header(<<unquote(pattern), rest::bytes>>) do
584
      decode_header_names(rest, unquote(value), unquote(value), _acc = [])
42✔
585
    end
586
  end
587

588
  def decode_header(<<_bin::bytes>>) do
1✔
589
    :more
590
  end
591

592
  defp decode_header_names(<<rest::bytes>>, 0, count, names) do
593
    decode_header_types(rest, count, _acc = [], :lists.reverse(names))
27✔
594
  end
595

596
  for {pattern, value} <- varints do
597
    defp decode_header_names(
598
           <<unquote(pattern), name::size(unquote(value))-bytes, rest::bytes>>,
599
           left,
600
           count,
601
           acc
602
         ) do
603
      decode_header_names(rest, left - 1, count, [name | acc])
84✔
604
    end
605
  end
606

607
  defp decode_header_names(<<_bin::bytes>>, _left, _count, _acc) do
15✔
608
    :more
609
  end
610

611
  defp decode_header_types(<<rest::bytes>>, 0, types, names) do
612
    {:ok, names, decoding_types_reverse(types), rest}
7✔
613
  end
614

615
  for {pattern, value} <- varints do
616
    defp decode_header_types(
617
           <<unquote(pattern), type::size(unquote(value))-bytes, rest::bytes>>,
618
           count,
619
           acc,
620
           names
621
         ) do
622
      decode_header_types(rest, count - 1, [type | acc], names)
30✔
623
    end
624
  end
625

626
  defp decode_header_types(<<_bin::bytes>>, _count, _acc, _names) do
20✔
627
    :more
628
  end
629

630
  @doc """
631
  Decodes [RowBinaryWithNamesAndTypes](https://clickhouse.com/docs/en/interfaces/formats/RowBinaryWithNamesAndTypes) into rows.
632

633
  Example:
634

635
      iex> decode_rows(<<1, 3, "1+1"::bytes, 5, "UInt8"::bytes, 2>>)
636
      [[2]]
637

638
  """
639
  def decode_rows(row_binary_with_names_and_types)
640
  def decode_rows(<<>>), do: []
1✔
641

642
  for {pattern, value} <- varints do
643
    def decode_rows(<<unquote(pattern), rest::bytes>>) do
644
      skip_names(rest, unquote(value), unquote(value))
7✔
645
    end
646
  end
647

648
  @doc """
649
  Same as `decode_rows/1` but the first element is a list of column names.
650

651
  Example:
652

653
      iex> decode_names_and_rows(<<1, 3, "1+1"::bytes, 5, "UInt8"::bytes, 2>>)
654
      [["1+1"], [2]]
655

656
  """
657
  def decode_names_and_rows(row_binary_with_names_and_types)
658

659
  for {pattern, value} <- varints do
660
    def decode_names_and_rows(<<unquote(pattern), rest::bytes>>) do
661
      decode_names(rest, unquote(value), unquote(value), _acc = [])
2,477✔
662
    end
663
  end
664

665
  @doc """
666
  Decodes [RowBinary](https://clickhouse.com/docs/en/interfaces/formats/RowBinary) into rows.
667

668
  Example:
669

670
      iex> decode_rows(<<1>>, ["UInt8"])
671
      [[1]]
672

673
  """
674
  def decode_rows(row_binary, types)
675
  def decode_rows(<<>>, _types), do: []
1✔
676

677
  def decode_rows(<<data::bytes>>, types) do
678
    decode_rows!(data, decoding_types(types))
13✔
679
  end
680

681
  defp decode_rows!(data, types) do
682
    {rows, remaining_data, state} = decode_rows(types, data, [], [], types)
2,348✔
683

684
    case state do
2,312✔
685
      nil ->
686
        rows
2,310✔
687

688
      {:cont, types_rest, row} ->
689
        raise ArgumentError, """
2✔
690
        incomplete RowBinary data: ran out of bytes while decoding
691

692
        Expected to decode: #{inspect(types_rest)}
693
        Remaining bytes: #{byte_size(remaining_data)} bytes
2✔
694
        Partial row: #{inspect(row)}
695
        Completed rows: #{length(rows)}
2✔
696
        """
697
    end
698
  end
699

700
  @doc false
701
  def decode_rows_continue(<<data::bytes>>, types, state) do
702
    case state do
201,166✔
703
      {:cont, types_rest, row} -> decode_rows(types_rest, data, row, [], types)
201,074✔
704
      nil -> decode_rows(types, data, [], [], types)
92✔
705
    end
706
  end
707

708
  @doc false
709
  def decoding_types([type | types]) do
130✔
710
    [decoding_type(type) | decoding_types(types)]
711
  end
712

713
  def decoding_types([] = done), do: done
86✔
714

715
  defp decoding_types_reverse(types), do: decoding_types_reverse(types, [])
2,342✔
716

717
  defp decoding_types_reverse([type | types], acc) do
718
    decoding_types_reverse(types, [decoding_type(type) | acc])
5,634✔
719
  end
720

721
  defp decoding_types_reverse([], acc), do: acc
2,342✔
722

723
  defp decoding_type(t) when is_binary(t) do
724
    decoding_type(Ch.Types.decode(t))
5,750✔
725
  end
726

727
  defp decoding_type(t)
728
       when t in [
729
              :string,
730
              :binary,
731
              :json,
732
              :dynamic,
733
              :boolean,
734
              :uuid,
735
              :date,
736
              :date32,
737
              :time,
738
              :time64,
739
              :ipv4,
740
              :ipv6,
741
              :point,
742
              :nothing,
743
              :bf16
744
            ],
745
       do: t
2,285✔
746

747
  defp decoding_type({:datetime, _tz} = t), do: t
32✔
748
  defp decoding_type({:fixed_string, _len} = t), do: t
22✔
749

750
  for size <- [8, 16, 32, 64, 128, 256] do
751
    defp decoding_type(unquote(:"u#{size}") = u), do: u
2,687✔
752
    defp decoding_type(unquote(:"i#{size}") = i), do: i
98✔
753
  end
754

755
  for size <- [32, 64] do
756
    defp decoding_type(unquote(:"f#{size}") = f), do: f
44✔
757
  end
758

759
  defp decoding_type(:datetime = t), do: {t, _tz = nil}
21✔
760

761
  defp decoding_type({:array = a, t}), do: {a, decoding_type(t)}
125✔
762

763
  defp decoding_type({:tuple = t, ts}) do
18✔
764
    {t, Enum.map(ts, &decoding_type/1)}
765
  end
766

767
  defp decoding_type({:variant = v, ts}) do
28✔
768
    {v, Enum.map(ts, &decoding_type/1)}
769
  end
770

771
  defp decoding_type({:map = m, kt, vt}) do
772
    {m, decoding_type(kt), decoding_type(vt)}
25✔
773
  end
774

775
  defp decoding_type({:nullable = n, t}), do: {n, decoding_type(t)}
58✔
776
  defp decoding_type({:low_cardinality, t}), do: decoding_type(t)
149✔
777

778
  defp decoding_type({:decimal = t, p, s}), do: {t, decimal_size(p), s}
472✔
779
  defp decoding_type({:decimal32, s}), do: {:decimal, 32, s}
1✔
780
  defp decoding_type({:decimal64, s}), do: {:decimal, 64, s}
1✔
781
  defp decoding_type({:decimal128, s}), do: {:decimal, 128, s}
1✔
782
  defp decoding_type({:decimal256, s}), do: {:decimal, 256, s}
1✔
783

784
  defp decoding_type({:datetime64 = t, p}), do: {t, time_unit(p), _tz = nil}
33✔
785
  defp decoding_type({:datetime64 = t, p, tz}), do: {t, time_unit(p), tz}
20✔
786

787
  defp decoding_type({:time64 = t, p}), do: {t, time_unit(p)}
120✔
788

789
  defp decoding_type({e, mappings}) when e in [:enum8, :enum16] do
17✔
790
    {e, Map.new(mappings, fn {k, v} -> {v, k} end)}
34✔
791
  end
792

793
  defp decoding_type({:simple_aggregate_function, _f, t}), do: decoding_type(t)
6✔
794

795
  defp decoding_type(:ring), do: {:array, :point}
6✔
796
  defp decoding_type(:polygon), do: {:array, {:array, :point}}
6✔
797
  defp decoding_type(:multipolygon), do: {:array, {:array, {:array, :point}}}
6✔
798

799
  defp decoding_type(type) do
800
    raise ArgumentError, "unsupported type for decoding: #{inspect(type)}"
×
801
  end
802

803
  defp skip_names(<<rest::bytes>>, 0, count), do: decode_types(rest, count, _acc = [])
7✔
804

805
  for {pattern, value} <- varints do
806
    defp skip_names(<<unquote(pattern), _::size(unquote(value))-bytes, rest::bytes>>, left, count) do
807
      skip_names(rest, left - 1, count)
79✔
808
    end
809
  end
810

811
  defp decode_names(<<rest::bytes>>, 0, count, names) do
2,477✔
812
    [:lists.reverse(names) | decode_types(rest, count, _acc = [])]
813
  end
814

815
  for {pattern, value} <- varints do
816
    defp decode_names(
817
           <<unquote(pattern), name::size(unquote(value))-bytes, rest::bytes>>,
818
           left,
819
           count,
820
           acc
821
         ) do
822
      decode_names(rest, left - 1, count, [name | acc])
5,698✔
823
    end
824
  end
825

826
  defp decode_types(<<>>, 0, _types), do: []
149✔
827

828
  defp decode_types(<<rest::bytes>>, 0, types) do
829
    decode_rows!(rest, decoding_types_reverse(types))
2,335✔
830
  end
831

832
  for {pattern, value} <- varints do
833
    defp decode_types(
834
           <<unquote(pattern), type::size(unquote(value))-bytes, rest::bytes>>,
835
           count,
836
           acc
837
         ) do
838
      decode_types(rest, count - 1, [type | acc])
5,777✔
839
    end
840
  end
841

842
  @compile inline: [decode_string_decode_rows: 5]
843

844
  for {pattern, size} <- varints do
845
    defp decode_string_decode_rows(
846
           <<unquote(pattern), s::size(unquote(size))-bytes, bin::bytes>>,
847
           types_rest,
848
           row,
849
           rows,
850
           types
851
         ) do
852
      decode_rows(types_rest, bin, [to_utf8(s) | row], rows, types)
1,550✔
853
    end
854
  end
855

856
  defp decode_string_decode_rows(<<bin::bytes>>, types_rest, row, rows, _types) do
857
    to_be_continued(rows, bin, [:string | types_rest], row)
100,165✔
858
  end
859

860
  @doc false
861
  def to_utf8(str) do
862
    utf8 = to_utf8(str, 0, 0, str, [])
1,550✔
863
    IO.iodata_to_binary(utf8)
1,550✔
864
  end
865

866
  @dialyzer {:no_improper_lists, to_utf8: 5, to_utf8_escape: 5}
867

868
  defp to_utf8(<<valid::utf8, rest::bytes>>, from, len, original, acc) do
869
    to_utf8(rest, from, len + utf8_size(valid), original, acc)
30,145,868✔
870
  end
871

872
  defp to_utf8(<<_invalid, rest::bytes>>, from, len, original, acc) do
873
    acc = [acc | binary_part(original, from, len)]
6✔
874
    to_utf8_escape(rest, from + len, 1, original, acc)
6✔
875
  end
876

877
  defp to_utf8(<<>>, from, len, original, acc) do
1,549✔
878
    [acc | binary_part(original, from, len)]
879
  end
880

881
  defp to_utf8_escape(<<valid::utf8, rest::bytes>>, from, len, original, acc) do
882
    acc = [acc | "�"]
5✔
883
    to_utf8(rest, from + len, utf8_size(valid), original, acc)
5✔
884
  end
885

886
  defp to_utf8_escape(<<_invalid, rest::bytes>>, from, len, original, acc) do
887
    to_utf8_escape(rest, from, len + 1, original, acc)
7✔
888
  end
889

890
  defp to_utf8_escape(<<>>, _from, _len, _original, acc) do
1✔
891
    [acc | "�"]
892
  end
893

894
  # UTF-8 encodes code points in one to four bytes
895
  @compile inline: [utf8_size: 1]
896
  defp utf8_size(codepoint) when codepoint <= 0x7F, do: 1
30,145,837✔
897
  defp utf8_size(codepoint) when codepoint <= 0x7FF, do: 2
13✔
898
  defp utf8_size(codepoint) when codepoint <= 0xFFFF, do: 3
23✔
899
  defp utf8_size(codepoint) when codepoint <= 0x10FFFF, do: 4
×
900

901
  @compile inline: [decode_string_json_decode_rows: 5]
902

903
  for {pattern, size} <- varints do
904
    defp decode_string_json_decode_rows(
905
           <<unquote(pattern), s::size(unquote(size))-bytes, bin::bytes>>,
906
           types_rest,
907
           row,
908
           rows,
909
           types
910
         ) do
911
      decode_rows(types_rest, bin, [Jason.decode!(s) | row], rows, types)
76✔
912
    end
913
  end
914

915
  defp decode_string_json_decode_rows(<<bin::bytes>>, types_rest, row, rows, _types) do
916
    to_be_continued(rows, bin, [:json | types_rest], row)
46✔
917
  end
918

919
  @compile inline: [decode_binary_decode_rows: 5]
920

921
  for {pattern, size} <- varints do
922
    defp decode_binary_decode_rows(
923
           <<unquote(pattern), s::size(unquote(size))-bytes, bin::bytes>>,
924
           types_rest,
925
           row,
926
           rows,
927
           types
928
         ) do
929
      decode_rows(types_rest, bin, [s | row], rows, types)
4✔
930
    end
931
  end
932

933
  defp decode_binary_decode_rows(<<bin::bytes>>, types_rest, row, rows, _types) do
934
    to_be_continued(rows, bin, [:binary | types_rest], row)
100,007✔
935
  end
936

937
  @compile inline: [decode_array_decode_rows: 6]
938
  defp decode_array_decode_rows(<<0, bin::bytes>>, _type, types_rest, row, rows, types) do
939
    decode_rows(types_rest, bin, [[] | row], rows, types)
79✔
940
  end
941

942
  for {pattern, size} <- varints do
943
    defp decode_array_decode_rows(
944
           <<unquote(pattern), bin::bytes>>,
945
           type,
946
           types_rest,
947
           row,
948
           rows,
949
           types
950
         ) do
951
      array_types = List.duplicate(type, unquote(size))
256✔
952
      types_rest = array_types ++ [{:array_over, row} | types_rest]
256✔
953
      decode_rows(types_rest, bin, [], rows, types)
256✔
954
    end
955
  end
956

957
  defp decode_array_decode_rows(<<bin::bytes>>, type, types_rest, row, rows, _types) do
958
    to_be_continued(rows, bin, [{:array, type} | types_rest], row)
12✔
959
  end
960

961
  @compile inline: [decode_map_decode_rows: 7]
962
  defp decode_map_decode_rows(
963
         <<0, bin::bytes>>,
964
         _key_type,
965
         _value_type,
966
         types_rest,
967
         row,
968
         rows,
969
         types
970
       ) do
971
    decode_rows(types_rest, bin, [%{} | row], rows, types)
10✔
972
  end
973

974
  for {pattern, size} <- varints do
975
    defp decode_map_decode_rows(
976
           <<unquote(pattern), bin::bytes>>,
977
           key_type,
978
           value_type,
979
           types_rest,
980
           row,
981
           rows,
982
           types
983
         ) do
984
      types_rest =
32✔
985
        map_types(unquote(size), key_type, value_type) ++ [{:map_over, row} | types_rest]
986

987
      decode_rows(types_rest, bin, [], rows, types)
32✔
988
    end
989
  end
990

991
  defp decode_map_decode_rows(<<bin::bytes>>, key_type, value_type, types_rest, row, rows, _types) do
992
    to_be_continued(rows, bin, [{:map, key_type, value_type} | types_rest], row)
6✔
993
  end
994

995
  defp map_types(count, key_type, value_type) when count > 0 do
57✔
996
    [key_type, value_type | map_types(count - 1, key_type, value_type)]
997
  end
998

999
  defp map_types(0, _key_type, _value_types), do: []
32✔
1000

1001
  # https://clickhouse.com/docs/sql-reference/data-types/data-types-binary-encoding
1002
  dynamic_types = [
1003
    nothing: 0x00,
1004
    u8: 0x01,
1005
    u16: 0x02,
1006
    u32: 0x03,
1007
    u64: 0x04,
1008
    u128: 0x05,
1009
    u256: 0x06,
1010
    i8: 0x07,
1011
    i16: 0x08,
1012
    i32: 0x09,
1013
    i64: 0x0A,
1014
    i128: 0x0B,
1015
    i256: 0x0C,
1016
    f32: 0x0D,
1017
    f64: 0x0E,
1018
    date: 0x0F,
1019
    date32: 0x10,
1020
    string: 0x15,
1021
    uuid: 0x1D,
1022
    ipv4: 0x28,
1023
    ipv6: 0x29,
1024
    boolean: 0x2D,
1025
    bf16: 0x31
1026
  ]
1027

1028
  # TODO compile inline?
1029

1030
  for {type, code} <- dynamic_types do
1031
    defp decode_dynamic(
1032
           <<unquote(code), rest::bytes>>,
1033
           dynamic,
1034
           types_rest,
1035
           row,
1036
           rows,
1037
           types
1038
         ) do
1039
      decode_dynamic_continue(rest, [unquote(type) | dynamic], types_rest, row, rows, types)
236✔
1040
    end
1041
  end
1042

1043
  # DateTime 0x11
1044
  defp decode_dynamic(<<0x11, rest::bytes>>, dynamic, types_rest, row, rows, types) do
1045
    decode_dynamic_continue(rest, [{:datetime, nil} | dynamic], types_rest, row, rows, types)
4✔
1046
  end
1047

1048
  # DateTime(time_zone) 0x12 <var_uint_time_zone_name_size><time_zone_name_data>
1049
  for {pattern, size} <- varints do
1050
    defp decode_dynamic(
1051
           <<0x12, unquote(pattern), tz::size(unquote(size))-bytes, rest::bytes>>,
1052
           dynamic,
1053
           types_rest,
1054
           row,
1055
           rows,
1056
           types
1057
         ) do
1058
      decode_dynamic_continue(rest, [{:datetime, tz} | dynamic], types_rest, row, rows, types)
2✔
1059
    end
1060
  end
1061

1062
  # DateTime64(P) 0x13 <uint8_precision>
1063
  defp decode_dynamic(
1064
         <<0x13, precision, rest::bytes>>,
1065
         dynamic,
1066
         types_rest,
1067
         row,
1068
         rows,
1069
         types
1070
       ) do
1071
    decode_dynamic_continue(
2✔
1072
      rest,
1073
      [decoding_type({:datetime64, precision}) | dynamic],
1074
      types_rest,
1075
      row,
1076
      rows,
1077
      types
1078
    )
1079
  end
1080

1081
  # DateTime64(P, time_zone) 0x14 <uint8_precision><var_uint_time_zone_name_size><time_zone_name_data>
1082
  for {pattern, size} <- varints do
1083
    defp decode_dynamic(
1084
           <<0x14, precision, unquote(pattern), tz::size(unquote(size))-bytes, rest::bytes>>,
1085
           dynamic,
1086
           types_rest,
1087
           row,
1088
           rows,
1089
           types
1090
         ) do
1091
      decode_dynamic_continue(
2✔
1092
        rest,
1093
        [decoding_type({:datetime64, precision, tz}) | dynamic],
1094
        types_rest,
1095
        row,
1096
        rows,
1097
        types
1098
      )
1099
    end
1100
  end
1101

1102
  # FixedString(N) 0x16 <var_uint_size>
1103
  for {pattern, size} <- varints do
1104
    defp decode_dynamic(
1105
           <<0x16, unquote(pattern), rest::bytes>>,
1106
           dynamic,
1107
           types_rest,
1108
           row,
1109
           rows,
1110
           types
1111
         ) do
1112
      decode_dynamic_continue(
4✔
1113
        rest,
1114
        [{:fixed_string, unquote(size)} | dynamic],
1115
        types_rest,
1116
        row,
1117
        rows,
1118
        types
1119
      )
1120
    end
1121
  end
1122

1123
  # Decimal32(P, S) 0x19 <uint8_precision><uint8_scale>
1124
  # Decimal64(P, S) 0x1A <uint8_precision><uint8_scale>
1125
  # Decimal128(P, S) 0x1B <uint8_precision><uint8_scale>
1126
  # Decimal256(P, S) 0x1C <uint8_precision><uint8_scale>
1127
  for {code, size} <- [{0x19, 32}, {0x1A, 64}, {0x1B, 128}, {0x1C, 256}] do
1128
    defp decode_dynamic(
1129
           <<unquote(code), _precision, scale, rest::bytes>>,
1130
           dynamic,
1131
           types_rest,
1132
           row,
1133
           rows,
1134
           types
1135
         ) do
1136
      decode_dynamic_continue(
8✔
1137
        rest,
1138
        [{:decimal, unquote(size), scale} | dynamic],
1139
        types_rest,
1140
        row,
1141
        rows,
1142
        types
1143
      )
1144
    end
1145
  end
1146

1147
  # Array(T) 0x1E <nested_type_encoding>
1148
  defp decode_dynamic(<<0x1E, rest::bytes>>, dynamic, types_rest, row, rows, types) do
1149
    decode_dynamic_continue(rest, [:array | dynamic], types_rest, row, rows, types)
58✔
1150
  end
1151

1152
  # Nullable(T)        0x23 <nested_type_encoding>
1153
  defp decode_dynamic(<<0x23, rest::bytes>>, dynamic, types_rest, row, rows, types) do
1154
    decode_dynamic_continue(rest, [:nullable | dynamic], types_rest, row, rows, types)
10✔
1155
  end
1156

1157
  # LowCardinality(T) 0x26 <nested_type_encoding>
1158
  defp decode_dynamic(<<0x26, rest::bytes>>, dynamic, types_rest, row, rows, types) do
1159
    decode_dynamic_continue(rest, [:low_cardinality | dynamic], types_rest, row, rows, types)
4✔
1160
  end
1161

1162
  # TODO
1163
  # Enum8        0x17 <var_uint_number_of_elements><var_uint_name_size_1><name_data_1><int8_value_1>...<var_uint_name_size_N><name_data_N><int8_value_N>
1164
  # Enum16        0x18 <var_uint_number_of_elements><var_uint_name_size_1><name_data_1><int16_little_endian_value_1>...><var_uint_name_size_N><name_data_N><int16_little_endian_value_N>
1165
  # Tuple(T1, ..., TN)        0x1F <var_uint_number_of_elements><nested_type_encoding_1>...<nested_type_encoding_N>
1166
  # Tuple(name1 T1, ..., nameN TN)        0x20 <var_uint_number_of_elements><var_uint_name_size_1><name_data_1><nested_type_encoding_1>...<var_uint_name_size_N><name_data_N><nested_type_encoding_N>
1167
  # Set        0x21
1168
  # Interval        0x22 <interval_kind> (see interval kind binary encoding)
1169
  # Function        0x24<var_uint_number_of_arguments><argument_type_encoding_1>...<argument_type_encoding_N><return_type_encoding>
1170
  # AggregateFunction(function_name(param_1, ..., param_N), arg_T1, ..., arg_TN)        0x25<var_uint_version><var_uint_function_name_size><function_name_data><var_uint_number_of_parameters><param_1>...<param_N><var_uint_number_of_arguments><argument_type_encoding_1>...<argument_type_encoding_N> (see aggregate function parameter binary encoding)
1171
  # Map(K, V)        0x27<key_type_encoding><value_type_encoding>
1172
  # Variant(T1, ..., TN)        0x2A<var_uint_number_of_variants><variant_type_encoding_1>...<variant_type_encoding_N>
1173
  # Dynamic(max_types=N)        0x2B<uint8_max_types>
1174
  # Custom type (Ring, Polygon, etc)        0x2C<var_uint_type_name_size><type_name_data>
1175
  # SimpleAggregateFunction(function_name(param_1, ..., param_N), arg_T1, ..., arg_TN)        0x2E<var_uint_function_name_size><function_name_data><var_uint_number_of_parameters><param_1>...<param_N><var_uint_number_of_arguments><argument_type_encoding_1>...<argument_type_encoding_N> (see aggregate function parameter binary encoding)
1176
  # Nested(name1 T1, ..., nameN TN)        0x2F<var_uint_number_of_elements><var_uint_name_size_1><name_data_1><nested_type_encoding_1>...<var_uint_name_size_N><name_data_N><nested_type_encoding_N>
1177
  # JSON(max_dynamic_paths=N, max_dynamic_types=M, path Type, SKIP skip_path, SKIP REGEXP skip_path_regexp)        0x30<uint8_serialization_version><var_int_max_dynamic_paths><uint8_max_dynamic_types><var_uint_number_of_typed_paths><var_uint_path_name_size_1><path_name_data_1><encoded_type_1>...<var_uint_number_of_skip_paths><var_uint_skip_path_size_1><skip_path_data_1>...<var_uint_number_of_skip_path_regexps><var_uint_skip_path_regexp_size_1><skip_path_data_regexp_1>...
1178

1179
  unsupported_dynamic_types = %{
1180
    "Enum8" => 0x17,
1181
    "Enum16" => 0x18,
1182
    "Tuple" => 0x1F,
1183
    "TupleWithNames" => 0x20,
1184
    "Set" => 0x21,
1185
    "Interval" => 0x22,
1186
    "Function" => 0x24,
1187
    "AggregateFunction" => 0x25,
1188
    "Map" => 0x27,
1189
    "Variant" => 0x2A,
1190
    "Dynamic" => 0x2B,
1191
    "CustomType" => 0x2C,
1192
    "SimpleAggregateFunction" => 0x2E,
1193
    "Nested" => 0x2F,
1194
    "JSON" => 0x30
1195
  }
1196

1197
  for {type, code} <- unsupported_dynamic_types do
1198
    defp decode_dynamic(<<unquote(code), _::bytes>>, _dynamic, _types_rest, _row, _rows, _types) do
1199
      raise ArgumentError, "unsupported dynamic type #{unquote(type)}"
18✔
1200
    end
1201
  end
1202

1203
  defp decode_dynamic(<<bin::bytes>>, dynamic, types_rest, row, rows, _types) do
1204
    to_be_continued(rows, bin, [{:dynamic, dynamic} | types_rest], row)
2✔
1205
  end
1206

1207
  @compile inline: [decode_dynamic_continue: 6]
1208

1209
  defp decode_dynamic_continue(<<rest::bytes>>, dynamic, types_rest, row, rows, types) do
1210
    continue? =
212✔
1211
      case dynamic do
1212
        [:array | _] -> true
58✔
1213
        [:nullable | _] -> true
10✔
1214
        [:low_cardinality | _] -> true
4✔
1215
        _ -> false
212✔
1216
      end
1217

1218
    if continue? do
212✔
1219
      decode_dynamic(rest, dynamic, types_rest, row, rows, types)
72✔
1220
    else
1221
      type = build_dynamic_type(:lists.reverse(dynamic))
212✔
1222
      decode_rows([type | types_rest], rest, row, rows, types)
212✔
1223
    end
1224
  end
1225

1226
  defp build_dynamic_type([type]), do: type
258✔
1227

1228
  defp build_dynamic_type(type) do
1229
    case type do
64✔
1230
      [:array | rest] -> {:array, build_dynamic_type(rest)}
50✔
1231
      [:nullable | rest] -> {:nullable, build_dynamic_type(rest)}
10✔
1232
      [:low_cardinality | rest] -> build_dynamic_type(rest)
4✔
1233
    end
1234
  end
1235

1236
  simple_types = %{
1237
    u8: %{pattern: quote(do: <<u>>), value: quote(do: u)},
1238
    u16: %{pattern: quote(do: <<u::16-little>>), value: quote(do: u)},
1239
    u32: %{pattern: quote(do: <<u::32-little>>), value: quote(do: u)},
1240
    u64: %{pattern: quote(do: <<u::64-little>>), value: quote(do: u)},
1241
    u128: %{pattern: quote(do: <<u::128-little>>), value: quote(do: u)},
1242
    u256: %{pattern: quote(do: <<u::256-little>>), value: quote(do: u)},
1243
    i8: %{pattern: quote(do: <<i::signed>>), value: quote(do: i)},
1244
    i16: %{pattern: quote(do: <<i::16-little-signed>>), value: quote(do: i)},
1245
    i32: %{pattern: quote(do: <<i::32-little-signed>>), value: quote(do: i)},
1246
    i64: %{pattern: quote(do: <<i::64-little-signed>>), value: quote(do: i)},
1247
    i128: %{pattern: quote(do: <<i::128-little-signed>>), value: quote(do: i)},
1248
    i256: %{pattern: quote(do: <<i::256-little-signed>>), value: quote(do: i)},
1249
    f32: [
1250
      %{pattern: quote(do: <<f::32-little-float>>), value: quote(do: f)},
1251
      %{pattern: quote(do: <<_nan_or_inf::32>>), value: quote(do: nil)}
1252
    ],
1253
    f64: [
1254
      %{pattern: quote(do: <<f::64-little-float>>), value: quote(do: f)},
1255
      %{pattern: quote(do: <<_nan_or_inf::64>>), value: quote(do: nil)}
1256
    ],
1257
    bf16: [
1258
      %{pattern: quote(do: <<bits::16-little>>), value: quote(do: bfloat16_to_float(bits))}
1259
    ],
1260
    uuid: %{
1261
      pattern: quote(do: <<u1::64-little, u2::64-little>>),
1262
      value: quote(do: <<u1::64, u2::64>>)
1263
    },
1264
    date: %{
1265
      pattern: quote(do: <<d::16-little>>),
1266
      value: quote(do: Date.from_gregorian_days(d + @epoch_gregorian_days))
1267
    },
1268
    date32: %{
1269
      pattern: quote(do: <<d::32-little-signed>>),
1270
      value: quote(do: Date.from_gregorian_days(d + @epoch_gregorian_days))
1271
    },
1272
    time: %{
1273
      pattern: quote(do: <<s::32-little-signed>>),
1274
      value: quote(do: time_after_midnight(s, 1))
1275
    },
1276
    boolean: [
1277
      %{pattern: quote(do: <<0>>), value: quote(do: false)},
1278
      %{pattern: quote(do: <<1>>), value: quote(do: true)},
1279
      %{pattern: quote(do: <<b>>), value: quote(do: raise("invalid boolean value: #{b}"))}
1280
    ],
1281
    ipv4: %{
1282
      pattern: quote(do: <<b4, b3, b2, b1>>),
1283
      value: quote(do: {b1, b2, b3, b4})
1284
    },
1285
    ipv6: %{
1286
      pattern: quote(do: <<b1::16, b2::16, b3::16, b4::16, b5::16, b6::16, b7::16, b8::16>>),
1287
      value: quote(do: {b1, b2, b3, b4, b5, b6, b7, b8})
1288
    },
1289
    point: %{
1290
      pattern: quote(do: <<x::64-little-float, y::64-little-float>>),
1291
      value: quote(do: {x, y})
1292
    }
1293
  }
1294

1295
  for {type, clauses} <- simple_types do
1296
    fun = :"decode_#{type}_decode_rows"
1297
    @compile inline: [{fun, 5}]
1298

1299
    for %{pattern: pattern, value: value} <- List.wrap(clauses) do
1300
      defp unquote(fun)(<<unquote(pattern), rest::bytes>>, types_rest, row, rows, types) do
1301
        decode_rows(types_rest, rest, [unquote(value) | row], rows, types)
2,158,823✔
1302
      end
1303
    end
1304

1305
    defp unquote(fun)(<<bin::bytes>>, types_rest, row, rows, _types) do
1306
      to_be_continued(rows, bin, [unquote(type) | types_rest], row)
582✔
1307
    end
1308
  end
1309

1310
  defp decode_rows([type | types_rest], <<bin::bytes>>, row, rows, types) do
1311
    case type do
2,363,629✔
1312
      :u8 ->
1313
        decode_u8_decode_rows(bin, types_rest, row, rows, types)
1,235✔
1314

1315
      :u16 ->
1316
        decode_u16_decode_rows(bin, types_rest, row, rows, types)
1,524✔
1317

1318
      :u32 ->
1319
        decode_u32_decode_rows(bin, types_rest, row, rows, types)
73✔
1320

1321
      :u64 ->
1322
        decode_u64_decode_rows(bin, types_rest, row, rows, types)
2,150,989✔
1323

1324
      :u128 ->
1325
        decode_u128_decode_rows(bin, types_rest, row, rows, types)
40✔
1326

1327
      :u256 ->
1328
        decode_u256_decode_rows(bin, types_rest, row, rows, types)
72✔
1329

1330
      :i8 ->
1331
        decode_i8_decode_rows(bin, types_rest, row, rows, types)
40✔
1332

1333
      :i16 ->
1334
        decode_i16_decode_rows(bin, types_rest, row, rows, types)
20✔
1335

1336
      :i32 ->
1337
        decode_i32_decode_rows(bin, types_rest, row, rows, types)
42✔
1338

1339
      :i64 ->
1340
        decode_i64_decode_rows(bin, types_rest, row, rows, types)
170✔
1341

1342
      :i128 ->
1343
        decode_i128_decode_rows(bin, types_rest, row, rows, types)
41✔
1344

1345
      :i256 ->
1346
        decode_i256_decode_rows(bin, types_rest, row, rows, types)
73✔
1347

1348
      :f32 ->
1349
        decode_f32_decode_rows(bin, types_rest, row, rows, types)
36✔
1350

1351
      :f64 ->
1352
        decode_f64_decode_rows(bin, types_rest, row, rows, types)
60✔
1353

1354
      :bf16 ->
1355
        decode_bf16_decode_rows(bin, types_rest, row, rows, types)
4,436✔
1356

1357
      :string ->
1358
        decode_string_decode_rows(bin, types_rest, row, rows, types)
101,715✔
1359

1360
      :binary ->
1361
        decode_binary_decode_rows(bin, types_rest, row, rows, types)
100,011✔
1362

1363
      :json ->
1364
        # assuming it arrives as text and not "native" binary JSON
1365
        # i.e. assumes `settings: [output_format_binary_write_json_as_string: 1]`
1366
        # TODO
1367
        decode_string_json_decode_rows(bin, types_rest, row, rows, types)
122✔
1368

1369
      :dynamic ->
1370
        decode_dynamic(bin, _dynamic = [], types_rest, row, rows, types)
276✔
1371

1372
      {:dynamic, dynamic} ->
1373
        decode_dynamic(bin, dynamic, types_rest, row, rows, types)
2✔
1374

1375
      # TODO utf8?
1376
      {:fixed_string, size} ->
1377
        case bin do
43✔
1378
          <<s::size(^size)-bytes, rest::bytes>> ->
1379
            decode_rows(types_rest, rest, [s | row], rows, types)
29✔
1380

1381
          _ ->
1382
            to_be_continued(rows, bin, [type | types_rest], row)
14✔
1383
        end
1384

1385
      :boolean ->
1386
        decode_boolean_decode_rows(bin, types_rest, row, rows, types)
66✔
1387

1388
      :uuid ->
1389
        decode_uuid_decode_rows(bin, types_rest, row, rows, types)
86✔
1390

1391
      :date ->
1392
        decode_date_decode_rows(bin, types_rest, row, rows, types)
42✔
1393

1394
      :date32 ->
1395
        decode_date32_decode_rows(bin, types_rest, row, rows, types)
40✔
1396

1397
      :time ->
1398
        decode_time_decode_rows(bin, types_rest, row, rows, types)
46✔
1399

1400
      {:time64, time_unit} ->
1401
        case bin do
180✔
1402
          <<ticks::64-little-signed, bin::bytes>> ->
1403
            time = time_after_midnight(ticks, time_unit)
150✔
1404
            decode_rows(types_rest, bin, [time | row], rows, types)
142✔
1405

1406
          _ ->
1407
            to_be_continued(rows, bin, [type | types_rest], row)
30✔
1408
        end
1409

1410
      {:datetime, timezone} ->
1411
        case bin do
88✔
1412
          <<s::32-little, bin::bytes>> ->
1413
            dt = DateTime.from_unix!(s)
66✔
1414

1415
            dt =
66✔
1416
              case timezone do
1417
                nil -> DateTime.to_naive(dt)
26✔
1418
                "UTC" -> dt
22✔
1419
                _ -> DateTime.shift_zone!(dt, timezone)
18✔
1420
              end
1421

1422
            decode_rows(types_rest, bin, [dt | row], rows, types)
64✔
1423

1424
          _ ->
1425
            to_be_continued(rows, bin, [type | types_rest], row)
22✔
1426
        end
1427

1428
      {:decimal, size, scale} ->
1429
        case bin do
608✔
1430
          <<val::size(^size)-little-signed, bin::bytes>> ->
1431
            sign = if val < 0, do: -1, else: 1
490✔
1432
            d = Decimal.new(sign, abs(val), -scale)
490✔
1433
            decode_rows(types_rest, bin, [d | row], rows, types)
490✔
1434

1435
          _ ->
1436
            to_be_continued(rows, bin, [type | types_rest], row)
118✔
1437
        end
1438

1439
      {:nullable, inner_type} ->
1440
        case bin do
175✔
1441
          <<b, bin::bytes>> ->
1442
            case b do
172✔
1443
              0 -> decode_rows([inner_type | types_rest], bin, row, rows, types)
79✔
1444
              1 -> decode_rows(types_rest, bin, [nil | row], rows, types)
93✔
1445
            end
1446

1447
          _ ->
1448
            to_be_continued(rows, bin, [type | types_rest], row)
3✔
1449
        end
1450

1451
      :nothing ->
1452
        decode_rows(types_rest, bin, [nil | row], rows, types)
54✔
1453

1454
      {:array, inner_type} ->
1455
        decode_array_decode_rows(bin, inner_type, types_rest, row, rows, types)
347✔
1456

1457
      {:array_over, original_row} ->
1458
        decode_rows(types_rest, bin, [:lists.reverse(row) | original_row], rows, types)
254✔
1459

1460
      {:map, key_type, value_type} ->
1461
        decode_map_decode_rows(bin, key_type, value_type, types_rest, row, rows, types)
48✔
1462

1463
      {:map_over, original_row} ->
1464
        map = row |> Enum.chunk_every(2) |> Enum.map(fn [v, k] -> {k, v} end) |> Map.new()
32✔
1465
        decode_rows(types_rest, bin, [map | original_row], rows, types)
32✔
1466

1467
      {:tuple, tuple_types} ->
1468
        decode_rows(tuple_types ++ [{:tuple_over, row} | types_rest], bin, [], rows, types)
26✔
1469

1470
      {:tuple_over, original_row} ->
1471
        tuple = row |> :lists.reverse() |> List.to_tuple()
26✔
1472
        decode_rows(types_rest, bin, [tuple | original_row], rows, types)
26✔
1473

1474
      {:variant, variant_types} ->
1475
        case bin do
59✔
1476
          <<255, bin::bytes>> ->
1477
            # 255 is the variant type index for "nothing"
1478
            decode_rows(types_rest, bin, [nil | row], rows, types)
12✔
1479

1480
          # TODO varint?
1481
          <<variant_type_index::8, bin::bytes>> ->
1482
            variant_type = Enum.at(variant_types, variant_type_index)
44✔
1483
            decode_rows([variant_type | types_rest], bin, row, rows, types)
44✔
1484

1485
          _ ->
1486
            to_be_continued(rows, bin, [type | types_rest], row)
3✔
1487
        end
1488

1489
      {:datetime64, time_unit, timezone} ->
1490
        case bin do
122✔
1491
          <<s::64-little-signed, bin::bytes>> ->
1492
            dt = DateTime.from_unix!(s, time_unit)
60✔
1493

1494
            dt =
60✔
1495
              case timezone do
1496
                nil -> DateTime.to_naive(dt)
34✔
1497
                "UTC" -> dt
8✔
1498
                _ -> DateTime.shift_zone!(dt, timezone)
18✔
1499
              end
1500

1501
            decode_rows(types_rest, bin, [dt | row], rows, types)
60✔
1502

1503
          _ ->
1504
            to_be_continued(rows, bin, [type | types_rest], row)
62✔
1505
        end
1506

1507
      {:enum8, mapping} ->
1508
        case bin do
28✔
1509
          <<v::signed, bin::bytes>> ->
1510
            decode_rows(types_rest, bin, [Map.fetch!(mapping, v) | row], rows, types)
28✔
1511

1512
          _ ->
1513
            to_be_continued(rows, bin, [type | types_rest], row)
×
1514
        end
1515

1516
      {:enum16, mapping} ->
1517
        case bin do
8✔
1518
          <<v::16-little-signed, bin::bytes>> ->
1519
            decode_rows(types_rest, bin, [Map.fetch!(mapping, v) | row], rows, types)
4✔
1520

1521
          _ ->
1522
            to_be_continued(rows, bin, [type | types_rest], row)
4✔
1523
        end
1524

1525
      :ipv4 ->
1526
        decode_ipv4_decode_rows(bin, types_rest, row, rows, types)
24✔
1527

1528
      :ipv6 ->
1529
        decode_ipv6_decode_rows(bin, types_rest, row, rows, types)
74✔
1530

1531
      :point ->
1532
        decode_point_decode_rows(bin, types_rest, row, rows, types)
176✔
1533
    end
1534
  end
1535

1536
  defp decode_rows([], <<>> = empty, row, rows, _types) do
1537
    rows = :lists.reverse([:lists.reverse(row) | rows])
2,402✔
1538
    {rows, empty, _no_state = nil}
2,402✔
1539
  end
1540

1541
  defp decode_rows([], <<bin::bytes>>, row, rows, types) do
1542
    row = :lists.reverse(row)
2,154,943✔
1543
    decode_rows(types, bin, [], [row | rows], types)
2,154,943✔
1544
  end
1545

1546
  defp decode_rows([_ | _] = types_rest, <<>> = empty, row, rows, _types) do
1547
    to_be_continued(rows, empty, types_rest, row)
×
1548
  end
1549

1550
  @compile inline: [to_be_continued: 4]
1551
  defp to_be_continued(rows, bin, types_rest, row) do
1552
    {:lists.reverse(rows), bin, {:cont, types_rest, row}}
200,719✔
1553
  end
1554

1555
  @compile inline: [decimal_size: 1]
1556
  # https://clickhouse.com/docs/en/sql-reference/data-types/decimal/
1557
  defp decimal_size(precision) when is_integer(precision) do
1558
    cond do
472✔
1559
      precision >= 39 -> 256
414✔
1560
      precision >= 19 -> 128
58✔
1561
      precision >= 10 -> 64
49✔
1562
      true -> 32
33✔
1563
    end
1564
  end
1565

1566
  @compile inline: [time_unit: 1]
1567
  for precision <- 0..9 do
1568
    time_unit = Integer.pow(10, precision)
1569
    defp time_unit(unquote(precision)), do: unquote(time_unit)
183✔
1570
  end
1571

1572
  @compile inline: [time_after_midnight: 2]
1573
  defp time_after_midnight(ticks, time_unit) do
1574
    if ticks >= 0 and ticks < 86400 * time_unit do
182✔
1575
      ticks |> DateTime.from_unix!(time_unit) |> DateTime.to_time()
166✔
1576
    else
1577
      # since ClickHouse supports Time64 values of [-999:59:59.999999999, 999:59:59.999999999]
1578
      # and Elixir's Time supports values of [00:00:00.000000, 23:59:59.999999]
1579
      # we raise an error when ClickHouse's Time64 value is out of Elixir's Time range
1580
      raise ArgumentError,
8✔
1581
            "ClickHouse Time value #{:erlang.float_to_binary(ticks / time_unit, [:short])} (seconds) is out of Elixir's Time range (00:00:00.000000 - 23:59:59.999999)"
8✔
1582

1583
      # TODO: we could potentially decode ClickHouse's Time/Time64 values as Elixir's Duration when it's out of Elixir's Time range
1584
    end
1585
  end
1586
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc