@clickhouse/client 1.22.0 → 1.23.0-head.70ad405.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 (191) hide show
  1. package/CHANGELOG.md +1342 -0
  2. package/README.md +20 -7
  3. package/dist/client.d.ts +2 -2
  4. package/dist/client.js +11 -4
  5. package/dist/client.js.map +1 -1
  6. package/dist/common/clickhouse_types.d.ts +98 -0
  7. package/dist/common/clickhouse_types.js +30 -0
  8. package/dist/common/clickhouse_types.js.map +1 -0
  9. package/dist/common/client.d.ts +233 -0
  10. package/dist/common/client.js +414 -0
  11. package/dist/common/client.js.map +1 -0
  12. package/dist/common/config.d.ts +234 -0
  13. package/dist/common/config.js +364 -0
  14. package/dist/common/config.js.map +1 -0
  15. package/dist/common/connection.d.ts +124 -0
  16. package/dist/common/connection.js +3 -0
  17. package/dist/common/connection.js.map +1 -0
  18. package/dist/common/data_formatter/format_query_params.d.ts +11 -0
  19. package/dist/common/data_formatter/format_query_params.js +128 -0
  20. package/dist/common/data_formatter/format_query_params.js.map +1 -0
  21. package/dist/common/data_formatter/format_query_settings.d.ts +2 -0
  22. package/dist/common/data_formatter/format_query_settings.js +20 -0
  23. package/dist/common/data_formatter/format_query_settings.js.map +1 -0
  24. package/dist/common/data_formatter/formatter.d.ts +41 -0
  25. package/dist/common/data_formatter/formatter.js +78 -0
  26. package/dist/common/data_formatter/formatter.js.map +1 -0
  27. package/dist/common/data_formatter/index.d.ts +3 -0
  28. package/dist/common/data_formatter/index.js +24 -0
  29. package/dist/common/data_formatter/index.js.map +1 -0
  30. package/dist/common/error/error.d.ts +20 -0
  31. package/dist/common/error/error.js +73 -0
  32. package/dist/common/error/error.js.map +1 -0
  33. package/dist/common/error/index.d.ts +1 -0
  34. package/dist/common/error/index.js +18 -0
  35. package/dist/common/error/index.js.map +1 -0
  36. package/dist/common/index.d.ts +67 -0
  37. package/dist/common/index.js +97 -0
  38. package/dist/common/index.js.map +1 -0
  39. package/dist/common/logger.d.ts +80 -0
  40. package/dist/common/logger.js +154 -0
  41. package/dist/common/logger.js.map +1 -0
  42. package/dist/common/parse/column_types.d.ts +155 -0
  43. package/dist/common/parse/column_types.js +594 -0
  44. package/dist/common/parse/column_types.js.map +1 -0
  45. package/dist/common/parse/index.d.ts +2 -0
  46. package/dist/common/parse/index.js +19 -0
  47. package/dist/common/parse/index.js.map +1 -0
  48. package/dist/common/parse/json_handling.d.ts +19 -0
  49. package/dist/common/parse/json_handling.js +8 -0
  50. package/dist/common/parse/json_handling.js.map +1 -0
  51. package/dist/common/result.d.ts +90 -0
  52. package/dist/common/result.js +3 -0
  53. package/dist/common/result.js.map +1 -0
  54. package/dist/common/settings.d.ts +2007 -0
  55. package/dist/common/settings.js +19 -0
  56. package/dist/common/settings.js.map +1 -0
  57. package/dist/common/tracing.d.ts +146 -0
  58. package/dist/common/tracing.js +76 -0
  59. package/dist/common/tracing.js.map +1 -0
  60. package/dist/common/ts_utils.d.ts +4 -0
  61. package/dist/common/ts_utils.js +3 -0
  62. package/dist/common/ts_utils.js.map +1 -0
  63. package/dist/common/utils/connection.d.ts +21 -0
  64. package/dist/common/utils/connection.js +43 -0
  65. package/dist/common/utils/connection.js.map +1 -0
  66. package/dist/common/utils/index.d.ts +5 -0
  67. package/dist/common/utils/index.js +22 -0
  68. package/dist/common/utils/index.js.map +1 -0
  69. package/dist/common/utils/multipart.d.ts +34 -0
  70. package/dist/common/utils/multipart.js +81 -0
  71. package/dist/common/utils/multipart.js.map +1 -0
  72. package/dist/common/utils/sleep.d.ts +4 -0
  73. package/dist/common/utils/sleep.js +12 -0
  74. package/dist/common/utils/sleep.js.map +1 -0
  75. package/dist/common/utils/stream.d.ts +15 -0
  76. package/dist/common/utils/stream.js +50 -0
  77. package/dist/common/utils/stream.js.map +1 -0
  78. package/dist/common/utils/url.d.ts +20 -0
  79. package/dist/common/utils/url.js +67 -0
  80. package/dist/common/utils/url.js.map +1 -0
  81. package/dist/common/version.d.ts +2 -0
  82. package/dist/common/version.js +4 -0
  83. package/dist/common/version.js.map +1 -0
  84. package/dist/config.d.ts +22 -2
  85. package/dist/config.js +2 -2
  86. package/dist/config.js.map +1 -1
  87. package/dist/connection/compression.d.ts +2 -2
  88. package/dist/connection/compression.js +4 -4
  89. package/dist/connection/compression.js.map +1 -1
  90. package/dist/connection/create_connection.d.ts +1 -1
  91. package/dist/connection/node_base_connection.d.ts +3 -3
  92. package/dist/connection/node_base_connection.js +22 -22
  93. package/dist/connection/node_base_connection.js.map +1 -1
  94. package/dist/connection/node_custom_agent_connection.js +2 -2
  95. package/dist/connection/node_custom_agent_connection.js.map +1 -1
  96. package/dist/connection/node_http_connection.js +2 -2
  97. package/dist/connection/node_http_connection.js.map +1 -1
  98. package/dist/connection/node_https_connection.d.ts +1 -1
  99. package/dist/connection/node_https_connection.js +3 -3
  100. package/dist/connection/node_https_connection.js.map +1 -1
  101. package/dist/connection/socket_pool.d.ts +1 -1
  102. package/dist/connection/socket_pool.js +30 -30
  103. package/dist/connection/socket_pool.js.map +1 -1
  104. package/dist/connection/stream.d.ts +1 -1
  105. package/dist/connection/stream.js +9 -9
  106. package/dist/connection/stream.js.map +1 -1
  107. package/dist/index.d.ts +9 -7
  108. package/dist/index.js +26 -24
  109. package/dist/index.js.map +1 -1
  110. package/dist/result_set.d.ts +1 -1
  111. package/dist/result_set.js +10 -10
  112. package/dist/result_set.js.map +1 -1
  113. package/dist/utils/encoder.d.ts +1 -1
  114. package/dist/utils/encoder.js +5 -5
  115. package/dist/utils/encoder.js.map +1 -1
  116. package/dist/version.d.ts +1 -1
  117. package/dist/version.js +1 -1
  118. package/dist/version.js.map +1 -1
  119. package/package.json +10 -7
  120. package/skills/AGENTS.md +8 -0
  121. package/skills/clickhouse-js-node-rowbinary/AGENTS.md +44 -0
  122. package/skills/clickhouse-js-node-rowbinary/CHANGELOG.md +49 -0
  123. package/skills/clickhouse-js-node-rowbinary/EXAMPLES.md +48 -0
  124. package/skills/clickhouse-js-node-rowbinary/README.md +319 -0
  125. package/skills/clickhouse-js-node-rowbinary/SKILL.md +111 -0
  126. package/skills/clickhouse-js-node-rowbinary/case-studies/iot-rowbinary-vs-json.md +83 -0
  127. package/skills/clickhouse-js-node-rowbinary/case-studies/ledger-rowbinary-vs-json.md +103 -0
  128. package/skills/clickhouse-js-node-rowbinary/case-studies/logs-json-wins.md +86 -0
  129. package/skills/clickhouse-js-node-rowbinary/case-studies/wasm-vs-js.md +172 -0
  130. package/skills/clickhouse-js-node-rowbinary/reader.md +126 -0
  131. package/skills/clickhouse-js-node-rowbinary/src/examples/carts.ts +75 -0
  132. package/skills/clickhouse-js-node-rowbinary/src/examples/events.ts +51 -0
  133. package/skills/clickhouse-js-node-rowbinary/src/examples/iot.ts +158 -0
  134. package/skills/clickhouse-js-node-rowbinary/src/examples/ledger.ts +98 -0
  135. package/skills/clickhouse-js-node-rowbinary/src/examples/logs.ts +73 -0
  136. package/skills/clickhouse-js-node-rowbinary/src/examples/observability.ts +141 -0
  137. package/skills/clickhouse-js-node-rowbinary/src/examples/orders.ts +66 -0
  138. package/skills/clickhouse-js-node-rowbinary/src/examples/profiles.ts +60 -0
  139. package/skills/clickhouse-js-node-rowbinary/src/examples/telemetry.ts +102 -0
  140. package/skills/clickhouse-js-node-rowbinary/src/readers/aggregateFunction.ts +34 -0
  141. package/skills/clickhouse-js-node-rowbinary/src/readers/bool.ts +10 -0
  142. package/skills/clickhouse-js-node-rowbinary/src/readers/columnar.ts +125 -0
  143. package/skills/clickhouse-js-node-rowbinary/src/readers/compile.ts +328 -0
  144. package/skills/clickhouse-js-node-rowbinary/src/readers/composite.ts +181 -0
  145. package/skills/clickhouse-js-node-rowbinary/src/readers/core.ts +77 -0
  146. package/skills/clickhouse-js-node-rowbinary/src/readers/datetime.ts +113 -0
  147. package/skills/clickhouse-js-node-rowbinary/src/readers/decimals.ts +57 -0
  148. package/skills/clickhouse-js-node-rowbinary/src/readers/dynamic.ts +332 -0
  149. package/skills/clickhouse-js-node-rowbinary/src/readers/enums.ts +40 -0
  150. package/skills/clickhouse-js-node-rowbinary/src/readers/floats.ts +32 -0
  151. package/skills/clickhouse-js-node-rowbinary/src/readers/geo.ts +109 -0
  152. package/skills/clickhouse-js-node-rowbinary/src/readers/header.ts +29 -0
  153. package/skills/clickhouse-js-node-rowbinary/src/readers/integers.ts +95 -0
  154. package/skills/clickhouse-js-node-rowbinary/src/readers/interval.ts +54 -0
  155. package/skills/clickhouse-js-node-rowbinary/src/readers/ip.ts +93 -0
  156. package/skills/clickhouse-js-node-rowbinary/src/readers/json.ts +33 -0
  157. package/skills/clickhouse-js-node-rowbinary/src/readers/lowCardinality.ts +18 -0
  158. package/skills/clickhouse-js-node-rowbinary/src/readers/nested.ts +23 -0
  159. package/skills/clickhouse-js-node-rowbinary/src/readers/nothing.ts +29 -0
  160. package/skills/clickhouse-js-node-rowbinary/src/readers/reader.ts +68 -0
  161. package/skills/clickhouse-js-node-rowbinary/src/readers/rowBinaryWithNamesAndTypes.ts +155 -0
  162. package/skills/clickhouse-js-node-rowbinary/src/readers/rows.ts +58 -0
  163. package/skills/clickhouse-js-node-rowbinary/src/readers/simpleAggregateFunction.ts +20 -0
  164. package/skills/clickhouse-js-node-rowbinary/src/readers/stream.ts +276 -0
  165. package/skills/clickhouse-js-node-rowbinary/src/readers/strings.ts +55 -0
  166. package/skills/clickhouse-js-node-rowbinary/src/readers/time.ts +61 -0
  167. package/skills/clickhouse-js-node-rowbinary/src/readers/uuid.ts +153 -0
  168. package/skills/clickhouse-js-node-rowbinary/src/readers/varint.ts +70 -0
  169. package/skills/clickhouse-js-node-rowbinary/src/writers/aggregateFunction.ts +18 -0
  170. package/skills/clickhouse-js-node-rowbinary/src/writers/bool.ts +10 -0
  171. package/skills/clickhouse-js-node-rowbinary/src/writers/composite.ts +140 -0
  172. package/skills/clickhouse-js-node-rowbinary/src/writers/core.ts +92 -0
  173. package/skills/clickhouse-js-node-rowbinary/src/writers/datetime.ts +123 -0
  174. package/skills/clickhouse-js-node-rowbinary/src/writers/decimals.ts +51 -0
  175. package/skills/clickhouse-js-node-rowbinary/src/writers/enums.ts +18 -0
  176. package/skills/clickhouse-js-node-rowbinary/src/writers/floats.ts +40 -0
  177. package/skills/clickhouse-js-node-rowbinary/src/writers/geo.ts +125 -0
  178. package/skills/clickhouse-js-node-rowbinary/src/writers/integers.ts +90 -0
  179. package/skills/clickhouse-js-node-rowbinary/src/writers/interval.ts +11 -0
  180. package/skills/clickhouse-js-node-rowbinary/src/writers/ip.ts +121 -0
  181. package/skills/clickhouse-js-node-rowbinary/src/writers/lowCardinality.ts +12 -0
  182. package/skills/clickhouse-js-node-rowbinary/src/writers/nested.ts +17 -0
  183. package/skills/clickhouse-js-node-rowbinary/src/writers/nothing.ts +21 -0
  184. package/skills/clickhouse-js-node-rowbinary/src/writers/rows.ts +144 -0
  185. package/skills/clickhouse-js-node-rowbinary/src/writers/simpleAggregateFunction.ts +12 -0
  186. package/skills/clickhouse-js-node-rowbinary/src/writers/strings.ts +77 -0
  187. package/skills/clickhouse-js-node-rowbinary/src/writers/time.ts +54 -0
  188. package/skills/clickhouse-js-node-rowbinary/src/writers/uuid.ts +60 -0
  189. package/skills/clickhouse-js-node-rowbinary/src/writers/varint.ts +64 -0
  190. package/skills/clickhouse-js-node-rowbinary/src/writers/writer.ts +101 -0
  191. package/skills/clickhouse-js-node-rowbinary/writer.md +96 -0
