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

plausible / ch / 924d867354145e11e28e765fc379e7fcc978c4a9-PR-397

03 Aug 2026 12:52PM UTC coverage: 97.573% (-0.5%) from 98.062%
924d867354145e11e28e765fc379e7fcc978c4a9-PR-397

Pull #397

github

ruslandoga
Merge remote-tracking branch 'origin/master' into rd/tuple-decoding-frames

# Conflicts:
#	lib/ch/row_binary.ex
Pull Request #397: Use active sequences for container decoding

93 of 95 new or added lines in 1 file covered. (97.89%)

2 existing lines in 1 file now uncovered.

764 of 783 relevant lines covered (97.57%)

16046.82 hits per line

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

99.01
/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
5✔
14
    [encode(:varint, length(names)), encode_many(names, :string), encode_types(types)]
15
  end
16

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

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

27
  defp encode_types([] = done), do: done
5✔
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))
22✔
46
  end
47

48
  defp _encode_row([el | els], [type | types]), do: [encode(type, el) | _encode_row(els, types)]
106✔
49
  defp _encode_row([] = done, []), do: done
22✔
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))
854✔
68
  end
69

70
  @doc false
71
  def _encode_rows([row | rows], types), do: _encode_rows(row, types, rows, types)
4,774✔
72
  def _encode_rows([] = done, _types), do: done
854✔
73

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

78
  defp _encode_rows([], [], rows, types), do: _encode_rows(rows, types)
4,774✔
79

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

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

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

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

109
  defp encoding_type({:datetime = d, "UTC"}), do: d
2✔
110

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

115
  defp encoding_type({:fixed_string, _len} = t), do: t
311✔
116

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

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

126
  defp encoding_type({:array = a, t}), do: {a, encoding_type(t)}
548✔
127

128
  defp encoding_type({:tuple = t, ts}) do
5✔
129
    {t, Enum.map(ts, &encoding_type/1)}
130
  end
131

132
  defp encoding_type({:variant = v, ts}) do
3✔
133
    {v, Enum.map(ts, &encoding_type/1)}
134
  end
135

136
  defp encoding_type({:map = m, kt, vt}) do
137
    {m, encoding_type(kt), encoding_type(vt)}
8✔
138
  end
139

140
  defp encoding_type({:nullable = n, t}), do: {n, encoding_type(t)}
113✔
141
  defp encoding_type({:low_cardinality, t}), do: encoding_type(t)
202✔
142

143
  defp encoding_type({:decimal, p, s}) do
144
    case decimal_size(p) do
5✔
145
      32 -> {:decimal32, s}
1✔
146
      64 -> {:decimal64, s}
2✔
147
      128 -> {:decimal128, s}
1✔
148
      256 -> {:decimal256, s}
1✔
149
    end
150
  end
151

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

156
  defp encoding_type({:datetime64 = t, p}), do: {t, time_unit(p)}
1✔
157

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

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

164
  defp encoding_type({:time64 = t, p}), do: {t, time_unit(p)}
105✔
165

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

170
  defp encoding_type({:simple_aggregate_function, _f, t}), do: encoding_type(t)
1✔
171

172
  defp encoding_type(:ring), do: {:array, :point}
1✔
173
  defp encoding_type(:polygon), do: {:array, {:array, :point}}
1✔
174
  defp encoding_type(:multipolygon), do: {:array, {:array, {:array, :point}}}
1✔
175

176
  defp encoding_type(type) do
177
    raise ArgumentError, "unsupported type for encoding: #{inspect(type)}"
1✔
178
  end
179

180
  @doc false
181
  def encode(type, value)
182

183
  def encode(:varint, i) when is_integer(i) and i >= 0 and i < 128, do: i
8,026✔
184
  def encode(:varint, i) when is_integer(i) and i >= 0, do: encode_varint_cont(i)
12✔
185

186
  def encode(:varint, i) when is_integer(i) do
187
    raise ArgumentError, "invalid varint: #{inspect(i)}"
1✔
188
  end
189

190
  def encode(:string, str) do
191
    case str do
5,962✔
192
      _ when is_binary(str) -> [encode(:varint, byte_size(str)) | str]
5,928✔
193
      _ when is_list(str) -> [encode(:varint, IO.iodata_length(str)) | str]
25✔
194
      nil -> 0
3✔
195
    end
196
  end
197

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

205
  def encode({:fixed_string, size}, str) when byte_size(str) == size do
206
    str
800✔
207
  end
208

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

214
  def encode({:fixed_string, size}, nil), do: <<0::size(size * 8)>>
2✔
215

216
  # UInt8 — [0 : 255]
217
  def encode(:u8, u) when is_integer(u) and u >= 0 and u <= 255, do: u
4,524✔
218
  def encode(:u8, nil), do: 0
4✔
219

220
  def encode(:u8, term) do
221
    raise ArgumentError, "invalid UInt8: #{inspect(term)}"
7✔
222
  end
223

224
  # Int8 — [-128 : 127]
225
  def encode(:i8, i) when is_integer(i) and i >= 0 and i <= 127, do: i
26✔
226
  def encode(:i8, i) when is_integer(i) and i < 0 and i >= -128, do: <<i::signed>>
10✔
227
  def encode(:i8, nil), do: 0
1✔
228

229
  def encode(:i8, term) do
230
    raise ArgumentError, "invalid Int8: #{inspect(term)}"
6✔
231
  end
232

233
  for size <- [16, 32, 64, 128, 256] do
234
    unsigned_max = (1 <<< size) - 1
235
    signed_min = -(1 <<< (size - 1))
236
    signed_max = (1 <<< (size - 1)) - 1
237
    uint = :"u#{size}"
238
    int = :"i#{size}"
239

240
    def encode(unquote(uint), u) when is_integer(u) and u >= 0 and u <= unquote(unsigned_max) do
241
      <<u::unquote(size)-little>>
144✔
242
    end
243

244
    def encode(unquote(int), i)
245
        when is_integer(i) and i >= unquote(signed_min) and i <= unquote(signed_max) do
246
      <<i::unquote(size)-little-signed>>
150✔
247
    end
248

249
    def encode(unquote(uint), nil), do: <<0::unquote(size)>>
3✔
250
    def encode(unquote(int), nil), do: <<0::unquote(size)>>
3✔
251

252
    def encode(unquote(uint), term) do
253
      raise ArgumentError, "invalid UInt#{unquote(size)}: #{inspect(term)}"
15✔
254
    end
255

256
    def encode(unquote(int), term) do
257
      raise ArgumentError, "invalid Int#{unquote(size)}: #{inspect(term)}"
15✔
258
    end
259
  end
260

261
  for size <- [32, 64] do
262
    type = :"f#{size}"
263

264
    def encode(unquote(type), f) when is_number(f) do
265
      <<f::unquote(size)-little-signed-float>>
1,156✔
266
    end
267

268
    def encode(unquote(type), nil), do: <<0::unquote(size)>>
4✔
269
  end
270

271
  def encode({:decimal, precision, scale}, decimal) do
272
    type =
4✔
273
      case decimal_size(precision) do
274
        32 -> :decimal32
1✔
275
        64 -> :decimal64
1✔
276
        128 -> :decimal128
1✔
277
        256 -> :decimal256
1✔
278
      end
279

280
    encode({type, scale}, decimal)
4✔
281
  end
282

283
  for size <- [32, 64, 128, 256] do
284
    type = :"decimal#{size}"
285

286
    def encode({unquote(type), scale} = t, %Decimal{sign: sign, coef: coef, exp: exp} = d) do
287
      cond do
29✔
288
        scale == -exp ->
289
          i = sign * coef
20✔
290
          <<i::unquote(size)-little>>
20✔
291

292
        exp >= 0 ->
9✔
293
          i = sign * coef * Integer.pow(10, exp + scale)
1✔
294
          <<i::unquote(size)-little>>
1✔
295

296
        true ->
8✔
297
          encode(t, Decimal.round(d, scale))
8✔
298
      end
299
    end
300

301
    def encode({unquote(type), _scale}, nil), do: <<0::unquote(size)>>
4✔
302
  end
303

304
  def encode(:boolean, true), do: 1
959✔
305
  def encode(:boolean, false), do: 0
