@clickhouse/client 1.22.0 → 1.23.0-head.287977a.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +2 -1
  2. package/dist/client.d.ts +2 -2
  3. package/dist/client.js +11 -4
  4. package/dist/client.js.map +1 -1
  5. package/dist/common/clickhouse_types.d.ts +98 -0
  6. package/dist/common/clickhouse_types.js +30 -0
  7. package/dist/common/clickhouse_types.js.map +1 -0
  8. package/dist/common/client.d.ts +233 -0
  9. package/dist/common/client.js +414 -0
  10. package/dist/common/client.js.map +1 -0
  11. package/dist/common/config.d.ts +234 -0
  12. package/dist/common/config.js +364 -0
  13. package/dist/common/config.js.map +1 -0
  14. package/dist/common/connection.d.ts +124 -0
  15. package/dist/common/connection.js +3 -0
  16. package/dist/common/connection.js.map +1 -0
  17. package/dist/common/data_formatter/format_query_params.d.ts +11 -0
  18. package/dist/common/data_formatter/format_query_params.js +128 -0
  19. package/dist/common/data_formatter/format_query_params.js.map +1 -0
  20. package/dist/common/data_formatter/format_query_settings.d.ts +2 -0
  21. package/dist/common/data_formatter/format_query_settings.js +20 -0
  22. package/dist/common/data_formatter/format_query_settings.js.map +1 -0
  23. package/dist/common/data_formatter/formatter.d.ts +41 -0
  24. package/dist/common/data_formatter/formatter.js +78 -0
  25. package/dist/common/data_formatter/formatter.js.map +1 -0
  26. package/dist/common/data_formatter/index.d.ts +3 -0
  27. package/dist/common/data_formatter/index.js +24 -0
  28. package/dist/common/data_formatter/index.js.map +1 -0
  29. package/dist/common/error/error.d.ts +20 -0
  30. package/dist/common/error/error.js +73 -0
  31. package/dist/common/error/error.js.map +1 -0
  32. package/dist/common/error/index.d.ts +1 -0
  33. package/dist/common/error/index.js +18 -0
  34. package/dist/common/error/index.js.map +1 -0
  35. package/dist/common/index.d.ts +67 -0
  36. package/dist/common/index.js +97 -0
  37. package/dist/common/index.js.map +1 -0
  38. package/dist/common/logger.d.ts +80 -0
  39. package/dist/common/logger.js +154 -0
  40. package/dist/common/logger.js.map +1 -0
  41. package/dist/common/parse/column_types.d.ts +155 -0
  42. package/dist/common/parse/column_types.js +594 -0
  43. package/dist/common/parse/column_types.js.map +1 -0
  44. package/dist/common/parse/index.d.ts +2 -0
  45. package/dist/common/parse/index.js +19 -0
  46. package/dist/common/parse/index.js.map +1 -0
  47. package/dist/common/parse/json_handling.d.ts +19 -0
  48. package/dist/common/parse/json_handling.js +8 -0
  49. package/dist/common/parse/json_handling.js.map +1 -0
  50. package/dist/common/result.d.ts +90 -0
  51. package/dist/common/result.js +3 -0
  52. package/dist/common/result.js.map +1 -0
  53. package/dist/common/settings.d.ts +2007 -0
  54. package/dist/common/settings.js +19 -0
  55. package/dist/common/settings.js.map +1 -0
  56. package/dist/common/tracing.d.ts +146 -0
  57. package/dist/common/tracing.js +76 -0
  58. package/dist/common/tracing.js.map +1 -0
  59. package/dist/common/ts_utils.d.ts +4 -0
  60. package/dist/common/ts_utils.js +3 -0
  61. package/dist/common/ts_utils.js.map +1 -0
  62. package/dist/common/utils/connection.d.ts +21 -0
  63. package/dist/common/utils/connection.js +43 -0
  64. package/dist/common/utils/connection.js.map +1 -0
  65. package/dist/common/utils/index.d.ts +5 -0
  66. package/dist/common/utils/index.js +22 -0
  67. package/dist/common/utils/index.js.map +1 -0
  68. package/dist/common/utils/multipart.d.ts +34 -0
  69. package/dist/common/utils/multipart.js +81 -0
  70. package/dist/common/utils/multipart.js.map +1 -0
  71. package/dist/common/utils/sleep.d.ts +4 -0
  72. package/dist/common/utils/sleep.js +12 -0
  73. package/dist/common/utils/sleep.js.map +1 -0
  74. package/dist/common/utils/stream.d.ts +15 -0
  75. package/dist/common/utils/stream.js +50 -0
  76. package/dist/common/utils/stream.js.map +1 -0
  77. package/dist/common/utils/url.d.ts +20 -0
  78. package/dist/common/utils/url.js +67 -0
  79. package/dist/common/utils/url.js.map +1 -0
  80. package/dist/common/version.d.ts +2 -0
  81. package/dist/common/version.js +4 -0
  82. package/dist/common/version.js.map +1 -0
  83. package/dist/config.d.ts +22 -2
  84. package/dist/config.js +2 -2
  85. package/dist/config.js.map +1 -1
  86. package/dist/connection/compression.d.ts +2 -2
  87. package/dist/connection/compression.js +4 -4
  88. package/dist/connection/compression.js.map +1 -1
  89. package/dist/connection/create_connection.d.ts +1 -1
  90. package/dist/connection/node_base_connection.d.ts +3 -3
  91. package/dist/connection/node_base_connection.js +22 -22
  92. package/dist/connection/node_base_connection.js.map +1 -1
  93. package/dist/connection/node_custom_agent_connection.js +2 -2
  94. package/dist/connection/node_custom_agent_connection.js.map +1 -1
  95. package/dist/connection/node_http_connection.js +2 -2
  96. package/dist/connection/node_http_connection.js.map +1 -1
  97. package/dist/connection/node_https_connection.d.ts +1 -1
  98. package/dist/connection/node_https_connection.js +3 -3
  99. package/dist/connection/node_https_connection.js.map +1 -1
  100. package/dist/connection/socket_pool.d.ts +1 -1
  101. package/dist/connection/socket_pool.js +30 -30
  102. package/dist/connection/socket_pool.js.map +1 -1
  103. package/dist/connection/stream.d.ts +1 -1
  104. package/dist/connection/stream.js +9 -9
  105. package/dist/connection/stream.js.map +1 -1
  106. package/dist/index.d.ts +9 -7
  107. package/dist/index.js +26 -24
  108. package/dist/index.js.map +1 -1
  109. package/dist/result_set.d.ts +1 -1
  110. package/dist/result_set.js +10 -10
  111. package/dist/result_set.js.map +1 -1
  112. package/dist/utils/encoder.d.ts +1 -1
  113. package/dist/utils/encoder.js +5 -5
  114. package/dist/utils/encoder.js.map +1 -1
  115. package/dist/version.d.ts +1 -1
  116. package/dist/version.js +1 -1
  117. package/dist/version.js.map +1 -1
  118. package/package.json +7 -5
  119. package/skills/clickhouse-js-node-rowbinary-parser/EXAMPLES.md +48 -0
  120. package/skills/clickhouse-js-node-rowbinary-parser/README.md +255 -0
  121. package/skills/clickhouse-js-node-rowbinary-parser/SKILL.md +206 -0
  122. package/skills/clickhouse-js-node-rowbinary-parser/case-studies/iot-rowbinary-vs-json.md +83 -0
  123. package/skills/clickhouse-js-node-rowbinary-parser/case-studies/ledger-rowbinary-vs-json.md +103 -0
  124. package/skills/clickhouse-js-node-rowbinary-parser/case-studies/logs-json-wins.md +86 -0
  125. package/skills/clickhouse-js-node-rowbinary-parser/case-studies/wasm-vs-js.md +172 -0
  126. package/skills/clickhouse-js-node-rowbinary-parser/src/aggregateFunction.ts +34 -0
  127. package/skills/clickhouse-js-node-rowbinary-parser/src/bool.ts +10 -0
  128. package/skills/clickhouse-js-node-rowbinary-parser/src/columnar.ts +125 -0
  129. package/skills/clickhouse-js-node-rowbinary-parser/src/compile.ts +318 -0
  130. package/skills/clickhouse-js-node-rowbinary-parser/src/composite.ts +181 -0
  131. package/skills/clickhouse-js-node-rowbinary-parser/src/core.ts +77 -0
  132. package/skills/clickhouse-js-node-rowbinary-parser/src/datetime.ts +113 -0
  133. package/skills/clickhouse-js-node-rowbinary-parser/src/decimals.ts +57 -0
  134. package/skills/clickhouse-js-node-rowbinary-parser/src/dynamic.ts +328 -0
  135. package/skills/clickhouse-js-node-rowbinary-parser/src/enums.ts +28 -0
  136. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/carts.ts +71 -0
  137. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/events.ts +51 -0
  138. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/iot.ts +158 -0
  139. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/ledger.ts +98 -0
  140. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/logs.ts +73 -0
  141. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/observability.ts +142 -0
  142. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/orders.ts +65 -0
  143. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/profiles.ts +60 -0
  144. package/skills/clickhouse-js-node-rowbinary-parser/src/examples/telemetry.ts +102 -0
  145. package/skills/clickhouse-js-node-rowbinary-parser/src/floats.ts +32 -0
  146. package/skills/clickhouse-js-node-rowbinary-parser/src/geo.ts +109 -0
  147. package/skills/clickhouse-js-node-rowbinary-parser/src/header.ts +29 -0
  148. package/skills/clickhouse-js-node-rowbinary-parser/src/integers.ts +95 -0
  149. package/skills/clickhouse-js-node-rowbinary-parser/src/interval.ts +54 -0
  150. package/skills/clickhouse-js-node-rowbinary-parser/src/ip.ts +93 -0
  151. package/skills/clickhouse-js-node-rowbinary-parser/src/json.ts +33 -0
  152. package/skills/clickhouse-js-node-rowbinary-parser/src/lowCardinality.ts +18 -0
  153. package/skills/clickhouse-js-node-rowbinary-parser/src/nested.ts +23 -0
  154. package/skills/clickhouse-js-node-rowbinary-parser/src/nothing.ts +29 -0
  155. package/skills/clickhouse-js-node-rowbinary-parser/src/reader.ts +68 -0
  156. package/skills/clickhouse-js-node-rowbinary-parser/src/rowBinaryWithNamesAndTypes.ts +155 -0
  157. package/skills/clickhouse-js-node-rowbinary-parser/src/rows.ts +58 -0
  158. package/skills/clickhouse-js-node-rowbinary-parser/src/simpleAggregateFunction.ts +20 -0
  159. package/skills/clickhouse-js-node-rowbinary-parser/src/stream.ts +276 -0
  160. package/skills/clickhouse-js-node-rowbinary-parser/src/strings.ts +55 -0
  161. package/skills/clickhouse-js-node-rowbinary-parser/src/time.ts +61 -0
  162. package/skills/clickhouse-js-node-rowbinary-parser/src/uuid.ts +153 -0
  163. package/skills/clickhouse-js-node-rowbinary-parser/src/varint.ts +70 -0