@@ -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
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Barrel re-export of the RowBinary reader, split by type family into the
3
+ * sibling modules. Import from here for everything in one place, or from a
4
+ * specific module (e.g. `./integers.js`, `./strings.js`) to pull in only the
5
+ * sub-parsers a given result actually needs — the latter is what a generated
6
+ * parser should do, copying just the modules its column types require.
7
+ *
8
+ * - core — Cursor, Reader<T>, advance, NeedMoreData
9
+ * - varint — readUVarint
10
+ * - integers — readUInt8..readUInt256, readInt8..readInt256
11
+ * - bool / enums / floats
12
+ * - decimals — DecimalValue, formatDecimal, readDecimal32..256
13
+ * - strings — readString, readFixedString, readFixedStringBytes
14
+ * - uuid — readUUID(+BigInt/HiLo), formatUUID(+Table)
15
+ * - ip — readIPv4/6, formatIPv4/6
16
+ * - datetime / time / interval
17
+ * - composite — readArray/Map/Tuple/TupleNamed/Nullable/Variant/QBit
18
+ * - rows — readRows
19
+ * - geo — Point, readPoint/Ring/LineString/Polygon/MultiLineString/MultiPolygon/Geometry
20
+ * - dynamic — readDynamic, readDynamicType
21
+ * - json — readJSON
22
+ * - stream — streamRowBatches, coalesceChunks
23
+ * - transparent / special wrappers (mostly documentation; see each file):
24
+ * lowCardinality (readLowCardinality), simpleAggregateFunction
25
+ * (readSimpleAggregateFunction), nested (readNested), nothing (readNothing),
26
+ * aggregateFunction (readAggregateFunction)
27
+ *
28
+ * Runtime schema path — compile a reader from the type STRINGS rather than
29
+ * hand-/code-generating one. Use this when the column types are not known until
30
+ * the response arrives (or you just want a generic decoder); for a fixed,
31
+ * known schema the specialized straight-line reader is faster.
32
+ * - header — readHeader: the RowBinaryWithNamesAndTypes preamble (column
33
+ * names + type strings) off the cursor
34
+ * - compile — astToReader: fold one parsed type AST (from
35
+ * `@clickhouse/datatype-parser`) into a value Reader. AST in,
36
+ * reader out — the type-to-combinator mapping, nothing else
37
+ * - rowBinaryWithNamesAndTypes — typeStringToReader (parse a type string +
38
+ * fold) and compileRowBinaryWithNamesAndTypes (read the header,
39
+ * compile every column, return a `readRows` driver for the rest of
40
+ * the stream): the end-to-end runtime entry point
41
+ */
42
+ export * from "./core.js";
43
+ export * from "./varint.js";
44
+ export * from "./integers.js";
45
+ export * from "./bool.js";
46
+ export * from "./enums.js";
47
+ export * from "./floats.js";
48
+ export * from "./decimals.js";
49
+ export * from "./strings.js";
50
+ export * from "./uuid.js";
51
+ export * from "./ip.js";
52
+ export * from "./datetime.js";
53
+ export * from "./time.js";
54
+ export * from "./interval.js";
55
+ export * from "./composite.js";
56
+ export * from "./rows.js";
57
+ export * from "./geo.js";
58
+ export * from "./dynamic.js";
59
+ export * from "./json.js";
60
+ export * from "./stream.js";
61
+ export * from "./lowCardinality.js";
62
+ export * from "./simpleAggregateFunction.js";
63
+ export * from "./nested.js";
64
+ export * from "./nothing.js";
65
+ export * from "./aggregateFunction.js";
66
+ export * from "./header.js";
67
+ export * from "./compile.js";
68
+ export * from "./rowBinaryWithNamesAndTypes.js";
@@ -0,0 +1,155 @@
1
+ /**
2
+ * The `RowBinaryWithNamesAndTypes` entry point: read the header off a cursor,
3
+ * compile each column's type string into a reader, and hand back a driver that
4
+ * decodes the rest of the stream.
5
+ *
6
+ * This ties together the pieces: {@link readHeader} (wire), the parser
7
+ * (`@clickhouse/datatype-parser`), and {@link astToReader} (the AST → reader
8
+ * fold in `compile.ts`), then assembles a named-tuple row reader over the
9
+ * columns and a {@link readRows} driver for the row data.
10
+ */
11
+
12
+ import { parseDataType } from "@clickhouse/datatype-parser";
13
+
14
+ import type { Reader, Cursor } from "./core.js";
15
+ import { readHeader } from "./header.js";
16
+ import { readRows } from "./rows.js";
17
+ import { astToReader, RowBinaryTypeError } from "./compile.js";
18
+
19
+ /** One decoded row, keyed by column name. */
20
+ export type Row = Record<string, unknown>;
21
+
22
+ /**
23
+ * The product of compiling a `RowBinaryWithNamesAndTypes` header: the column
24
+ * metadata, the per-column readers, and — the headline — `readRows`, the
25
+ * {@link Reader} that decodes every remaining row of the stream.
26
+ */
27
+ export interface CompiledStream {
28
+ /** Column names, in stream order (from the header). */
29
+ names: string[];
30
+ /** Column type strings, in stream order (from the header). */
31
+ types: string[];
32
+ /** One folded reader per column, in stream order. */
33
+ columnReaders: Reader<unknown>[];
34
+ /** Reads exactly one row into a `{ [name]: value }` object. */
35
+ readRow: Reader<Row>;
36
+ /**
37
+ * Reads the REST of the stream (all rows after the header) into an array.
38
+ * Streaming-aware via {@link readRows}: on a partial trailing row it rewinds
39
+ * to the last complete row and returns what it has.
40
+ */
41
+ readRows: Reader<Row[]>;
42
+ }
43
+
44
+ /**
45
+ * Parse one ClickHouse type string and fold it into a {@link Reader}. Throws a
46
+ * {@link RowBinaryTypeError} if the parser rejects the string (e.g. the
47
+ * deliberately unsupported `AggregateFunction` / `SimpleAggregateFunction`) —
48
+ * carrying the `typeString` and the parse `position`.
49
+ */
50
+ export function typeStringToReader(typeStr: string): Reader<unknown> {
51
+ const result = parseDataType(typeStr);
52
+ if (!result.ok()) {
53
+ const err = result.error!;
54
+ throw new RowBinaryTypeError(
55
+ `cannot compile type ${JSON.stringify(typeStr)}: ${err.message}`,
56
+ { typeString: typeStr, position: err.position },
57
+ );
58
+ }
59
+ return astToReader(result.ast!);
60
+ }
61
+
62
+ /** Resolves a ClickHouse type string to a reader — `typeStringToReader` or a cache wrapping it. */
63
+ export type TypeReaderResolver = (typeStr: string) => Reader<unknown>;
64
+
65
+ /**
66
+ * Build an LRU-cached {@link typeStringToReader}. The full ClickHouse type
67
+ * STRING is a perfect cache key: two columns of the same type compile to the
68
+ * same reader, and a reader is stateless (it only ever touches the cursor it is
69
+ * handed), so one instance is safe to share across columns and across streams —
70
+ * a cache hit skips the parse + AST fold entirely.
71
+ *
72
+ * Worth it when you decode many `RowBinaryWithNamesAndTypes` responses whose
73
+ * schemas overlap (e.g. the same query run repeatedly): keep one cache and pass
74
+ * it to {@link compileRowBinaryWithNamesAndTypes}, so a recurring type is
75
+ * compiled once rather than once per response. A single response rarely repeats
76
+ * a type across its own columns, so the win is across calls, not within one.
77
+ *
78
+ * Classic Map-based LRU: a `Map` iterates in insertion order, so on a HIT we
79
+ * delete + re-set the entry to move it to the most-recently-used end, and on
80
+ * overflow we evict the oldest key (the first the `Map` yields). `maxSize` caps
81
+ * memory. A parse FAILURE is never cached — {@link typeStringToReader} throws
82
+ * before anything is stored — so fixing a bad type is not shadowed by a cached
83
+ * error.
84
+ */
85
+ export function createTypeReaderCache(maxSize = 256): TypeReaderResolver {
86
+ const cache = new Map<string, Reader<unknown>>();
87
+ return (typeStr) => {
88
+ const cached = cache.get(typeStr);
89
+ if (cached !== undefined) {
90
+ // Touch on hit. A Map iterates in INSERTION order, not usage order — so on
91
+ // its own `keys().next()` would give the oldest-added key, not the
92
+ // least-recently-USED one. Deleting and re-inserting moves this key to the
93
+ // tail, which is what turns insertion order INTO recency order: every
94
+ // access (hit here, or miss below) lands the key at the tail, leaving the
95
+ // head as the genuine least-recently-used entry.
96
+ cache.delete(typeStr);
97
+ cache.set(typeStr, cached);
98
+ return cached;
99
+ }
100
+ const reader = typeStringToReader(typeStr); // may throw — then nothing is cached
101
+ cache.set(typeStr, reader);
102
+ if (cache.size > maxSize) {
103
+ // The head is the least-recently-used key (see touch-on-hit above), so it
104
+ // is the correct one to evict.
105
+ const lru = cache.keys().next().value;
106
+ if (lru !== undefined) cache.delete(lru);
107
+ }
108
+ return reader;
109
+ };
110
+ }
111
+
112
+ /**
113
+ * The headline entry point. Reads the `RowBinaryWithNamesAndTypes` header off
114
+ * `state`, compiles each column type into a combinator reader, and returns the
115
+ * column metadata plus the readers — including `readRows`, the reader for the
116
+ * REST of the stream. After this call the cursor sits at the first row, so:
117
+ *
118
+ * const s = new Cursor(buf);
119
+ * const { names, readRows } = compileRowBinaryWithNamesAndTypes(s);
120
+ * const rows = readRows(s); // decode every remaining row
121
+ *
122
+ * Pass `resolveType` to reuse readers across calls — e.g. a shared
123
+ * {@link createTypeReaderCache}. It defaults to {@link typeStringToReader}
124
+ * (compile every column afresh).
125
+ */
126
+ export function compileRowBinaryWithNamesAndTypes(
127
+ state: Cursor,
128
+ resolveType: TypeReaderResolver = typeStringToReader,
129
+ ): CompiledStream {
130
+ const { names, types } = readHeader(state);
131
+ const columnReaders = types.map((t) => resolveType(t));
132
+
133
+ // Build the row reader POSITIONALLY — by column index, NOT by keying the
134
+ // readers on column name and handing them to `readTupleNamed`. The header is
135
+ // an ordered list and RowBinary has no row delimiter, so every row MUST read
136
+ // exactly these readers, in exactly this order. Keying readers by name first
137
+ // would corrupt the stream on legal-but-awkward headers:
138
+ // - duplicate column names (e.g. two `SELECT 1 AS x, 2 AS x`) collapse to a
139
+ // single entry in a `Record`, so fewer readers run than there are columns;
140
+ // - integer-like names (`0`, `1`, …) are reordered ahead of string keys by
141
+ // `Object.keys()`, so the readers would run out of header order.
142
+ // Either desyncs the cursor and misreads every subsequent row. Reading by
143
+ // index sidesteps both. The row OBJECT is still keyed by name; on a duplicate
144
+ // name the last column with that name wins in the object, but every column is
145
+ // still consumed off the wire in order, so the cursor stays in sync.
146
+ const readRow: Reader<Row> = (s) => {
147
+ const row: Row = {};
148
+ for (let i = 0; i < columnReaders.length; i++) {
149
+ row[names[i]!] = columnReaders[i]!(s);
150
+ }
151
+ return row;
152
+ };
153
+
154
+ return { names, types, columnReaders, readRow, readRows: readRows(readRow) };
155
+ }
@@ -0,0 +1,58 @@
1
+ import { NeedMoreData, type Reader } from "./core.js";
2
+
3
+ /**
4
+ * Drive `readRow` over every row of a plain `RowBinary` result into an array.
5
+ * Curried: `readRows(readRow)` returns a `Reader<T[]>`. Rows are concatenated on
6
+ * the wire with no count, length prefix, or delimiter, so the result is exhausted
7
+ * only when the cursor reaches the buffer end.
8
+ *
9
+ * `readRow` must consume EXACTLY one row's bytes — a byte short or long compounds
10
+ * across rows and the cursor overshoots or never lands on `buf.length`. Returns
11
+ * `[]` for an empty buffer. When generating code, inline the per-column reads
12
+ * into the loop body:
13
+ *
14
+ * function readRowsUser(s) {
15
+ * const out = [];
16
+ * while (s.pos < s.buf.length) {
17
+ * out.push({ id: readUInt64(s), name: readString(s) });
18
+ * }
19
+ * return out;
20
+ * }
21
+ *
22
+ * STREAMING (partial trailing row): a chunk of a still-arriving response may end
23
+ * mid-row. `pos` is committed only AFTER a row reads cleanly, so when a row
24
+ * starves and `readRow` throws {@link NeedMoreData}, this catches it, rewinds
25
+ * `pos` to the last complete row boundary, and returns the rows so far — never a
26
+ * half-built row. The cursor is left at the straddling row, a commit point the
27
+ * driver carries forward:
28
+ *
29
+ * const drive = readRows(readRow);
30
+ * let committed = 0;
31
+ * for (const chunk of chunks) { // chunk = growing prefix
32
+ * const s = new Cursor(chunk);
33
+ * s.pos = committed;
34
+ * emit(drive(s)); // complete rows in this chunk
35
+ * committed = s.pos; // start of the straddling row
36
+ * }
37
+ *
38
+ * On a complete buffer no read starves, so the catch never runs. Errors other
39
+ * than {@link NeedMoreData} are real decode faults and propagate. See also
40
+ * `streamRowBatches`, the async driver built on this.
41
+ */
42
+ export function readRows<T>(readRow: Reader<T>): Reader<T[]> {
43
+ return (state) => {
44
+ const out: T[] = [];
45
+ let committed = state.pos;
46
+ try {
47
+ while (state.pos < state.buf.length) {
48
+ const row = readRow(state);
49
+ committed = state.pos; // row read cleanly — advance the commit point
50
+ out.push(row);
51
+ }
52
+ } catch (e) {
53
+ if (e !== NeedMoreData) throw e;
54
+ state.pos = committed; // drop the partial trailing row; resume next chunk
55
+ }
56
+ return out;
57
+ };
58
+ }
@@ -0,0 +1,20 @@
1
+ import { type Reader } from "./core.js";
2
+
3
+ /**
4
+ * `SimpleAggregateFunction(func, T)` is TRANSPARENT in RowBinary: the column
5
+ * already holds a finished value of the underlying type `T` (the partial
6
+ * aggregate of a "simple" function — sum / min / max / groupArrayArray / … — is
7
+ * just a value of `T`), so it is encoded byte-for-byte the same as `T`. Decode
8
+ * the inner `T` directly.
9
+ *
10
+ * Do NOT confuse it with `AggregateFunction(func, T)`, whose value is an opaque
11
+ * serialized aggregation STATE with a function-specific binary layout — see
12
+ * `./aggregateFunction.js`.
13
+ *
14
+ * Identity combinator, documentation only:
15
+ *
16
+ * readSimpleAggregateFunction(readUInt64) === readUInt64
17
+ */
18
+ export const readSimpleAggregateFunction = <T>(
19
+ readValue: Reader<T>,
20
+ ): Reader<T> => readValue;