902✔
306
  def encode(:boolean, nil), do: 0
1✔
307

308
  def encode({:array, type}, [_ | _] = l) do
2,064✔
309
    [encode(:varint, length(l)) | encode_many(l, type)]
310
  end
311

312
  def encode({:array, _type}, []), do: 0
277✔
313
  def encode({:array, _type}, nil), do: 0
4✔
314

315
  def encode({:map, k, v}, [_ | _] = m) do
1✔
316
    [encode(:varint, length(m)) | encode_many_kv(m, k, v)]
317
  end
318

319
  def encode({:map, k, v}, m) when is_map(m) do
14✔
320
    [
321
      encode(:varint, map_size(m))
322
      | :maps.fold(fn key, value, acc -> [encode(k, key), encode(v, value) | acc] end, [], m)
15✔
323
    ]
324
  end
325

326
  def encode({:map, _k, _v}, []), do: 0
1✔
327
  def encode({:map, _k, _v}, nil), do: 0
1✔
328

329
  def encode({:tuple, _types} = t, v) when is_tuple(v) do
330
    encode(t, Tuple.to_list(v))
11✔
331
  end
332

333
  def encode({:tuple, types}, values) when is_list(types) and is_list(values) do
334
    encode_row(values, types)
11✔
335
  end
336

337
  def encode({:tuple, types}, nil) when is_list(types) do
338
    Enum.map(types, fn type -> encode(type, nil) end)
1✔
339
  end
340

341
  def encode({:variant, _types}, nil), do: 255
3✔
342

343
  def encode({:variant, types}, value) do
344
    try_encode_variant(types, 0, value)
8✔
345
  end
346

347
  def encode(:datetime, %NaiveDateTime{} = datetime) do
348
    {seconds, _micros} = NaiveDateTime.to_gregorian_seconds(datetime)
17✔
349
    <<seconds - @epoch_gregorian_seconds::32-little>>
17✔
350
  end
351

352
  def encode(:datetime, %DateTime{} = datetime) do
353
    <<DateTime.to_unix(datetime, :second)::32-little>>
5✔
354
  end
355

356
  def encode(:datetime, nil), do: <<0::32>>
1✔
357

358
  def encode({:datetime64, time_unit}, %NaiveDateTime{} = datetime) do
359
    {seconds, micros} = NaiveDateTime.to_gregorian_seconds(datetime)
4✔
360

361
    <<(seconds - @epoch_gregorian_seconds) * time_unit + div(micros * time_unit, 1_000_000)::64-little-signed>>
4✔
362
  end
363

364
  def encode({:datetime64, time_unit}, %DateTime{} = datetime) do
365
    <<DateTime.to_unix(datetime, time_unit)::64-little-signed>>
5✔
366
  end
367

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

370
  def encode(:date, %Date{} = date) do
371
    <<Date.to_gregorian_days(date) - @epoch_gregorian_days::16-little>>
14✔
372
  end
373

374
  def encode(:date, nil), do: <<0::16>>
1✔
375

376
  def encode(:date32, %Date{} = date) do
377
    <<Date.to_gregorian_days(date) - @epoch_gregorian_days::32-little-signed>>
8✔
378
  end
379

380
  def encode(:date32, nil), do: <<0::32>>
1✔
381

382
  def encode(:time, %Time{} = time) do
383
    {s, _micros} = Time.to_seconds_after_midnight(time)
107✔
384
    <<s::32-little-signed>>
107✔
385
  end
386

387
  def encode(:time, nil), do: <<0::32>>
1✔
388

389
  def encode({:time64, time_unit}, %Time{} = time) do
390
    {s, micros} = Time.to_seconds_after_midnight(time)
117✔
391

392
    micros_as_ticks =
117✔
393
      cond do
394
        time_unit < 1_000_000 -> div(micros, div(1_000_000, time_unit))
74✔
395
        time_unit == 1_000_000 -> micros
43✔
396
        true -> micros * div(time_unit, 1_000_000)
34✔
397
      end
398

399
    ticks = s * time_unit + micros_as_ticks
117✔
400
    <<ticks::64-little-signed>>
117✔
401
  end
402

403
  def encode({:time64, _time_unit}, nil), do: <<0::64>>
1✔
404

405
  def encode(:uuid, <<u1::64, u2::64>>), do: <<u1::64-little, u2::64-little>>
12✔
406

407
  def encode(
408
        :uuid,
409
        <<a1, a2, a3, a4, a5, a6, a7, a8, ?-, b1, b2, b3, b4, ?-, c1, c2, c3, c4, ?-, d1, d2, d3,
410
          d4, ?-, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12>>
411
      ) do
412
    raw =
2✔
413
      <<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,
414
        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,
415
        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,
416
        d(e8)::4, d(e9)::4, d(e10)::4, d(e11)::4, d(e12)::4>>
417

418
    encode(:uuid, raw)
2✔
419
  end
420

421
  def encode(:uuid, nil), do: <<0::128>>
1✔
422

423
  def encode(:ipv4, {a, b, c, d}), do: [d, c, b, a]
6✔
424
  def encode(:ipv4, nil), do: <<0::32>>
1✔
425

426
  def encode(:ipv6, {b1, b2, b3, b4, b5, b6, b7, b8}) do
427
    <<b1::16, b2::16, b3::16, b4::16, b5::16, b6::16, b7::16, b8::16>>
6✔
428
  end
429

430
  def encode(:ipv6, <<_::128>> = encoded), do: encoded
1✔
431
  def encode(:ipv6, nil), do: <<0::128>>
1✔
432

433
  def encode(:point, {x, y}), do: [encode(:f64, x) | encode(:f64, y)]
22✔
434
  def encode(:point, nil), do: <<0::128>>
1✔
435
  def encode(:ring, points), do: encode({:array, :point}, points)
1✔
436
  def encode(:polygon, rings), do: encode({:array, :ring}, rings)
1✔
437
  def encode(:multipolygon, polygons), do: encode({:array, :polygon}, polygons)
1✔
438

439
  # TODO
440
  def encode(:dynamic, value) do
441
    case value do
12✔
442
      _ when is_binary(value) -> [0x15 | encode(:string, value)]
2✔
443
      _ when is_integer(value) and value >= 0 -> [0x04 | encode(:u64, value)]
3✔
444
      _ when is_integer(value) -> [0x0A | encode(:i64, value)]
1✔
445
      _ when is_float(value) -> [0x0E | encode(:f64, value)]
2✔
446
      %Date{} -> [0x0F | encode(:date, value)]
2✔
447
      %NaiveDateTime{} -> [0x11 | encode(:datetime, value)]
1✔
448
      [] -> [0x1E, 0x00]
1✔
449
    end
450
  end
451

452
  # TODO enum8 and enum16 nil
453
  for size <- [8, 16] do
454
    enum_t = :"enum#{size}"
455
    int_t = :"i#{size}"
456

457
    def encode({unquote(enum_t), mapping}, e) do
458
      i =
12✔
459
        case e do
460
          _ when is_integer(e) ->
461
            e
2✔
462

463
          _ when is_binary(e) ->
464
            case Map.fetch(mapping, e) do
10✔
465
              {:ok, res} ->
466
                res
9✔
467

468
              :error ->
469
                raise ArgumentError,
1✔
470
                      "enum value #{inspect(e)} not found in mapping: #{inspect(mapping)}"
471
            end
472
        end
473

474
      encode(unquote(int_t), i)
11✔
475
    end
476
  end
477

478
  def encode({:nullable, _type}, nil), do: 1
889✔
479

480
  def encode({:nullable, type}, value) do
481
    case encode(type, value) do
840✔
482
      e when is_list(e) or is_binary(e) -> [0 | e]
839✔
483
      e -> [0, e]
1✔
484
    end
485
  end
486

487
  defp encode_varint_cont(i) when i < 128, do: <<i>>
12✔
488

489
  defp encode_varint_cont(i) do
17✔
490
    [(i &&& 0b0111_1111) ||| 0b1000_0000 | encode_varint_cont(i >>> 7)]
491
  end
492

493
  defp encode_many([el | rest], type), do: [encode(type, el) | encode_many(rest, type)]
8,986✔
494
  defp encode_many([] = done, _type), do: done
2,069✔
495