@@ -0,0 +1,102 @@
1
+ import {
2
+ readArray,
3
+ readMap,
4
+ readNullable,
5
+ readTupleNamed,
6
+ } from "../composite.js";
7
+ import { type Reader, advance } from "../core.js";
8
+ import { readFloat64 } from "../floats.js";
9
+ import { readUInt16, readUInt32 } from "../integers.js";
10
+ import { readString } from "../strings.js";
11
+ import { readUVarint } from "../varint.js";
12
+
13
+ /**
14
+ * Example: a telemetry table — composite readers that nest.
15
+ *
16
+ * Columns (the trigger):
17
+ * host String
18
+ * tags Map(String, String)
19
+ * cpu Array(Float64)
20
+ * region Nullable(String)
21
+ * window Tuple(start UInt32, count UInt16)
22
+ *
23
+ * The combinators compose exactly the way the column type nests:
24
+ * `readMap(k, v)`, `readArray(elem)`, `readNullable(inner)`, and
25
+ * `readTupleNamed({...})` each take sub-readers and return a `Reader`. This is the
26
+ * generic (closure-per-element) API; a generated parser would monomorphize these
27
+ * into inlined per-type loops, but the result shape is exactly this.
28
+ */
29
+ export type TelemetryRow = {
30
+ host: string;
31
+ tags: Map<string, string>;
32
+ cpu: number[];
33
+ region: string | null;
34
+ window: { start: number; count: number };
35
+ };
36
+
37
+ export const readTelemetryRow: Reader<TelemetryRow> = (s) => ({
38
+ host: readString(s),
39
+ tags: readMap(readString, readString)(s),
40
+ cpu: readArray(readFloat64)(s),
41
+ region: readNullable(readString)(s),
42
+ window: readTupleNamed({ start: readUInt32, count: readUInt16 })(s),
43
+ });
44
+
45
+ /**
46
+ * Optimized {@link readTelemetryRow}, fully monomorphized: the API version above
47
+ * builds FOUR combinator closures per row (`readMap(...)`, `readArray(...)`,
48
+ * `readNullable(...)`, `readTupleNamed(...)`) and, for the named tuple, iterates
49
+ * a keys array building an object field by field. Here every loop and branch is
50
+ * inlined and the `window` object is a flat literal — no per-row closures, no
51
+ * key iteration. The most composite-heavy example.
52
+ *
53
+ * MEASURED (Node 24 / V8, `telemetry.bench.ts`): ~1.4x faster — four per-row
54
+ * combinator closures and the named-tuple key iteration removed.
55
+ */
56
+ export const readTelemetryRowFast: Reader<TelemetryRow> = (s) => {
57
+ const { buf, view } = s;
58
+
59
+ // host String: length prefix, then the bytes.
60
+ let len = readUVarint(s);
61
+ let start = advance(s, len);
62
+ const host = buf.toString("utf8", start, start + len);
63
+
64
+ // tags Map(String, String): count, then key/value strings.
65
+ const mapN = readUVarint(s);
66
+ const tags = new Map<string, string>();
67
+ for (let i = 0; i < mapN; i++) {
68
+ len = readUVarint(s);
69
+ start = advance(s, len);
70
+ const k = buf.toString("utf8", start, start + len);
71
+ len = readUVarint(s);
72
+ start = advance(s, len);
73
+ tags.set(k, buf.toString("utf8", start, start + len));
74
+ }
75
+
76
+ // cpu Array(Float64): count, then 8 bytes each.
77
+ const cpuN = readUVarint(s);
78
+ const cpu = new Array<number>(cpuN);
79
+ for (let i = 0; i < cpuN; i++) {
80
+ cpu[i] = view.getFloat64(advance(s, 8), true);
81
+ }
82
+
83
+ // region Nullable(String): null-flag byte, then if non-null a string.
84
+ let region: string | null;
85
+ if (buf[advance(s, 1)]! !== 0) {
86
+ region = null;
87
+ } else {
88
+ len = readUVarint(s);
89
+ start = advance(s, len);
90
+ region = buf.toString("utf8", start, start + len);
91
+ }
92
+
93
+ // window Tuple(start UInt32, count UInt16): two adjacent fixed-width fields,
94
+ // bounds-checked once (6 bytes), then read at literal offsets.
95
+ const w = advance(s, 6);
96
+ const window = {
97
+ start: view.getUint32(w, true),
98
+ count: view.getUint16(w + 4, true),
99
+ };
100
+
101
+ return { host, tags, cpu, region, window };
102
+ };
@@ -0,0 +1,32 @@
1
+ import { Cursor, advance } from "./core.js";
2
+
3
+ /**
4
+ * Scratch view for widening a `BFloat16`: its 16 bits are the top half of an
5
+ * IEEE 754 float32, so we stage them into a 4-byte buffer and read a float32.
6
+ */
7
+ const bf16Scratch = new DataView(new ArrayBuffer(4));
8
+
9
+ /** Read a `Float32`: 4 bytes, little-endian IEEE 754 single precision. */
10
+ export function readFloat32(state: Cursor): number {
11
+ return state.view.getFloat32(advance(state, 4), true);
12
+ }
13
+
14
+ /** Read a `Float64`: 8 bytes, little-endian IEEE 754 double precision. */
15
+ export function readFloat64(state: Cursor): number {
16
+ return state.view.getFloat64(advance(state, 8), true);
17
+ }
18
+
19
+ /**
20
+ * Read a `BFloat16`: 2 bytes, little-endian. BFloat16 is the high 16 bits of a
21
+ * float32 (same 8-bit exponent, 7-bit mantissa), so placing the bits in the top
22
+ * half of a 32-bit float and reading it back is exact.
23
+ *
24
+ * NOTE: `bf16Scratch` is module-level shared state written-then-read in this
25
+ * function. That is safe because the read is synchronous; do NOT introduce an
26
+ * `await`/`yield` between the `setUint32` and the `getFloat32`.
27
+ */
28
+ export function readBFloat16(state: Cursor): number {
29
+ const bits = state.view.getUint16(advance(state, 2), true);
30
+ bf16Scratch.setUint32(0, bits * 0x10000, true);
31
+ return bf16Scratch.getFloat32(0, true);
32
+ }
@@ -0,0 +1,109 @@
1
+ import { Cursor } from "./core.js";
2
+ import { readFloat64 } from "./floats.js";
3
+ import { readUInt8 } from "./integers.js";
4
+ import { readUVarint } from "./varint.js";
5
+
6
+ /** A geo `Point`: `[x, y]`, the base of every ClickHouse geo type. */
7
+ export type Point = [x: number, y: number];
8
+
9
+ // Geo types are concrete compositions of Point = Tuple(Float64, Float64). They
10
+ // are monomorphic (no sub-readers) — the generator can emit them as-is.
11
+
12
+ /** Read a `Point`: `Tuple(Float64, Float64)` -> `[x, y]`. */
13
+ export function readPoint(state: Cursor): Point {
14
+ const x = readFloat64(state);
15
+ const y = readFloat64(state);
16
+ return [x, y];
17
+ }
18
+
19
+ /**
20
+ * Read a `Ring`: `Array(Point)` — a LEB128 point count, then that many points.
21
+ * `LineString` has the identical wire (see {@link readLineString}). `readPoint`
22
+ * is inlined here (two `readFloat64`s) to drop a call per point on this hot path.
23
+ */
24
+ export function readRing(state: Cursor): Point[] {
25
+ const n = readUVarint(state);
26
+ const out: Point[] = [];
27
+ for (let i = 0; i < n; i++) {
28
+ const x = readFloat64(state);
29
+ const y = readFloat64(state);
30
+ out.push([x, y]);
31
+ }
32
+ return out;
33
+ }
34
+
35
+ /**
36
+ * Read a `LineString`: `Array(Point)` (identical wire to a `Ring`). Points are
37
+ * inlined (two `readFloat64`s) to drop a call per point on this hot path.
38
+ */
39
+ export function readLineString(state: Cursor): Point[] {
40
+ const n = readUVarint(state);
41
+ const out: Point[] = [];
42
+ for (let i = 0; i < n; i++) {
43
+ const x = readFloat64(state);
44
+ const y = readFloat64(state);
45
+ out.push([x, y]);
46
+ }
47
+ return out;
48
+ }
49
+
50
+ /** Read a `Polygon`: `Array(Ring)` — the outer ring first, then any holes. */
51
+ export function readPolygon(state: Cursor): Point[][] {
52
+ const n = readUVarint(state);
53
+ const out: Point[][] = [];
54
+ for (let i = 0; i < n; i++) out.push(readRing(state));
55
+ return out;
56
+ }
57
+
58
+ /** Read a `MultiLineString`: `Array(LineString)` (identical wire to a `Polygon`). */
59
+ export function readMultiLineString(state: Cursor): Point[][] {
60
+ const n = readUVarint(state);
61
+ const out: Point[][] = [];
62
+ for (let i = 0; i < n; i++) out.push(readLineString(state));
63
+ return out;
64
+ }
65
+
66
+ /** Read a `MultiPolygon`: `Array(Polygon)`. */
67
+ export function readMultiPolygon(state: Cursor): Point[][][] {
68
+ const n = readUVarint(state);
69
+ const out: Point[][][] = [];
70
+ for (let i = 0; i < n; i++) out.push(readPolygon(state));
71
+ return out;
72
+ }
73
+
74
+ /**
75
+ * Read a `Geometry`: a named `Variant` over the six geo types. This is the
76
+ * MONOMORPHIZED form of `readVariant` for a concrete variant — a switch over the
77
+ * discriminant with each branch inlined, no reader array. The alternatives,
78
+ * sorted by type name (so in discriminant order), are LineString(0),
79
+ * MultiLineString(1), MultiPolygon(2), Point(3), Polygon(4), Ring(5); 0xFF is NULL.
80
+ *
81
+ * NOTE: the value shapes overlap — LineString and Ring are both `Point[]`,
82
+ * MultiLineString and Polygon both `Point[][]` — so the value alone does not say
83
+ * which geo type it was. If you need the kind, branch on the discriminant.
84
+ */
85
+ export function readGeometry(
86
+ state: Cursor,
87
+ ): Point | Point[] | Point[][] | Point[][][] | null {
88
+ const discriminant = readUInt8(state);
89
+ switch (discriminant) {
90
+ case 0:
91
+ return readLineString(state);
92
+ case 1:
93
+ return readMultiLineString(state);
94
+ case 2:
95
+ return readMultiPolygon(state);
96
+ case 3:
97
+ return readPoint(state);
98
+ case 4:
99
+ return readPolygon(state);
100
+ case 5:
101
+ return readRing(state);
102
+ case 0xff:
103
+ return null;
104
+ default:
105
+ throw new RangeError(
106
+ `RowBinary: unknown Geometry discriminant ${discriminant}`,
107
+ );
108
+ }
109
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The `RowBinaryWithNamesAndTypes` HEADER: the column names and their type
3
+ * strings the server writes before the row data. Parsing the type strings into
4
+ * readers lives in `compile.ts`; this module is just the wire read.
5
+ */
6
+
7
+ import type { Cursor } from "./core.js";
8
+ import { readUVarint } from "./varint.js";
9
+ import { readString } from "./strings.js";
10
+
11
+ /** Column names and their type strings, in stream order. */
12
+ export interface RowBinaryHeader {
13
+ names: string[];
14
+ types: string[];
15
+ }
16
+
17
+ /**
18
+ * Read the `RowBinaryWithNamesAndTypes` header off the cursor: a LEB128 column
19
+ * count, then that many column-name `String`s, then that many type-string
20
+ * `String`s. Leaves the cursor at the first row's bytes.
21
+ */
22
+ export function readHeader(state: Cursor): RowBinaryHeader {
23
+ const count = readUVarint(state);
24
+ const names: string[] = new Array(count);
25
+ for (let i = 0; i < count; i++) names[i] = readString(state);
26
+ const types: string[] = new Array(count);
27
+ for (let i = 0; i < count; i++) types[i] = readString(state);
28
+ return { names, types };
29
+ }
@@ -0,0 +1,95 @@
1
+ import { Cursor, advance } from "./core.js";
2
+
3
+ /** Read a single unsigned byte and advance. */
4
+ export function readUInt8(state: Cursor): number {
5
+ return state.buf[advance(state, 1)]!;
6
+ }
7
+
8
+ /** Read an `Int8`: 1 byte, two's-complement signed (-128 .. 127). */
9
+ export function readInt8(state: Cursor): number {
10
+ return state.view.getInt8(advance(state, 1));
11
+ }
12
+
13
+ /** Read a `UInt16`: 2 bytes, little-endian (0 .. 65535). */
14
+ export function readUInt16(state: Cursor): number {
15
+ return state.view.getUint16(advance(state, 2), true);
16
+ }
17
+
18
+ /**
19
+ * Read an `Int16`: 2 bytes, little-endian, two's-complement signed (-32768 ..
20
+ * 32767). `DataView` reads from any offset and decodes explicitly little-endian,
21
+ * so the value never depends on host byte order.
22
+ */
23
+ export function readInt16(state: Cursor): number {
24
+ return state.view.getInt16(advance(state, 2), true);
25
+ }
26
+
27
+ /** Read a `UInt32`: 4 bytes, little-endian (0 .. 4294967295). */
28
+ export function readUInt32(state: Cursor): number {
29
+ return state.view.getUint32(advance(state, 4), true);
30
+ }
31
+
32
+ /** Read an `Int32`: 4 bytes, little-endian, two's-complement signed. */
33
+ export function readInt32(state: Cursor): number {
34
+ return state.view.getInt32(advance(state, 4), true);
35
+ }
36
+
37
+ /**
38
+ * Read a `UInt64`: 8 bytes, little-endian. Returns a `bigint`.
39
+ * SAFE TO TOGGLE: if the values fit in 53 bits, wrap in `Number(...)`.
40
+ */
41
+ export function readUInt64(state: Cursor): bigint {
42
+ return state.view.getBigUint64(advance(state, 8), true);
43
+ }
44
+
45
+ /**
46
+ * Read an `Int64`: 8 bytes, little-endian, two's-complement. Returns a `bigint`
47
+ * (range exceeds `Number.MAX_SAFE_INTEGER`).
48
+ * SAFE TO TOGGLE: if the values fit in 53 bits, wrap in `Number(...)`.
49
+ */
50
+ export function readInt64(state: Cursor): bigint {
51
+ return state.view.getBigInt64(advance(state, 8), true);
52
+ }
53
+
54
+ /** Read a `UInt128`: 16 bytes, little-endian. Always a `bigint`. */
55
+ export function readUInt128(state: Cursor): bigint {
56
+ const start = advance(state, 16);
57
+ const lo = state.view.getBigUint64(start, true);
58
+ const hi = state.view.getBigUint64(start + 8, true);
59
+ return (hi << 64n) + lo;
60
+ }
61
+
62
+ /**
63
+ * Read an `Int128`: 16 bytes, little-endian, two's-complement. Always a
64
+ * `bigint`, composed from the low (unsigned) and high (signed) 64-bit words —
65
+ * reading the high word signed extends the sign across all 128 bits.
66
+ */
67
+ export function readInt128(state: Cursor): bigint {
68
+ const start = advance(state, 16);
69
+ const lo = state.view.getBigUint64(start, true);
70
+ const hi = state.view.getBigInt64(start + 8, true);
71
+ return (hi << 64n) + lo;
72
+ }
73
+
74
+ /** Read a `UInt256`: 32 bytes, little-endian. Always a `bigint`. */
75
+ export function readUInt256(state: Cursor): bigint {
76
+ const start = advance(state, 32);
77
+ const w0 = state.view.getBigUint64(start, true);
78
+ const w1 = state.view.getBigUint64(start + 8, true);
79
+ const w2 = state.view.getBigUint64(start + 16, true);
80
+ const w3 = state.view.getBigUint64(start + 24, true);
81
+ return w0 + (w1 << 64n) + (w2 << 128n) + (w3 << 192n);
82
+ }
83
+
84
+ /**
85
+ * Read an `Int256`: 32 bytes, little-endian, two's-complement. Always a
86
+ * `bigint`. The most-significant 64-bit word is read signed.
87
+ */
88
+ export function readInt256(state: Cursor): bigint {
89
+ const start = advance(state, 32);
90
+ const w0 = state.view.getBigUint64(start, true);
91
+ const w1 = state.view.getBigUint64(start + 8, true);
92
+ const w2 = state.view.getBigUint64(start + 16, true);
93
+ const w3 = state.view.getBigInt64(start + 24, true);
94
+ return (w3 << 192n) + (w2 << 128n) + (w1 << 64n) + w0;
95
+ }
@@ -0,0 +1,54 @@
1
+ import { Cursor } from "./core.js";
2
+ import { readInt64 } from "./integers.js";
3
+
4
+ /** The 11 `Interval` units, in ClickHouse's ascending order. */
5
+ export type IntervalUnit =
6
+ | "Nanosecond"
7
+ | "Microsecond"
8
+ | "Millisecond"
9
+ | "Second"
10
+ | "Minute"
11
+ | "Hour"
12
+ | "Day"
13
+ | "Week"
14
+ | "Month"
15
+ | "Quarter"
16
+ | "Year";
17
+
18
+ /**
19
+ * `Interval` units indexed by the kind byte the binary type encoding writes
20
+ * after the `0x22` tag (`0x00` = Nanosecond ... `0x0a` = Year). Exported because
21
+ * the `Dynamic` reader needs it to decode an `Interval` nested in a `Dynamic`.
22
+ */
23
+ export const INTERVAL_UNITS: readonly IntervalUnit[] = [
24
+ "Nanosecond",
25
+ "Microsecond",
26
+ "Millisecond",
27
+ "Second",
28
+ "Minute",
29
+ "Hour",
30
+ "Day",
31
+ "Week",
32
+ "Month",
33
+ "Quarter",
34
+ "Year",
35
+ ];
36
+
37
+ /**
38
+ * An `Interval` decoded where the unit is carried IN the wire (inside a
39
+ * `Dynamic`): the signed `Int64` count plus its unit. A standalone `Interval*`
40
+ * column has no unit byte — there, use {@link readInterval} and take the unit
41
+ * from the column type instead.
42
+ */
43
+ export type IntervalValue = readonly [count: bigint, unit: IntervalUnit];
44
+
45
+ /**
46
+ * Read an `Interval` — any of `IntervalNanosecond` ... `IntervalYear`: a signed
47
+ * `Int64` count of the unit. The unit is in the type name, not the bytes, and
48
+ * all 11 interval types share this exact wire, so this one reader covers them
49
+ * all; the caller knows the unit from the column type. Returns a `bigint`; wrap
50
+ * in `Number(...)` if the counts are known to fit in 53 bits.
51
+ */
52
+ export function readInterval(state: Cursor): bigint {
53
+ return readInt64(state);
54
+ }
@@ -0,0 +1,93 @@
1
+ import { Cursor, advance } from "./core.js";
2
+
3
+ /**
4
+ * Read an `IPv4`: stored as a 4-byte little-endian `UInt32`. Returns the raw
5
+ * 32-bit value (the little-endian load already orders the octets); pass it to
6
+ * {@link formatIPv4} for the dotted-quad string.
7
+ */
8
+ export function readIPv4(state: Cursor): number {
9
+ return state.view.getUint32(advance(state, 4), true);
10
+ }
11
+
12
+ /**
13
+ * Read an `IPv6`: 16 bytes in network (big-endian) order. Returns the raw bytes
14
+ * as a zero-copy view; pass them to {@link formatIPv6} for the canonical string.
15
+ *
16
+ * The view shares memory with the response buffer, so keeping it alive pins the
17
+ * whole response chunk in memory. If the value must outlive the row/response,
18
+ * copy it with `Buffer.from(...)`.
19
+ */
20
+ export function readIPv6(state: Cursor): Buffer {
21
+ const start = advance(state, 16);
22
+ return state.buf.subarray(start, start + 16);
23
+ }
24
+
25
+ /**
26
+ * Format an `IPv4` (the raw 32-bit value from {@link readIPv4}) as a dotted-quad
27
+ * string. Kept aside so the hot read path can skip building a string when the
28
+ * numeric value is all the caller needs.
29
+ */
30
+ export function formatIPv4(value: number): string {
31
+ return `${(value >>> 24) & 0xff}.${(value >>> 16) & 0xff}.${(value >>> 8) & 0xff}.${value & 0xff}`;
32
+ }
33
+
34
+ /**
35
+ * Join groups `[from, to)` as colon-separated lowercase hex, by concatenating
36
+ * into a string in a loop. Benchmarks faster than `slice().map().join(":")`,
37
+ * which allocates an intermediate array. Returns `""` for an empty range.
38
+ */
39
+ function joinGroupsHex(g: number[], from: number, to: number): string {
40
+ if (from >= to) return "";
41
+ let s = g[from]!.toString(16);
42
+ for (let i = from + 1; i < to; i++) s += ":" + g[i]!.toString(16);
43
+ return s;
44
+ }
45
+
46
+ /**
47
+ * Format an `IPv6` (the raw 16 bytes from {@link readIPv6}) as the canonical
48
+ * RFC 5952 string: lowercase, no leading zeros, the longest run of zero groups
49
+ * (>= 2) collapsed to `::` (leftmost on a tie), and the `::ffff:a.b.c.d` form
50
+ * for IPv4-mapped addresses (matching ClickHouse).
51
+ *
52
+ * Kept aside from the read so the hot path only formats when a string is
53
+ * actually needed.
54
+ */
55
+ export function formatIPv6(b: Buffer): string {
56
+ // IPv4-mapped (::ffff:a.b.c.d): first 10 bytes zero, then 0xffff.
57
+ let mapped = b[10] === 0xff && b[11] === 0xff;
58
+ for (let i = 0; mapped && i < 10; i++) {
59
+ if (b[i] !== 0) mapped = false;
60
+ }
61
+ if (mapped) {
62
+ return `::ffff:${b[12]}.${b[13]}.${b[14]}.${b[15]}`;
63
+ }
64
+
65
+ // Eight 16-bit groups, big-endian.
66
+ const g: number[] = [];
67
+ for (let i = 0; i < 8; i++) {
68
+ g.push((b[2 * i]! << 8) | b[2 * i + 1]!);
69
+ }
70
+
71
+ // Longest run of >= 2 zero groups becomes "::" (leftmost wins on a tie).
72
+ let bestStart = -1;
73
+ let bestLen = 0;
74
+ let curStart = -1;
75
+ let curLen = 0;
76
+ for (let i = 0; i < 8; i++) {
77
+ if (g[i] === 0) {
78
+ if (curStart < 0) curStart = i;
79
+ curLen++;
80
+ if (curLen > bestLen) {
81
+ bestLen = curLen;
82
+ bestStart = curStart;
83
+ }
84
+ } else {
85
+ curStart = -1;
86
+ curLen = 0;
87
+ }
88
+ }
89
+ if (bestLen < 2) {
90
+ return joinGroupsHex(g, 0, 8);
91
+ }
92
+ return `${joinGroupsHex(g, 0, bestStart)}::${joinGroupsHex(g, bestStart + bestLen, 8)}`;
93
+ }
@@ -0,0 +1,33 @@
1
+ import { Cursor } from "./core.js";
2
+ import { readUVarint } from "./varint.js";
3
+ import { readString } from "./strings.js";
4
+ import { readDynamic } from "./dynamic.js";
5
+
6
+ /**
7
+ * Read a `JSON` value. ClickHouse's `JSON` is NOT JSON text and NOT BSON — it is
8
+ * a list of (path, value) pairs built on the same machinery as `Dynamic`:
9
+ *
10
+ * <varuint pathCount> then pathCount x ( <String path> <Dynamic value> )
11
+ *
12
+ * Nested objects are FLATTENED to dotted paths (`{a:{b:2}}` -> path `"a.b"`), and
13
+ * each leaf value is a self-describing `Dynamic`, so this just loops
14
+ * {@link readString} + {@link readDynamic}. Returns a `Map` keyed by the flat
15
+ * dotted path. Path order on the wire is not significant.
16
+ *
17
+ * GOTCHA: a null-valued path is NOT stored at all — `{"a":null}` serializes as
18
+ * zero paths, identical to `{}`. JSON arrays come back as `Array(Nullable(T))`.
19
+ *
20
+ * LIMITATION — typed paths only. This reads a plain `JSON` column, where every
21
+ * path is dynamic (tagged). A `JSON(a T, ...)` with DECLARED typed paths
22
+ * serializes those paths' values WITHOUT a type tag, so they cannot be decoded
23
+ * without the schema; read each typed path with its known `T` reader instead.
24
+ */
25
+ export function readJSON(state: Cursor): Map<string, unknown> {
26
+ const n = readUVarint(state);
27
+ const out = new Map<string, unknown>();
28
+ for (let i = 0; i < n; i++) {
29
+ const path = readString(state);
30
+ out.set(path, readDynamic(state));
31
+ }
32
+ return out;
33
+ }
@@ -0,0 +1,18 @@
1
+ import { type Reader } from "./core.js";
2
+
3
+ /**
4
+ * `LowCardinality(T)` is TRANSPARENT in RowBinary: it is encoded byte-for-byte
5
+ * the same as `T`, with NO dictionary/index layer. (The dictionary encoding
6
+ * exists only in the Native format — do not look for it here.) So there is
7
+ * nothing to decode at this level: use `T`'s own reader directly.
8
+ *
9
+ * This identity combinator exists only to document that, and to let a generated
10
+ * parser name the wrapper at the call site if it wants the type to read
11
+ * literally — it returns the inner reader unchanged:
12
+ *
13
+ * readLowCardinality(readString) === readString
14
+ *
15
+ * Prefer just calling the inner reader.
16
+ */
17
+ export const readLowCardinality = <T>(readValue: Reader<T>): Reader<T> =>
18
+ readValue;
@@ -0,0 +1,23 @@
1
+ import { readArray, readTupleNamed } from "./composite.js";
2
+ import { type Reader } from "./core.js";
3
+
4
+ /**
5
+ * `Nested(a T1, b T2, …)` has NO wire format of its own:
6
+ * - `flatten_nested = 1` (the default): the column expands into separate
7
+ * columns `a Array(T1)`, `b Array(T2)`, … — decode each with `readArray`.
8
+ * - `flatten_nested = 0`: the column is `Array(Tuple(a T1, b T2, …))` — decode
9
+ * with `readArray` + `readTupleNamed`.
10
+ *
11
+ * Either way it reuses existing readers; there is no dedicated Nested wire. This
12
+ * thin alias just composes the two for the `flatten_nested = 0` shape, as
13
+ * documentation that "Nested === Array(Tuple(...))":
14
+ *
15
+ * readNested({ a: readUInt8, b: readString })
16
+ * === readArray(readTupleNamed({ a: readUInt8, b: readString }))
17
+ *
18
+ * When generating code, prefer inlining (monomorphize the array + tuple) over
19
+ * this generic composition.
20
+ */
21
+ export const readNested = <T extends Record<string, unknown>>(fields: {
22
+ [K in keyof T]: Reader<T[K]>;
23
+ }): Reader<T[]> => readArray(readTupleNamed(fields));
@@ -0,0 +1,29 @@
1
+ import { type Reader } from "./core.js";
2
+
3
+ /**
4
+ * `Nothing` is the empty type: it has NO values and occupies ZERO bytes. It is
5
+ * never a column on its own (you cannot materialize a value of it) — it only
6
+ * appears wrapped, as the inferred element of an untyped literal:
7
+ *
8
+ * [] -> Array(Nothing) -> always the empty array (varint length 0x00)
9
+ * NULL -> Nullable(Nothing) -> always NULL (lone flag byte 0x01)
10
+ *
11
+ * So a `Nothing` value is NEVER read: `readArray`'s element reader and
12
+ * `readNullable`'s inner reader are not called in those cases (the array is
13
+ * empty / the value is NULL). There is nothing to decode.
14
+ *
15
+ * Wire this in as the inner reader to make that invariant loud: it throws if it
16
+ * is ever actually invoked, which would mean a `Nothing` reader was placed where
17
+ * a real element/inner type was expected.
18
+ *
19
+ * readArray(readNothing) // [] — readNothing never runs
20
+ * readNullable(readNothing) // null — readNothing never runs
21
+ */
22
+ export const readNothing: Reader<never> = () => {
23
+ throw new Error(
24
+ "RowBinary: Nothing is zero-width and is never decoded — it only appears as " +
25
+ "an empty Array(Nothing) or a NULL Nullable(Nothing), where the inner reader " +
26
+ "is not called. Reaching here means a Nothing reader was wired where a real " +
27
+ "element/inner type was expected.",
28
+ );
29
+ };