@clickhouse/client 1.22.0 → 1.23.0-head.c8dc8d8.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.
- package/README.md +2 -1
- package/dist/client.d.ts +2 -2
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/common/clickhouse_types.d.ts +98 -0
- package/dist/common/clickhouse_types.js +30 -0
- package/dist/common/clickhouse_types.js.map +1 -0
- package/dist/common/client.d.ts +233 -0
- package/dist/common/client.js +414 -0
- package/dist/common/client.js.map +1 -0
- package/dist/common/config.d.ts +234 -0
- package/dist/common/config.js +364 -0
- package/dist/common/config.js.map +1 -0
- package/dist/common/connection.d.ts +124 -0
- package/dist/common/connection.js +3 -0
- package/dist/common/connection.js.map +1 -0
- package/dist/common/data_formatter/format_query_params.d.ts +11 -0
- package/dist/common/data_formatter/format_query_params.js +128 -0
- package/dist/common/data_formatter/format_query_params.js.map +1 -0
- package/dist/common/data_formatter/format_query_settings.d.ts +2 -0
- package/dist/common/data_formatter/format_query_settings.js +20 -0
- package/dist/common/data_formatter/format_query_settings.js.map +1 -0
- package/dist/common/data_formatter/formatter.d.ts +41 -0
- package/dist/common/data_formatter/formatter.js +78 -0
- package/dist/common/data_formatter/formatter.js.map +1 -0
- package/dist/common/data_formatter/index.d.ts +3 -0
- package/dist/common/data_formatter/index.js +24 -0
- package/dist/common/data_formatter/index.js.map +1 -0
- package/dist/common/error/error.d.ts +20 -0
- package/dist/common/error/error.js +73 -0
- package/dist/common/error/error.js.map +1 -0
- package/dist/common/error/index.d.ts +1 -0
- package/dist/common/error/index.js +18 -0
- package/dist/common/error/index.js.map +1 -0
- package/dist/common/index.d.ts +67 -0
- package/dist/common/index.js +97 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/logger.d.ts +80 -0
- package/dist/common/logger.js +154 -0
- package/dist/common/logger.js.map +1 -0
- package/dist/common/parse/column_types.d.ts +127 -0
- package/dist/common/parse/column_types.js +586 -0
- package/dist/common/parse/column_types.js.map +1 -0
- package/dist/common/parse/index.d.ts +2 -0
- package/dist/common/parse/index.js +19 -0
- package/dist/common/parse/index.js.map +1 -0
- package/dist/common/parse/json_handling.d.ts +19 -0
- package/dist/common/parse/json_handling.js +8 -0
- package/dist/common/parse/json_handling.js.map +1 -0
- package/dist/common/result.d.ts +90 -0
- package/dist/common/result.js +3 -0
- package/dist/common/result.js.map +1 -0
- package/dist/common/settings.d.ts +1990 -0
- package/dist/common/settings.js +19 -0
- package/dist/common/settings.js.map +1 -0
- package/dist/common/tracing.d.ts +146 -0
- package/dist/common/tracing.js +76 -0
- package/dist/common/tracing.js.map +1 -0
- package/dist/common/ts_utils.d.ts +4 -0
- package/dist/common/ts_utils.js +3 -0
- package/dist/common/ts_utils.js.map +1 -0
- package/dist/common/utils/connection.d.ts +21 -0
- package/dist/common/utils/connection.js +43 -0
- package/dist/common/utils/connection.js.map +1 -0
- package/dist/common/utils/index.d.ts +5 -0
- package/dist/common/utils/index.js +22 -0
- package/dist/common/utils/index.js.map +1 -0
- package/dist/common/utils/multipart.d.ts +34 -0
- package/dist/common/utils/multipart.js +81 -0
- package/dist/common/utils/multipart.js.map +1 -0
- package/dist/common/utils/sleep.d.ts +4 -0
- package/dist/common/utils/sleep.js +12 -0
- package/dist/common/utils/sleep.js.map +1 -0
- package/dist/common/utils/stream.d.ts +15 -0
- package/dist/common/utils/stream.js +50 -0
- package/dist/common/utils/stream.js.map +1 -0
- package/dist/common/utils/url.d.ts +20 -0
- package/dist/common/utils/url.js +67 -0
- package/dist/common/utils/url.js.map +1 -0
- package/dist/common/version.d.ts +2 -0
- package/dist/common/version.js +4 -0
- package/dist/common/version.js.map +1 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/connection/compression.d.ts +2 -2
- package/dist/connection/compression.js +4 -4
- package/dist/connection/compression.js.map +1 -1
- package/dist/connection/create_connection.d.ts +1 -1
- package/dist/connection/node_base_connection.d.ts +3 -3
- package/dist/connection/node_base_connection.js +22 -22
- package/dist/connection/node_base_connection.js.map +1 -1
- package/dist/connection/node_custom_agent_connection.js +2 -2
- package/dist/connection/node_custom_agent_connection.js.map +1 -1
- package/dist/connection/node_http_connection.js +2 -2
- package/dist/connection/node_http_connection.js.map +1 -1
- package/dist/connection/node_https_connection.d.ts +1 -1
- package/dist/connection/node_https_connection.js +3 -3
- package/dist/connection/node_https_connection.js.map +1 -1
- package/dist/connection/socket_pool.d.ts +1 -1
- package/dist/connection/socket_pool.js +30 -30
- package/dist/connection/socket_pool.js.map +1 -1
- package/dist/connection/stream.d.ts +1 -1
- package/dist/connection/stream.js +9 -9
- package/dist/connection/stream.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/dist/result_set.d.ts +1 -1
- package/dist/result_set.js +10 -10
- package/dist/result_set.js.map +1 -1
- package/dist/utils/encoder.d.ts +1 -1
- package/dist/utils/encoder.js +5 -5
- package/dist/utils/encoder.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +7 -5
- package/skills/clickhouse-js-node-rowbinary-parser/EXAMPLES.md +48 -0
- package/skills/clickhouse-js-node-rowbinary-parser/README.md +248 -0
- package/skills/clickhouse-js-node-rowbinary-parser/SKILL.md +190 -0
- package/skills/clickhouse-js-node-rowbinary-parser/case-studies/iot-rowbinary-vs-json.md +83 -0
- package/skills/clickhouse-js-node-rowbinary-parser/case-studies/ledger-rowbinary-vs-json.md +103 -0
- package/skills/clickhouse-js-node-rowbinary-parser/case-studies/logs-json-wins.md +86 -0
- package/skills/clickhouse-js-node-rowbinary-parser/case-studies/wasm-vs-js.md +172 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/aggregateFunction.ts +34 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/bool.ts +10 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/columnar.ts +125 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/composite.ts +181 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/core.ts +77 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/datetime.ts +113 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/decimals.ts +57 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/dynamic.ts +328 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/enums.ts +28 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/carts.ts +71 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/events.ts +51 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/iot.ts +158 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/ledger.ts +98 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/logs.ts +73 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/observability.ts +142 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/orders.ts +65 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/profiles.ts +60 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/examples/telemetry.ts +102 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/floats.ts +32 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/geo.ts +109 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/integers.ts +95 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/interval.ts +54 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/ip.ts +93 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/json.ts +33 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/lowCardinality.ts +18 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/nested.ts +23 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/nothing.ts +29 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/reader.ts +51 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/rows.ts +58 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/simpleAggregateFunction.ts +20 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/stream.ts +276 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/strings.ts +55 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/time.ts +61 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/uuid.ts +153 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/varint.ts +70 -0
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
export * from "./core.js";
|
|
29
|
+
export * from "./varint.js";
|
|
30
|
+
export * from "./integers.js";
|
|
31
|
+
export * from "./bool.js";
|
|
32
|
+
export * from "./enums.js";
|
|
33
|
+
export * from "./floats.js";
|
|
34
|
+
export * from "./decimals.js";
|
|
35
|
+
export * from "./strings.js";
|
|
36
|
+
export * from "./uuid.js";
|
|
37
|
+
export * from "./ip.js";
|
|
38
|
+
export * from "./datetime.js";
|
|
39
|
+
export * from "./time.js";
|
|
40
|
+
export * from "./interval.js";
|
|
41
|
+
export * from "./composite.js";
|
|
42
|
+
export * from "./rows.js";
|
|
43
|
+
export * from "./geo.js";
|
|
44
|
+
export * from "./dynamic.js";
|
|
45
|
+
export * from "./json.js";
|
|
46
|
+
export * from "./stream.js";
|
|
47
|
+
export * from "./lowCardinality.js";
|
|
48
|
+
export * from "./simpleAggregateFunction.js";
|
|
49
|
+
export * from "./nested.js";
|
|
50
|
+
export * from "./nothing.js";
|
|
51
|
+
export * from "./aggregateFunction.js";
|
|
@@ -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;
|