496
  defp encode_many_kv([{key, value} | rest], key_type, value_type) do
1✔
497
    [
498
      encode(key_type, key),
499
      encode(value_type, value)
500
      | encode_many_kv(rest, key_type, value_type)
501
    ]
502
  end
503

504
  defp encode_many_kv([] = done, _key_type, _value_type), do: done
1✔
505

506
  # TODO find a better way than try/rescue
507
  defp try_encode_variant([type | types], idx, value) do
508
    try do
13✔
509
      encode(type, value)
13✔
510
    else
511
      encoded -> [idx | encoded]
7✔
512
    rescue
513
      _e -> try_encode_variant(types, idx + 1, value)
6✔
514
    end
515
  end
516

517
  defp try_encode_variant([], _idx, value) do
518
    raise ArgumentError, "no matching type found for encoding #{inspect(value)} as Variant"
1✔
519
  end
520

521
  @compile {:inline, d: 1}
522

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

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

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

579
  for {pattern, value} <- varints do
580
    def decode_header(<<unquote(pattern), rest::bytes>>) do
581
      decode_header_names(rest, unquote(value), unquote(value), _acc = [])
36✔
582
    end
583
  end
584

585
  def decode_header(<<_bin::bytes>>) do
1✔
586
    :more
587
  end
588

589
  defp decode_header_names(<<rest::bytes>>, 0, count, names) do
590
    decode_header_types(rest, count, _acc = [], :lists.reverse(names))
21✔
591
  end
592

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

604
  defp decode_header_names(<<_bin::bytes>>, _left, _count, _acc) do
15✔
605
    :more
606
  end
607

608
  defp decode_header_types(<<rest::bytes>>, 0, types, names) do
609
    {:ok, names, decoding_types_reverse(types), rest}
1✔
610
  end
611

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

623
  defp decode_header_types(<<_bin::bytes>>, _count, _acc, _names) do
20✔
624
    :more
625
  end
626

627
  @doc """
628
  Decodes [RowBinaryWithNamesAndTypes](https://clickhouse.com/docs/en/interfaces/formats/RowBinaryWithNamesAndTypes) into rows.
629

630
  Example:
631

632
      iex> decode_rows(<<1, 3, "1+1"::bytes, 5, "UInt8"::bytes, 2>>)
633
      [[2]]
634

635
  """
636
  def decode_rows(row_binary_with_names_and_types)
637
  def decode_rows(<<>>), do: []
1✔
638

639
  for {pattern, value} <- varints do
640
    def decode_rows(<<unquote(pattern), rest::bytes>>) do
641
      skip_names(rest, unquote(value), unquote(value))
5✔
642
    end
643
  end
644

645
  @doc """
646
  Same as `decode_rows/1` but the first element is a list of column names.
647

648
  Example:
649

650
      iex> decode_names_and_rows(<<1, 3, "1+1"::bytes, 5, "UInt8"::bytes, 2>>)
651
      [["1+1"], [2]]
652

653
  """
654
  def decode_names_and_rows(row_binary_with_names_and_types)
655

656
  for {pattern, value} <- varints do
657
    def decode_names_and_rows(<<unquote(pattern), rest::bytes>>) do
658
      decode_names(rest, unquote(value), unquote(value), _acc = [])
3,030✔
659
    end
660
  end
661

662
  @doc """
663
  Decodes [RowBinary](https://clickhouse.com/docs/en/interfaces/formats/RowBinary) into rows.
664

665
  Example:
666

667
      iex> decode_rows(<<1>>, ["UInt8"])
668
      [[1]]
669

670
  """
671
  def decode_rows(row_binary, types)
672
  def decode_rows(<<>>, _types), do: []
1✔
673

674
  def decode_rows(<<data::bytes>>, types) do
675
    decode_rows!(data, decoding_types(types))
436✔
676
  end
677

678
  defp decode_rows!(data, types) do
679
    {rows, remaining_data, state} =
3,430✔
680
      decode_rows(types, data, [], [], types, :row, types, 1, [])
681

682
    case state do
3,412✔
683
      nil ->
684
        rows
3,410✔
685

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

690
        Expected to decode: #{inspect(types_rest)}
691
        Remaining bytes: #{byte_size(remaining_data)} bytes
2✔
692
        Partial row: #{inspect(row)}
693
        Completed rows: #{length(rows)}
2✔
694
        """
695

696
      {:cont, types_rest, row, _kind, _schema, _left, _outer} ->
NEW
697
        raise ArgumentError, """
×
698
        incomplete RowBinary data: ran out of bytes while decoding
699

700
        Expected to decode: #{inspect(types_rest)}
UNCOV
701
        Remaining bytes: #{byte_size(remaining_data)} bytes
×
702
        Partial row: #{inspect(row)}
UNCOV
703
        Completed rows: #{length(rows)}
×
704
        """
705
    end
706
  end
707

708
  @doc false
709
  def decode_rows_continue(<<data::bytes>>, types, state) do
710
    case state do
201,104✔
711
      {:cont, types_rest, row} ->
712
        decode_rows(types_rest, data, row, [], types, :row, types, 1, [])
200,907✔
713

714
      {:cont, types_rest, row, kind, schema, left, outer} ->
715
        decode_rows(types_rest, data, row, [], types, kind, schema, left, outer)
139✔
716

717
      nil ->
718
        decode_rows(types, data, [], [], types, :row, types, 1, [])
58✔
719
    end
720
  end
721

722
  @doc false
723
  def decoding_types([type | types]) do
553✔
724
    [decoding_type(type) | decoding_types(types)]
725
  end
726

727
  def decoding_types([] = done), do: done
508✔
728

729
  defp decoding_types_reverse(types), do: decoding_types_reverse(types, [])
2,996✔
730

731
  defp decoding_types_reverse([type | types], acc) do
732
    decoding_types_reverse(types, [decoding_type(type) | acc])
16,415✔
733
  end
734

735
  defp decoding_types_reverse([], acc), do: acc
2,996✔
736

737
  defp decoding_type(t) when is_binary(t) do
738
    decoding_type(Ch.Types.decode(t))
16,732✔
739
  end
740

741
  defp decoding_type(t)
742
       when t in [
743
              :string,
744
              :json,
745
              :dynamic,
746
              :boolean,
747
              :uuid,
748
              :date,
749
              :date32,
750
              :time,
751
              :time64,
752
              :ipv4,
753
              :ipv6,
754
              :point,
755
              :nothing
756
            ],
757
       do: t
3,203✔
758

759
  defp decoding_type({:datetime, _tz} = t), do: t
20✔
760
  defp decoding_type({:fixed_string, _len} = t), do: t
424✔
761

762
  for size <- [8, 16, 32, 64, 128, 256] do
763
    defp decoding_type(unquote(:"u#{size}") = u), do: u
12,177✔
764
    defp decoding_type(unquote(:"i#{size}") = i), do: i
423✔
765
  end
766

767
  for size <- [32, 64] do
768
    defp decoding_type(unquote(:"f#{size}") = f), do: f
440✔
769
  end
770

771
  defp decoding_type(:datetime = t), do: {t, _tz = nil}
13✔
772

773
  defp decoding_type({:array = a, t}), do: {a, decoding_type(t)}
1,336✔
774

775
  defp decoding_type({:tuple = t, ts}) do
320✔
776
    {t, Enum.map(ts, &decoding_type/1)}
777
  end
778

779
  defp decoding_type({:variant = v, ts}) do
17✔
780
    {v, ts |> Enum.map(&decoding_type/1) |> List.to_tuple()}
781
  end
782

783
  defp decoding_type({:map = m, kt, vt}) do
784
    {m, decoding_type(kt), decoding_type(vt)}
330✔
785
  end
786

787
  defp decoding_type({:nullable = n, t}), do: {n, decoding_type(t)}
545✔
788
  defp decoding_type({:low_cardinality, t}), do: decoding_type(t)
277✔
789

790
  defp decoding_type({:decimal = t, p, s}), do: {t, decimal_size(p), s}
356✔
791
  defp decoding_type({:decimal32, s}), do: {:decimal, 32, s}
1✔
792
  defp decoding_type({:decimal64, s}), do: {:decimal, 64, s}
1✔
793
  defp decoding_type({:decimal128, s}), do: {:decimal, 128, s}
1✔
794
  defp decoding_type({:decimal256, s}), do: {:decimal, 256, s}
1✔
795

796
  defp decoding_type({:datetime64 = t, p}), do: {t, time_unit(p), _tz = nil}
6✔
797
  defp decoding_type({:datetime64 = t, p, tz}), do: {t, time_unit(p), tz}
315✔
798

799
  defp decoding_type({:time64 = t, p}), do: {t, time_unit(p)}
262✔
800

801
  defp decoding_type({e, mappings}) when e in [:enum8, :enum16] do
9✔
802
    {e, Map.new(mappings, fn {k, v} -> {v, k} end)}
18✔
803
  end
804

805
  defp decoding_type({:simple_aggregate_function, _f, t}), do: decoding_type(t)
6✔
806

807
  defp decoding_type(:ring), do: {:array, :point}
1✔
808
  defp decoding_type(:polygon), do: {:array, {:array, :point}}
1✔
809
  defp decoding_type(:multipolygon), do: {:array, {:array, {:array, :point}}}
1✔
810

811
  defp decoding_type(type) do
812
    raise ArgumentError, "unsupported type for decoding: #{inspect(type)}"
1✔
813
  end
814

815
  defp skip_names(<<rest::bytes>>, 0, count), do: decode_types(rest, count, _acc = [])
5✔
816

817
  for {pattern, value} <- varints do
818
    defp skip_names(<<unquote(pattern), _::size(unquote(value))-bytes, rest::bytes>>, left, count) do
819
      skip_names(rest, left - 1, count)
75✔
820
    end
821
  end
822

823
  defp decode_names(<<rest::bytes>>, 0, count, names) do
3,030✔
824
    [:lists.reverse(names) | decode_types(rest, count, _acc = [])]
825
  end
826

827
  for {pattern, value} <- varints do
828
    defp decode_names(
829
           <<unquote(pattern), name::size(unquote(value))-bytes, rest::bytes>>,
830
           left,
831
           count,
832
           acc
833
         ) do
834
      decode_names(rest, left - 1, count, [name | acc])
16,447✔
835
    end
836
  end
837

838
  defp decode_types(<<>>, 0, _types), do: []
40✔
839

840
  defp decode_types(<<rest::bytes>>, 0, types) do
841
    decode_rows!(rest, decoding_types_reverse(types))
2,995✔
842
  end
843

844
  for {pattern, value} <- varints do
845
    defp decode_types(
846
           <<unquote(pattern), type::size(unquote(value))-bytes, rest::bytes>>,
847
           count,
848
           acc
849
         ) do
850
      decode_types(rest, count - 1, [type | acc])
16,522✔
851
    end
852
  end
853

854
  @compile inline: [decode_string_decode_rows: 9]
855

856
  for {pattern, size} <- varints do
857
    defp decode_string_decode_rows(
858
           <<unquote(pattern), s::size(unquote(size))-bytes, bin::bytes>>,
859
           types_rest,
860
           row,
861
           rows,
862
           types,
863
           kind,
864
           schema,
865
           left,
866
           outer
867
         ) do
868
      decode_rows(types_rest, bin, [s | row], rows, types, kind, schema, left, outer)
9,187✔
869
    end
870
  end
871

872
  defp decode_string_decode_rows(
873
         <<bin::bytes>>,
874
         types_rest,
875
         row,
876
         rows,
877
         types,
878
         kind,
879
         schema,
880
         left,
881
         outer
882
       ) do
883
    to_be_continued(
200,144✔
884
      rows,
885
      bin,
886
      [:string | types_rest],
887
      row,
888
      types,
889
      kind,
890
      schema,
891
      left,
892
      outer
893
    )
894
  end
895

896
  @compile inline: [decode_string_json_decode_rows: 9]
897

898
  for {pattern, size} <- varints do
899
    defp decode_string_json_decode_rows(
900
           <<unquote(pattern), s::size(unquote(size))-bytes, bin::bytes>>,
901
           types_rest,
902
           row,
903
           rows,
904
           types,
905
           kind,
906
           schema,
907
           left,
908
           outer
909
         ) do
910
      decode_rows(
43✔
911
        types_rest,
912
        bin,
913
        [JSON.decode!(s) | row],
914
        rows,
915
        types,
916
        kind,
917
        schema,
918
        left,
919
        outer
920
      )
921
    end
922
  end
923

924
  defp decode_string_json_decode_rows(
925
         <<bin::bytes>>,
926
         types_rest,
927
         row,
928
         rows,
929
         types,
930
         kind,
931
         schema,
932
         left,
933
         outer
934
       ) do
935
    to_be_continued(
46✔
936
      rows,
937
      bin,
938
      [:json | types_rest],
939
      row,
940
      types,
941
      kind,
942
      schema,
943
      left,
944
      outer
945
    )
946
  end
947

948
  @compile inline: [decode_array_decode_rows: 10]
949
  defp decode_array_decode_rows(
950
         <<0, bin::bytes>>,
951
         _type,
952
         types_rest,
953
         row,
954
         rows,
955
         types,
956
         kind,
957
         schema,
958
         left,
959
         outer
960
       ) do
961
    decode_rows(types_rest, bin, [[] | row], rows, types, kind, schema, left, outer)
395✔
962
  end
963

964
  for {pattern, size} <- varints do
965
    defp decode_array_decode_rows(
966
           <<unquote(pattern), bin::bytes>>,
967
           type,
968
           types_rest,
969
           row,
970
           rows,
971
           types,
972
           kind,
973
           schema,
974
           left,
975
           outer
976
         ) do
977
      array_schema = [type]
2,794✔
978

979
      decode_rows(
2,794✔
980
        array_schema,
981
        bin,
982
        [],
983
        rows,
984
        types,
985
        :array,
986
        array_schema,
987
        unquote(size),
988
        [{types_rest, row, kind, schema, left} | outer]
989
      )
990
    end
991
  end
992

993
  defp decode_array_decode_rows(
994
         <<bin::bytes>>,
995
         type,
996
         types_rest,
997
         row,
998
         rows,
999
         types,
1000
         kind,
1001
         schema,
1002
         left,
1003
         outer
1004
       ) do
1005
    to_be_continued(
12✔
1006
      rows,
1007
      bin,
1008
      [{:array, type} | types_rest],
1009
      row,
1010
      types,
1011
      kind,
1012
      schema,
1013
      left,
1014
      outer
1015
    )
1016
  end
1017

1018
  @compile inline: [decode_map_decode_rows: 11]
1019
  defp decode_map_decode_rows(
1020
         <<0, bin::bytes>>,
1021
         _key_type,
1022
         _value_type,
1023
         types_rest,
1024
         row,
1025
         rows,
1026
         types,
1027
         kind,
1028
         schema,
1029
         left,
1030
         outer
1031
       ) do
1032
    decode_rows(types_rest, bin, [%{} | row], rows, types, kind, schema, left, outer)
43✔
1033
  end
1034

1035
  for {pattern, size} <- varints do
1036
    defp decode_map_decode_rows(
1037
           <<unquote(pattern), bin::bytes>>,
1038
           key_type,
1039
           value_type,
1040
           types_rest,
1041
           row,
1042
           rows,
1043
           types,
1044
           kind,
1045
           schema,
1046
           left,
1047
           outer
1048
         ) do
1049
      map_schema = [key_type, value_type]
289✔
1050

1051
      decode_rows(
289✔
1052
        map_schema,
1053
        bin,
1054
        [],
1055
        rows,
1056
        types,
1057
        {:map, %{}},
1058
        map_schema,
1059
        unquote(size),
1060
        [{types_rest, row, kind, schema, left} | outer]
1061
      )
1062
    end
1063
  end
1064

1065
  defp decode_map_decode_rows(
1066
         <<bin::bytes>>,
1067
         key_type,
1068
         value_type,
1069
         types_rest,
1070
         row,
1071
         rows,
1072
         types,
1073
         kind,
1074
         schema,
1075
         left,
1076
         outer
1077
       ) do
1078
    to_be_continued(
6✔
1079
      rows,
1080
      bin,
1081
      [{:map, key_type, value_type} | types_rest],
1082
      row,
1083
      types,
1084
      kind,
1085
      schema,
1086
      left,
1087
      outer
1088
    )
1089
  end
1090

1091
  # https://clickhouse.com/docs/sql-reference/data-types/data-types-binary-encoding
1092
  dynamic_types = [
1093
    nothing: 0x00,
1094
    u8: 0x01,
1095
    u16: 0x02,
1096
    u32: 0x03,
1097
    u64: 0x04,
1098
    u128: 0x05,
1099
    u256: 0x06,
1100
    i8: 0x07,
1101
    i16: 0x08,
1102
    i32: 0x09,
1103
    i64: 0x0A,
1104
    i128: 0x0B,
1105
    i256: 0x0C,
1106
    f32: 0x0D,
1107
    f64: 0x0E,
1108
    date: 0x0F,
1109
    date32: 0x10,
1110
    string: 0x15,
1111
    uuid: 0x1D,
1112
    ipv4: 0x28,
1113
    ipv6: 0x29,
1114
    boolean: 0x2D
1115
  ]
1116

1117
  # TODO compile inline?
1118

1119
  for {type, code} <- dynamic_types do
1120
    defp decode_dynamic(
1121
           <<unquote(code), rest::bytes>>,
1122
           dynamic,
1123
           types_rest,
1124
           row,
1125
           rows,
1126
           types,
1127
           kind,
1128
           schema,
1129
           left,
1130
           outer
1131
         ) do
1132
      decode_dynamic_continue(
120✔
1133
        rest,
1134
        [unquote(type) | dynamic],
1135
        types_rest,
1136
        row,
1137
        rows,
1138
        types,
1139
        kind,
1140
        schema,
1141
        left,
1142
        outer
1143
      )
1144
    end
1145
  end
1146

1147
  # DateTime 0x11
1148
  defp decode_dynamic(
1149
         <<0x11, rest::bytes>>,
1150
         dynamic,
1151
         types_rest,
1152
         row,
1153
         rows,
1154
         types,
1155
         kind,
1156
         schema,
1157
         left,
1158
         outer
1159
       ) do
1160
    decode_dynamic_continue(
2✔
1161
      rest,
1162
      [{:datetime, nil} | dynamic],
1163
      types_rest,
1164
      row,
1165
      rows,
1166
      types,
1167
      kind,
1168
      schema,
1169
      left,
1170
      outer
1171
    )
1172
  end
1173

1174
  # DateTime(time_zone) 0x12 <var_uint_time_zone_name_size><time_zone_name_data>
1175
  for {pattern, size} <- varints do
1176
    defp decode_dynamic(
1177
           <<0x12, unquote(pattern), tz::size(unquote(size))-bytes, rest::bytes>>,
1178
           dynamic,
1179
           types_rest,
1180
           row,
1181
           rows,
1182
           types,
1183
           kind,
1184
           schema,
1185
           left,
1186
           outer
1187
         ) do
1188
      decode_dynamic_continue(
1✔
1189
        rest,
1190
        [{:datetime, tz} | dynamic],
1191
        types_rest,
1192
        row,
1193
        rows,
1194
        types,
1195
        kind,
1196
        schema,
1197
        left,
1198
        outer
1199
      )
1200
    end
1201
  end
1202

1203
  # DateTime64(P) 0x13 <uint8_precision>
1204
  defp decode_dynamic(
1205
         <<0x13, precision, rest::bytes>>,
1206
         dynamic,
1207
         types_rest,
1208
         row,
1209
         rows,
1210
         types,
1211
         kind,
1212
         schema,
1213
         left,
1214
         outer
1215
       ) do
1216
    decode_dynamic_continue(
1✔
1217
      rest,
1218
      [decoding_type({:datetime64, precision}) | dynamic],
1219
      types_rest,
1220
      row,
1221
      rows,
1222
      types,
1223
      kind,
1224
      schema,
1225
      left,
1226
      outer
1227
    )
1228
  end
1229

1230
  # DateTime64(P, time_zone) 0x14 <uint8_precision><var_uint_time_zone_name_size><time_zone_name_data>
1231
  for {pattern, size} <- varints do
1232
    defp decode_dynamic(
1233
           <<0x14, precision, unquote(pattern), tz::size(unquote(size))-bytes, rest::bytes>>,
1234
           dynamic,
1235
           types_rest,
1236
           row,
1237
           rows,
1238
           types,
1239
           kind,
1240
           schema,
1241
           left,
1242
           outer
1243
         ) do
1244
      decode_dynamic_continue(
1✔
1245
        rest,
1246
        [decoding_type({:datetime64, precision, tz}) | dynamic],
1247
        types_rest,
1248
        row,
1249
        rows,
1250
        types,
1251
        kind,
1252
        schema,
1253
        left,
1254
        outer
1255
      )
1256
    end
1257
  end
1258

1259
  # FixedString(N) 0x16 <var_uint_size>
1260
  for {pattern, size} <- varints do
1261
    defp decode_dynamic(
1262
           <<0x16, unquote(pattern), rest::bytes>>,
1263
           dynamic,
1264
           types_rest,
1265
           row,
1266
           rows,
1267
           types,
1268
           kind,
1269
           schema,
1270
           left,
1271
           outer
1272
         ) do
1273
      decode_dynamic_continue(
2✔
1274
        rest,
1275
        [{:fixed_string, unquote(size)} | dynamic],
1276
        types_rest,
1277
        row,
1278
        rows,
1279
        types,
1280
        kind,
1281
        schema,
1282
        left,
1283
        outer
1284
      )
1285
    end
1286
  end
1287

1288
  # Decimal32(P, S) 0x19 <uint8_precision><uint8_scale>
1289
  # Decimal64(P, S) 0x1A <uint8_precision><uint8_scale>
1290
  # Decimal128(P, S) 0x1B <uint8_precision><uint8_scale>
1291
  # Decimal256(P, S) 0x1C <uint8_precision><uint8_scale>
1292
  for {code, size} <- [{0x19, 32}, {0x1A, 64}, {0x1B, 128}, {0x1C, 256}] do
1293
    defp decode_dynamic(
1294
           <<unquote(code), _precision, scale, rest::bytes>>,
1295
           dynamic,
1296
           types_rest,
1297
           row,
1298
           rows,
1299
           types,
1300
           kind,
1301
           schema,
1302
           left,
1303
           outer
1304
         ) do
1305
      decode_dynamic_continue(
4✔
1306
        rest,
1307
        [{:decimal, unquote(size), scale} | dynamic],
1308
        types_rest,
1309
        row,
1310
        rows,
1311
        types,
1312
        kind,
1313
        schema,
1314
        left,
1315
        outer
1316
      )
1317
    end
1318
  end
1319

1320
  # Array(T) 0x1E <nested_type_encoding>
1321
  # Nullable(T) 0x23 <nested_type_encoding>
1322
  # LowCardinality(T) 0x26 <nested_type_encoding>
1323
  for {code, wrapper} <- [{0x1E, :array}, {0x23, :nullable}, {0x26, :low_cardinality}] do
1324
    defp decode_dynamic(
1325
           <<unquote(code), rest::bytes>>,
1326
           dynamic,
1327
           types_rest,
1328
           row,
1329
           rows,
1330
           types,
1331
           kind,
1332
           schema,
1333
           left,
1334
           outer
1335
         ) do
1336
      decode_dynamic_continue(
36✔
1337
        rest,
1338
        [unquote(wrapper) | dynamic],
1339
        types_rest,
1340
        row,
1341
        rows,
1342
        types,
1343
        kind,
1344
        schema,
1345
        left,
1346
        outer
1347
      )
1348
    end
1349
  end
1350

1351
  # TODO
1352
  # 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>
1353
  # 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>
1354
  # Tuple(T1, ..., TN)        0x1F <var_uint_number_of_elements><nested_type_encoding_1>...<nested_type_encoding_N>
1355
  # 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>
1356
  # Set        0x21
1357
  # Interval        0x22 <interval_kind> (see interval kind binary encoding)
1358
  # Function        0x24<var_uint_number_of_arguments><argument_type_encoding_1>...<argument_type_encoding_N><return_type_encoding>
1359
  # 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)
1360
  # Map(K, V)        0x27<key_type_encoding><value_type_encoding>
1361
  # Variant(T1, ..., TN)        0x2A<var_uint_number_of_variants><variant_type_encoding_1>...<variant_type_encoding_N>
1362
  # Dynamic(max_types=N)        0x2B<uint8_max_types>
1363
  # Custom type (Ring, Polygon, etc)        0x2C<var_uint_type_name_size><type_name_data>
1364
  # 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)
1365
  # 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>
1366
  # 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>...
1367

1368
  unsupported_dynamic_types = %{
1369
    "Enum8" => 0x17,
1370
    "Enum16" => 0x18,
1371
    "Tuple" => 0x1F,
1372
    "TupleWithNames" => 0x20,
1373
    "Set" => 0x21,
1374
    "Interval" => 0x22,
1375
    "Function" => 0x24,
1376
    "AggregateFunction" => 0x25,
1377
    "Map" => 0x27,
1378
    "Variant" => 0x2A,
1379
    "Dynamic" => 0x2B,
1380
    "CustomType" => 0x2C,
1381
    "SimpleAggregateFunction" => 0x2E,
1382
    "Nested" => 0x2F,
1383
    "JSON" => 0x30
1384
  }
1385

1386
  for {type, code} <- unsupported_dynamic_types do
1387
    defp decode_dynamic(
1388
           <<unquote(code), _::bytes>>,
1389
           _dynamic,
1390
           _types_rest,
1391
           _row,
1392
           _rows,
1393
           _types,
1394
           _kind,
1395
           _schema,
1396
           _left,
1397
           _outer
1398
         ) do
1399
      raise ArgumentError, "unsupported dynamic type #{unquote(type)}"
9✔
1400
    end
1401
  end
1402

1403
  defp decode_dynamic(
1404
         <<bin::bytes>>,
1405
         dynamic,
1406
         types_rest,
1407
         row,
1408
         rows,
1409
         types,
1410
         kind,
1411
         schema,
1412
         left,
1413
         outer
1414
       ) do
1415
    to_be_continued(
2✔
1416
      rows,
1417
      bin,
1418
      [{:dynamic, dynamic} | types_rest],
1419
      row,
1420
      types,
1421
      kind,
1422
      schema,
1423
      left,
1424
      outer
1425
    )
1426
  end
1427

1428
  @compile inline: [decode_dynamic_continue: 10]
1429

1430
  defp decode_dynamic_continue(
1431
         <<rest::bytes>>,
1432
         dynamic,
1433
         types_rest,
1434
         row,
1435
         rows,
1436
         types,
1437
         kind,
1438
         schema,
1439
         left,
1440
         outer
1441
       ) do
1442
    continue? =
103✔
1443
      case dynamic do
1444
        [:array | _] -> true
29✔
1445
        [:nullable | _] -> true
5✔
1446
        [:low_cardinality | _] -> true
2✔
1447
        _ -> false
103✔
1448
      end
1449

1450
    if continue? do
103✔
1451
      decode_dynamic(rest, dynamic, types_rest, row, rows, types, kind, schema, left, outer)
36✔
1452
    else
1453
      type = build_dynamic_type(:lists.reverse(dynamic))
103✔
1454

1455
      decode_rows(
103✔
1456
        [type | types_rest],
1457
        rest,
1458
        row,
1459
        rows,
1460
        types,
1461
        kind,
1462
        schema,
1463
        left,
1464
        outer
1465
      )
1466
    end
1467
  end
1468

1469
  defp build_dynamic_type([type]), do: type
131✔
1470

1471
  defp build_dynamic_type(type) do
1472
    case type do
32✔
1473
      [:array | rest] -> {:array, build_dynamic_type(rest)}
25✔
1474
      [:nullable | rest] -> {:nullable, build_dynamic_type(rest)}
5✔
1475
      [:low_cardinality | rest] -> build_dynamic_type(rest)
2✔
1476
    end
1477
  end
1478

1479
  simple_types = %{
1480
    u8: %{pattern: quote(do: <<u>>), value: quote(do: u)},
1481
    u16: %{pattern: quote(do: <<u::16-little>>), value: quote(do: u)},
1482
    u32: %{pattern: quote(do: <<u::32-little>>), value: quote(do: u)},
1483
    u64: %{pattern: quote(do: <<u::64-little>>), value: quote(do: u)},
1484
    u128: %{pattern: quote(do: <<u::128-little>>), value: quote(do: u)},
1485
    u256: %{pattern: quote(do: <<u::256-little>>), value: quote(do: u)},
1486
    i8: %{pattern: quote(do: <<i::signed>>), value: quote(do: i)},
1487
    i16: %{pattern: quote(do: <<i::16-little-signed>>), value: quote(do: i)},
1488
    i32: %{pattern: quote(do: <<i::32-little-signed>>), value: quote(do: i)},
1489
    i64: %{pattern: quote(do: <<i::64-little-signed>>), value: quote(do: i)},
1490
    i128: %{pattern: quote(do: <<i::128-little-signed>>), value: quote(do: i)},
1491
    i256: %{pattern: quote(do: <<i::256-little-signed>>), value: quote(do: i)},
1492
    f32: [
1493
      %{pattern: quote(do: <<f::32-little-float>>), value: quote(do: f)},
1494
      %{pattern: quote(do: <<_nan_or_inf::32>>), value: quote(do: nil)}
1495
    ],
1496
    f64: [
1497
      %{pattern: quote(do: <<f::64-little-float>>), value: quote(do: f)},
1498
      %{pattern: quote(do: <<_nan_or_inf::64>>), value: quote(do: nil)}
1499
    ],
1500
    uuid: %{
1501
      pattern: quote(do: <<u1::64-little, u2::64-little>>),
1502
      value: quote(do: <<u1::64, u2::64>>)
1503
    },
1504
    date: %{
1505
      pattern: quote(do: <<d::16-little>>),
1506
      value: quote(do: Date.from_gregorian_days(d + @epoch_gregorian_days))
1507
    },
1508
    date32: %{
1509
      pattern: quote(do: <<d::32-little-signed>>),
1510
      value: quote(do: Date.from_gregorian_days(d + @epoch_gregorian_days))
1511
    },
1512
    time: %{
1513
      pattern: quote(do: <<s::32-little-signed>>),
1514
      value: quote(do: time_after_midnight(s, 1))
1515
    },
1516
    boolean: [
1517
      %{pattern: quote(do: <<0>>), value: quote(do: false)},
1518
      %{pattern: quote(do: <<1>>), value: quote(do: true)},
1519
      %{pattern: quote(do: <<b>>), value: quote(do: raise("invalid boolean value: #{b}"))}
1520
    ],
1521
    ipv4: %{
1522
      pattern: quote(do: <<b4, b3, b2, b1>>),
1523
      value: quote(do: {b1, b2, b3, b4})
1524
    },
1525
    ipv6: %{
1526
      pattern: quote(do: <<b1::16, b2::16, b3::16, b4::16, b5::16, b6::16, b7::16, b8::16>>),
1527
      value: quote(do: {b1, b2, b3, b4, b5, b6, b7, b8})
1528
    },
1529
    point: %{
1530
      pattern: quote(do: <<x::64-little-float, y::64-little-float>>),
1531
      value: quote(do: {x, y})
1532
    }
1533
  }
1534

1535
  for {type, clauses} <- simple_types do
1536
    fun = :"decode_#{type}_decode_rows"
1537
    @compile inline: [{fun, 9}]
1538

1539
    for %{pattern: pattern, value: value} <- List.wrap(clauses) do
1540
      defp unquote(fun)(
1541
             <<unquote(pattern), rest::bytes>>,
1542
             types_rest,
1543
             row,
1544
             rows,
1545
             types,
1546
             kind,
1547
             schema,
1548
             left,
1549
             outer
1550
           ) do
1551
        decode_rows(
2,021,540✔
1552
          types_rest,
1553
          rest,
1554
          [unquote(value) | row],
1555
          rows,
1556
          types,
1557
          kind,
1558
          schema,
1559
          left,
1560
          outer
1561
        )
1562
      end
1563
    end
1564

1565
    defp unquote(fun)(
1566
           <<bin::bytes>>,
1567
           types_rest,
1568
           row,
1569
           rows,
1570
           types,
1571
           kind,
1572
           schema,
1573
           left,
1574
           outer
1575
         ) do
1576
      to_be_continued(
583✔
1577
        rows,
1578
        bin,
1579
        [unquote(type) | types_rest],
1580
        row,
1581
        types,
1582
        kind,
1583
        schema,
1584
        left,
1585
        outer
1586
      )
1587
    end
1588
  end
1589

1590
  # The active sequence is carried in kind/schema/left. Only nested parents are stored in outer,
1591
  # so decoding another collection item does not allocate a replacement continuation frame.
1592
  defp decode_rows(
1593
         [type | types_rest],
1594
         <<bin::bytes>>,
1595
         row,
1596
         rows,
1597
         types,
1598
         kind,
1599
         schema,
1600
         left,
1601
         outer
1602
       ) do
1603
    case type do
2,244,499✔
1604
      :u8 ->
1605
        decode_u8_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
5,954✔
1606

1607
      :u16 ->
1608
        decode_u16_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
10,306✔
1609

1610
      :u32 ->
1611
        decode_u32_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
97✔
1612

1613
      :u64 ->
1614
        decode_u64_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
2,000,245✔
1615

1616
      :u128 ->
1617
        decode_u128_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
59✔
1618

1619
      :u256 ->
1620
        decode_u256_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
104✔
1621

1622
      :i8 ->
1623
        decode_i8_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
167✔
1624

1625
      :i16 ->
1626
        decode_i16_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
218✔
1627

1628
      :i32 ->
1629
        decode_i32_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
78✔
1630

1631
      :i64 ->
1632
        decode_i64_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
147✔
1633

1634
      :i128 ->
1635
        decode_i128_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
85✔
1636

1637
      :i256 ->
1638
        decode_i256_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
103✔
1639

1640
      :f32 ->
1641
        decode_f32_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
798✔
1642

1643
      :f64 ->
1644
        decode_f64_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
924✔
1645

1646
      :string ->
1647
        decode_string_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
209,331✔
1648

1649
      :json ->
1650
        # assuming it arrives as text and not "native" binary JSON
1651
        # i.e. assumes `settings: [output_format_binary_write_json_as_string: 1]`
1652
        # TODO
1653
        decode_string_json_decode_rows(
89✔
1654
          bin,
1655
          types_rest,
1656
          row,
1657
          rows,
1658
          types,
1659
          kind,
1660
          schema,
1661
          left,
1662
          outer
1663
        )
1664

1665
      :dynamic ->
1666
        decode_dynamic(
140✔
1667
          bin,
1668
          _dynamic = [],
1669
          types_rest,
1670
          row,
1671
          rows,
1672
          types,
1673
          kind,
1674
          schema,
1675
          left,
1676
          outer
1677
        )
1678

1679
      {:dynamic, dynamic} ->
1680
        decode_dynamic(bin, dynamic, types_rest, row, rows, types, kind, schema, left, outer)
2✔
1681

1682
      {:fixed_string, size} ->
1683
        case bin do
4,587✔
1684
          <<s::size(^size)-bytes, rest::bytes>> ->
1685
            decode_rows(types_rest, rest, [s | row], rows, types, kind, schema, left, outer)
4,573✔
1686

1687
          _ ->
1688
            to_be_continued(
14✔
1689
              rows,
1690
              bin,
1691
              [type | types_rest],
1692
              row,
1693
              types,
1694
              kind,
1695
              schema,
1696
              left,
1697
              outer
1698
            )
1699
        end
1700

1701
      :boolean ->
1702
        decode_boolean_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
1,983✔
1703

1704
      :uuid ->
1705
        decode_uuid_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
186✔
1706

1707
      :date ->
1708
        decode_date_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
148✔
1709

1710
      :date32 ->
1711
        decode_date32_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
48✔
1712

1713
      :time ->
1714
        decode_time_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
230✔
1715

1716
      {:time64, time_unit} ->
1717
        case bin do
300✔
1718
          <<ticks::64-little-signed, bin::bytes>> ->
1719
            time = time_after_midnight(ticks, time_unit)
270✔
1720
            decode_rows(types_rest, bin, [time | row], rows, types, kind, schema, left, outer)
265✔
1721

1722
          _ ->
1723
            to_be_continued(
30✔
1724
              rows,
1725
              bin,
1726
              [type | types_rest],
1727
              row,
1728
              types,
1729
              kind,
1730
              schema,
1731
              left,
1732
              outer
1733
            )
1734
        end
1735

1736
      {:datetime, timezone} ->
1737
        case bin do
70✔
1738
          <<s::32-little, bin::bytes>> ->
1739
            dt = DateTime.from_unix!(s)
48✔
1740

1741
            dt =
48✔
1742
              case timezone do
1743
                nil -> DateTime.to_naive(dt)
16✔
1744
                "UTC" -> dt
26✔
1745
                _ -> DateTime.shift_zone!(dt, timezone)
6✔
1746
              end
1747

1748
            decode_rows(types_rest, bin, [dt | row], rows, types, kind, schema, left, outer)
48✔
1749

1750
          _ ->
1751
            to_be_continued(
22✔
1752
              rows,
1753
              bin,
1754
              [type | types_rest],
1755
              row,
1756
              types,
1757
              kind,
1758
              schema,
1759
              left,
1760
              outer
1761
            )
1762
        end
1763

1764
      {:decimal, size, scale} ->
1765
        case bin do
493✔
1766
          <<val::size(^size)-little-signed, bin::bytes>> ->
1767
            sign = if val < 0, do: -1, else: 1
375✔
1768
            d = Decimal.new(sign, abs(val), -scale)
375✔
1769
            decode_rows(types_rest, bin, [d | row], rows, types, kind, schema, left, outer)
375✔
1770

1771
          _ ->
1772
            to_be_continued(
118✔
1773
              rows,
1774
              bin,
1775
              [type | types_rest],
1776
              row,
1777
              types,
1778
              kind,
1779
              schema,
1780
              left,
1781
              outer
1782
            )
1783
        end
1784

1785
      {:nullable, inner_type} ->
1786
        case bin do
2,752✔
1787
          <<b, bin::bytes>> ->
1788
            case b do
2,749✔
1789
              0 ->
1790
                decode_rows(
1,356✔
1791
                  [inner_type | types_rest],
1792
                  bin,
1793
                  row,
1794
                  rows,
1795
                  types,
1796
                  kind,
1797
                  schema,
1798
                  left,
1799
                  outer
1800
                )
1801

1802
              1 ->
1803
                decode_rows(types_rest, bin, [nil | row], rows, types, kind, schema, left, outer)
1,393✔
1804
            end
1805

1806
          _ ->
1807
            to_be_continued(
3✔
1808
              rows,
1809
              bin,
1810
              [type | types_rest],
1811
              row,
1812
              types,
1813
              kind,
1814
              schema,
1815
              left,
1816
              outer
1817
            )
1818
        end
1819

1820
      :nothing ->
1821
        decode_rows(types_rest, bin, [nil | row], rows, types, kind, schema, left, outer)
27✔
1822

1823
      {:array, inner_type} ->
1824
        decode_array_decode_rows(
3,201✔
1825
          bin,
1826
          inner_type,
1827
          types_rest,
1828
          row,
1829
          rows,
1830
          types,
1831
          kind,
1832
          schema,
1833
          left,
1834
          outer
1835
        )
1836

1837
      {:map, key_type, value_type} ->
1838
        decode_map_decode_rows(
338✔
1839
          bin,
1840
          key_type,
1841
          value_type,
1842
          types_rest,
1843
          row,
1844
          rows,
1845
          types,
1846
          kind,
1847
          schema,
1848
          left,
1849
          outer
1850
        )
1851

1852
      {:tuple, []} ->
NEW
1853
        decode_rows(types_rest, bin, [{} | row], rows, types, kind, schema, left, outer)
×
1854

1855
      {:tuple, tuple_types} ->
1856
        decode_rows(
334✔
1857
          tuple_types,
1858
          bin,
1859
          [],
1860
          rows,
1861
          types,
1862
          :tuple,
1863
          tuple_types,
1864
          1,
1865
          [{types_rest, row, kind, schema, left} | outer]
1866
        )
1867

1868
      {:variant, variant_types} ->
1869
        case bin do
35✔
1870
          <<255, bin::bytes>> ->
1871
            # 255 is the variant type index for "nothing"
1872
            decode_rows(types_rest, bin, [nil | row], rows, types, kind, schema, left, outer)
7✔
1873

1874
          # TODO varint?
1875
          <<variant_type_index::8, bin::bytes>>
1876
          when variant_type_index < tuple_size(variant_types) ->
1877
            variant_type = elem(variant_types, variant_type_index)
24✔
1878

1879
            decode_rows(
24✔
1880
              [variant_type | types_rest],
1881
              bin,
1882
              row,
1883
              rows,
1884
              types,
1885
              kind,
1886
              schema,
1887
              left,
1888
              outer
1889
            )
1890

1891
          <<variant_type_index::8, _bin::bytes>> ->
1892
            raise ArgumentError, "invalid Variant type index: #{variant_type_index}"
1✔
1893

1894
          _ ->
1895
            to_be_continued(
3✔
1896
              rows,
1897
              bin,
1898
              [type | types_rest],
1899
              row,
1900
              types,
1901
              kind,
1902
              schema,
1903
              left,
1904
              outer
1905
            )
1906
        end
1907

1908
      {:datetime64, time_unit, timezone} ->
1909
        case bin do
656✔
1910
          <<s::64-little-signed, bin::bytes>> ->
1911
            dt = DateTime.from_unix!(s, time_unit)
594✔
1912

1913
            dt =
594✔
1914
              case timezone do
1915
                nil -> DateTime.to_naive(dt)
7✔
1916
                "UTC" -> dt
581✔
1917
                _ -> DateTime.shift_zone!(dt, timezone)
6✔
1918
              end
1919

1920
            decode_rows(types_rest, bin, [dt | row], rows, types, kind, schema, left, outer)
594✔
1921

1922
          _ ->
1923
            to_be_continued(
62✔
1924
              rows,
1925
              bin,
1926
              [type | types_rest],
1927
              row,
1928
              types,
1929
              kind,
1930
              schema,
1931
              left,
1932
              outer
1933
            )
1934
        end
1935

1936
      {:enum8, mapping} ->
1937
        case bin do
15✔
1938
          <<v::signed, bin::bytes>> ->
1939
            decode_rows(
14✔
1940
              types_rest,
1941
              bin,
1942
              [Map.fetch!(mapping, v) | row],
1943
              rows,
1944
              types,
1945
              kind,
1946
              schema,
1947
              left,
1948
              outer
1949
            )
1950

1951
          _ ->
1952
            to_be_continued(
1✔
1953
              rows,
1954
              bin,
1955
              [type | types_rest],
1956
              row,
1957
              types,
1958
              kind,
1959
              schema,
1960
              left,
1961
              outer
1962
            )
1963
        end
1964

1965
      {:enum16, mapping} ->
1966
        case bin do
6✔
1967
          <<v::16-little-signed, bin::bytes>> ->
1968
            decode_rows(
2✔
1969
              types_rest,
1970
              bin,
1971
              [Map.fetch!(mapping, v) | row],
1972
              rows,
1973
              types,
1974
              kind,
1975
              schema,
1976
              left,
1977
              outer
1978
            )
1979

1980
          _ ->
1981
            to_be_continued(
4✔
1982
              rows,
1983
              bin,
1984
              [type | types_rest],
1985
              row,
1986
              types,
1987
              kind,
1988
              schema,
1989
              left,
1990
              outer
1991
            )
1992
        end
1993

1994
      :ipv4 ->
1995
        decode_ipv4_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
35✔
1996

1997
      :ipv6 ->
1998
        decode_ipv6_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
100✔
1999

2000
      :point ->
2001
        decode_point_decode_rows(bin, types_rest, row, rows, types, kind, schema, left, outer)
108✔
2002
    end
2003
  end
2004

2005
  defp decode_rows([], <<bin::bytes>>, row, rows, types, :array, schema, left, outer)
2006
       when left > 1 do
2007
    decode_rows(schema, bin, row, rows, types, :array, schema, left - 1, outer)
8,832✔
2008
  end
2009

2010
  defp decode_rows([], <<bin::bytes>>, row, rows, types, {:map, map}, schema, left, outer) do
2011
    [value, key] = row
1,135✔
2012
    map = Map.put(map, key, value)
1,135✔
2013

2014
    if left > 1 do
1,135✔
2015
      decode_rows(schema, bin, [], rows, types, {:map, map}, schema, left - 1, outer)
846✔
2016
    else
2017
      restore_parent(map, bin, rows, types, outer)
289✔
2018
    end
2019
  end
2020

2021
  defp decode_rows([], <<bin::bytes>>, row, rows, types, :array, _schema, 1, outer) do
2022
    restore_parent(:lists.reverse(row), bin, rows, types, outer)
2,793✔
2023
  end
2024

2025
  defp decode_rows([], <<bin::bytes>>, row, rows, types, :tuple, _schema, 1, outer) do
2026
    tuple = row |> :lists.reverse() |> List.to_tuple()
334✔
2027
    restore_parent(tuple, bin, rows, types, outer)
334✔
2028
  end
2029

2030
  defp decode_rows([], <<>> = empty, row, rows, _types, :row, _schema, 1, []) do
2031
    rows = :lists.reverse([:lists.reverse(row) | rows])
3,466✔
2032
    {rows, empty, _no_state = nil}
3,466✔
2033
  end
2034

2035
  defp decode_rows([], <<bin::bytes>>, row, rows, types, :row, schema, 1, []) do
2036
    row = :lists.reverse(row)
2,004,004✔
2037
    decode_rows(schema, bin, [], [row | rows], types, :row, schema, 1, [])
2,004,004✔
2038
  end
2039

2040
  @compile inline: [restore_parent: 5]
2041
  defp restore_parent(
2042
         value,
2043
         bin,
2044
         rows,
2045
         types,
2046
         [{parent_types, parent_row, parent_kind, parent_schema, parent_left} | outer]
2047
       ) do
2048
    decode_rows(
3,416✔
2049
      parent_types,
2050
      bin,
2051
      [value | parent_row],
2052
      rows,
2053
      types,
2054
      parent_kind,
2055
      parent_schema,
2056
      parent_left,
2057
      outer
2058
    )
2059
  end
2060

2061
  @compile inline: [to_be_continued: 9]
2062
  defp to_be_continued(rows, bin, types_rest, row, _types, kind, schema, left, outer) do
2063
    state =
200,901✔
2064
      if kind == :row and outer == [] do
201,047✔
2065
        {:cont, types_rest, row}
200,908✔
2066
      else
2067
        {:cont, types_rest, row, kind, schema, left, outer}
139✔
2068
      end
2069

2070
    {:lists.reverse(rows), bin, state}
201,047✔
2071
  end
2072

2073
  @compile inline: [decimal_size: 1]
2074
  # https://clickhouse.com/docs/en/sql-reference/data-types/decimal/
2075
  defp decimal_size(precision) when is_integer(precision) do
2076
    cond do
365✔
2077
      precision >= 39 -> 256
207✔
2078
      precision >= 19 -> 128
157✔
2079
      precision >= 10 -> 64
151✔
2080
      true -> 32
18✔
2081
    end
2082
  end
2083

2084
  @compile inline: [time_unit: 1]
2085
  for precision <- 0..9 do
2086
    time_unit = Integer.pow(10, precision)
2087
    defp time_unit(unquote(precision)), do: unquote(time_unit)
691✔
2088
  end
2089

2090
  @compile inline: [time_after_midnight: 2]
2091
  defp time_after_midnight(ticks, time_unit) do
2092
    if ticks >= 0 and ticks < 86400 * time_unit do
486✔
2093
      ticks |> DateTime.from_unix!(time_unit) |> DateTime.to_time()
478✔
2094
    else
2095
      # since ClickHouse supports Time64 values of [-999:59:59.999999999, 999:59:59.999999999]
2096
      # and Elixir's Time supports values of [00:00:00.000000, 23:59:59.999999]
2097
      # we raise an error when ClickHouse's Time64 value is out of Elixir's Time range
2098
      raise ArgumentError,
8✔
2099
            "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✔
2100

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

© 2026 Coveralls, Inc