@clickhouse/client 1.23.0-head.fae5998.1 → 1.23.0
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/CHANGELOG.md +1342 -0
- package/README.md +18 -6
- package/dist/common/index.d.ts +2 -2
- package/dist/common/index.js +2 -2
- package/dist/common/index.js.map +1 -1
- package/dist/common/parse/column_types.d.ts +30 -2
- package/dist/common/parse/column_types.js +8 -0
- package/dist/common/parse/column_types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.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 -6
- package/skills/AGENTS.md +8 -0
- package/skills/clickhouse-js-node-rowbinary/AGENTS.md +44 -0
- package/skills/clickhouse-js-node-rowbinary/CHANGELOG.md +49 -0
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/README.md +85 -14
- package/skills/clickhouse-js-node-rowbinary/SKILL.md +111 -0
- package/skills/{clickhouse-js-node-rowbinary-parser/SKILL.md → clickhouse-js-node-rowbinary/reader.md} +59 -123
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/carts.ts +9 -5
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/events.ts +5 -5
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/iot.ts +4 -4
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/ledger.ts +3 -3
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/logs.ts +4 -4
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/observability.ts +9 -10
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/orders.ts +10 -9
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/profiles.ts +5 -5
- package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/telemetry.ts +6 -6
- package/skills/clickhouse-js-node-rowbinary/src/readers/compile.ts +328 -0
- package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/dynamic.ts +12 -8
- package/skills/clickhouse-js-node-rowbinary/src/readers/enums.ts +40 -0
- package/skills/clickhouse-js-node-rowbinary/src/readers/header.ts +29 -0
- package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/reader.ts +17 -0
- package/skills/clickhouse-js-node-rowbinary/src/readers/rowBinaryWithNamesAndTypes.ts +155 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/aggregateFunction.ts +18 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/bool.ts +10 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/composite.ts +140 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/core.ts +92 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/datetime.ts +123 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/decimals.ts +51 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/enums.ts +18 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/floats.ts +40 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/geo.ts +125 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/integers.ts +90 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/interval.ts +11 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/ip.ts +121 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/lowCardinality.ts +12 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/nested.ts +17 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/nothing.ts +21 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/rows.ts +144 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/simpleAggregateFunction.ts +12 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/strings.ts +77 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/time.ts +54 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/uuid.ts +60 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/varint.ts +64 -0
- package/skills/clickhouse-js-node-rowbinary/src/writers/writer.ts +101 -0
- package/skills/clickhouse-js-node-rowbinary/writer.md +96 -0
- package/skills/clickhouse-js-node-rowbinary-parser/src/enums.ts +0 -28
- /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/EXAMPLES.md +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/iot-rowbinary-vs-json.md +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/ledger-rowbinary-vs-json.md +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/logs-json-wins.md +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/wasm-vs-js.md +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/aggregateFunction.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/bool.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/columnar.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/composite.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/core.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/datetime.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/decimals.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/floats.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/geo.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/integers.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/interval.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/ip.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/json.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/lowCardinality.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/nested.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/nothing.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/rows.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/simpleAggregateFunction.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/stream.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/strings.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/time.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/uuid.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/varint.ts +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type Writer, Sink } from "./core.js";
|
|
2
|
+
import { type ScaledTicks, type Seconds } from "../readers/time.js";
|
|
3
|
+
import { writeInt32, writeInt64 } from "./integers.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Write a `Time`: 4-byte signed `Int32` seconds-of-day. The inverse of `readTime`;
|
|
7
|
+
* pair with {@link parseTime} to start from an "[-]HH:MM:SS" string.
|
|
8
|
+
*/
|
|
9
|
+
export function writeTime(sink: Sink, value: Seconds): void {
|
|
10
|
+
writeInt32(sink, value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Write a `Time64(P)`: 8-byte signed `Int64` count of `10^-P`-second ticks, from
|
|
15
|
+
* a {@link ScaledTicks} `[ticks, precision]`. The inverse of `readTime64`; the
|
|
16
|
+
* precision lives in the type, so only `ticks` is written. Pair with
|
|
17
|
+
* {@link parseTime64} to start from a string.
|
|
18
|
+
*/
|
|
19
|
+
export const writeTime64: Writer<ScaledTicks> = (sink, [ticks]) =>
|
|
20
|
+
writeInt64(sink, ticks);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse an "[-]HH:MM:SS" string into signed seconds-of-day — the inverse of
|
|
24
|
+
* `formatTime`. The hour field may exceed two digits (range ±999:59:59).
|
|
25
|
+
*/
|
|
26
|
+
export function parseTime(text: string): Seconds {
|
|
27
|
+
const neg = text.startsWith("-");
|
|
28
|
+
const body = neg ? text.slice(1) : text;
|
|
29
|
+
const [hh, mm, ss] = body.split(":");
|
|
30
|
+
const seconds = Number(hh) * 3600 + Number(mm) * 60 + Number(ss);
|
|
31
|
+
return neg ? -seconds : seconds;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse an "[-]HH:MM:SS[.fff]" string into a {@link ScaledTicks} at the given
|
|
36
|
+
* `precision` — the inverse of `formatTime64`. A shorter fraction is right-padded
|
|
37
|
+
* with zeros to `precision`; a longer one is truncated.
|
|
38
|
+
*/
|
|
39
|
+
export function parseTime64(text: string, precision: number): ScaledTicks {
|
|
40
|
+
const neg = text.startsWith("-");
|
|
41
|
+
const body = neg ? text.slice(1) : text;
|
|
42
|
+
const dot = body.indexOf(".");
|
|
43
|
+
const timePart = dot < 0 ? body : body.slice(0, dot);
|
|
44
|
+
const fracPart = dot < 0 ? "" : body.slice(dot + 1);
|
|
45
|
+
const [hh, mm, ss] = timePart.split(":");
|
|
46
|
+
const scale = 10n ** BigInt(precision);
|
|
47
|
+
const wholeSeconds =
|
|
48
|
+
BigInt(Number(hh) * 3600 + Number(mm) * 60 + Number(ss)) * scale;
|
|
49
|
+
const frac = BigInt(
|
|
50
|
+
(fracPart + "0".repeat(precision)).slice(0, precision) || "0",
|
|
51
|
+
);
|
|
52
|
+
const ticks = wholeSeconds + frac;
|
|
53
|
+
return [neg ? -ticks : ticks, precision];
|
|
54
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Sink, reserve } from "./core.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse a canonical `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` UUID string into the
|
|
5
|
+
* raw 16 wire bytes — the inverse of `formatUUID`. ClickHouse stores a UUID as
|
|
6
|
+
* two little-endian `UInt64` halves (high then low), so the 32 hex digits are
|
|
7
|
+
* split at the midpoint and each half written little-endian. Pair with
|
|
8
|
+
* {@link writeUUID}.
|
|
9
|
+
*/
|
|
10
|
+
export function parseUUID(text: string): Buffer {
|
|
11
|
+
const hex = text.replace(/-/g, "");
|
|
12
|
+
if (hex.length !== 32) {
|
|
13
|
+
throw new RangeError(
|
|
14
|
+
`RowBinary: invalid UUID string ${JSON.stringify(text)}`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const v = BigInt("0x" + hex);
|
|
18
|
+
// SAFE: allocUnsafe — the two writeBigUInt64LE calls below overwrite all 16
|
|
19
|
+
// bytes (offsets 0..7 and 8..15), so no uninitialized pool memory survives.
|
|
20
|
+
const b = Buffer.allocUnsafe(16);
|
|
21
|
+
b.writeBigUInt64LE(v >> 64n, 0); // high half -> first 8 bytes
|
|
22
|
+
b.writeBigUInt64LE(v & 0xffffffffffffffffn, 8); // low half -> last 8 bytes
|
|
23
|
+
return b;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Write a `UUID` from its raw 16 wire bytes (as produced by `readUUID` or
|
|
28
|
+
* {@link parseUUID}): copied verbatim. The inverse of `readUUID`. Throws unless
|
|
29
|
+
* exactly 16 bytes are given.
|
|
30
|
+
*/
|
|
31
|
+
export function writeUUID(sink: Sink, value: Uint8Array): void {
|
|
32
|
+
if (value.length !== 16) {
|
|
33
|
+
throw new RangeError(
|
|
34
|
+
`RowBinary: UUID must be 16 bytes, got ${value.length}`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const o = reserve(sink, 16);
|
|
38
|
+
sink.buf.set(value, o);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Write a `UUID` from a single 128-bit `bigint` (`hi << 64 | lo`) — the inverse
|
|
43
|
+
* of `readUUIDBigInt`. The high 64 bits go to the first little-endian `UInt64`
|
|
44
|
+
* half, the low 64 bits to the second.
|
|
45
|
+
*/
|
|
46
|
+
export function writeUUIDBigInt(sink: Sink, value: bigint): void {
|
|
47
|
+
const o = reserve(sink, 16);
|
|
48
|
+
sink.buf.writeBigUInt64LE((value >> 64n) & 0xffffffffffffffffn, o);
|
|
49
|
+
sink.buf.writeBigUInt64LE(value & 0xffffffffffffffffn, o + 8);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Write a `UUID` from its two raw little-endian `UInt64` halves `[hi, lo]` — the
|
|
54
|
+
* inverse of `readUUIDHiLo`, the faithful wire split with no combining work.
|
|
55
|
+
*/
|
|
56
|
+
export function writeUUIDHiLo(sink: Sink, [hi, lo]: [bigint, bigint]): void {
|
|
57
|
+
const o = reserve(sink, 16);
|
|
58
|
+
sink.buf.writeBigUInt64LE(hi, o);
|
|
59
|
+
sink.buf.writeBigUInt64LE(lo, o + 8);
|
|
60
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Sink, reserve } from "./core.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Write a LEB128 unsigned varint — the encode mirror of `readUVarint` (used for
|
|
5
|
+
* string/array/map lengths).
|
|
6
|
+
*
|
|
7
|
+
* Takes a JS `number`, so it is NOT bigint-friendly: the value MUST be a
|
|
8
|
+
* non-negative integer no larger than `Number.MAX_SAFE_INTEGER` (2^53 - 1). That
|
|
9
|
+
* precondition is NOT checked here — at this level the data is expected to be
|
|
10
|
+
* correct (a length the encoder itself produced), and an out-of-range value is a
|
|
11
|
+
* programming error the server will reject. If you genuinely need lengths beyond
|
|
12
|
+
* 2^53, write a bigint version with a bigint accumulator instead of widening this
|
|
13
|
+
* one.
|
|
14
|
+
*
|
|
15
|
+
* UNROLLED, mirroring `readUVarint`: branch on magnitude so the exact byte count
|
|
16
|
+
* is known up front for a single {@link reserve}, with no length-counting loop.
|
|
17
|
+
* Each byte carries 7 payload bits low-first, with the continuation bit (`+ 0x80`)
|
|
18
|
+
* set while more bits remain. `/` and `%` (never `>>>`/`&`): JS bitwise operators
|
|
19
|
+
* are 32-bit and would corrupt values past bit 31. The overwhelmingly common
|
|
20
|
+
* 1–2 byte case costs one or two compares.
|
|
21
|
+
*/
|
|
22
|
+
export function writeUVarint(sink: Sink, value: number): void {
|
|
23
|
+
if (value < 0x80) {
|
|
24
|
+
sink.buf[reserve(sink, 1)] = value;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (value < 0x4000) {
|
|
28
|
+
const o = reserve(sink, 2);
|
|
29
|
+
sink.buf[o] = (value % 128) + 0x80;
|
|
30
|
+
sink.buf[o + 1] = Math.floor(value / 128);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (value < 0x200000) {
|
|
34
|
+
const o = reserve(sink, 3);
|
|
35
|
+
sink.buf[o] = (value % 128) + 0x80;
|
|
36
|
+
sink.buf[o + 1] = (Math.floor(value / 128) % 128) + 0x80;
|
|
37
|
+
sink.buf[o + 2] = Math.floor(value / 16384);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (value < 0x10000000) {
|
|
41
|
+
const o = reserve(sink, 4);
|
|
42
|
+
sink.buf[o] = (value % 128) + 0x80;
|
|
43
|
+
sink.buf[o + 1] = (Math.floor(value / 128) % 128) + 0x80;
|
|
44
|
+
sink.buf[o + 2] = (Math.floor(value / 16384) % 128) + 0x80;
|
|
45
|
+
sink.buf[o + 3] = Math.floor(value / 2097152);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// >= 2^28 — rare for RowBinary lengths. Fall back to a short loop writing into
|
|
49
|
+
// a single span sized by a leading magnitude count (still one reserve()).
|
|
50
|
+
let size = 5;
|
|
51
|
+
for (
|
|
52
|
+
let v = Math.floor(value / 268435456);
|
|
53
|
+
v >= 0x80;
|
|
54
|
+
v = Math.floor(v / 128)
|
|
55
|
+
)
|
|
56
|
+
size++;
|
|
57
|
+
const o = reserve(sink, size);
|
|
58
|
+
let v = value;
|
|
59
|
+
for (let i = 0; i < size - 1; i++) {
|
|
60
|
+
sink.buf[o + i] = (v % 128) + 0x80;
|
|
61
|
+
v = Math.floor(v / 128);
|
|
62
|
+
}
|
|
63
|
+
sink.buf[o + size - 1] = v;
|
|
64
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel re-export of the RowBinary WRITER — the encode mirror of `reader.ts`,
|
|
3
|
+
* split by type family across parallel `*.ts` modules (the readers stay in
|
|
4
|
+
* their own files untouched). Import from here for everything in one place, or
|
|
5
|
+
* from a specific module (e.g. `./integers.js`, `./strings.js`) to
|
|
6
|
+
* pull in only the sub-writers a given result needs — the latter is what a
|
|
7
|
+
* generated encoder should do, copying just the modules its column types require.
|
|
8
|
+
*
|
|
9
|
+
* Each `writeX` is the inverse of the matching `readX`: it appends the value's
|
|
10
|
+
* RowBinary bytes to a {@link Sink} (the write-side mirror of the reader's
|
|
11
|
+
* `Cursor`). Leaf writers are `Writer<T>`s directly; combinators (e.g.
|
|
12
|
+
* `writeArray`) take sub-writers and return a `Writer`, so types compose with no
|
|
13
|
+
* per-element closures — exactly like the reader combinators.
|
|
14
|
+
*
|
|
15
|
+
* const sink = new Sink(Buffer.allocUnsafe(64));
|
|
16
|
+
* writeUInt8(sink, 255);
|
|
17
|
+
* sink.bytes(); // the encoded RowBinary
|
|
18
|
+
*
|
|
19
|
+
* - core — Sink, Writer<T>, reserve (mirror of Cursor, Reader<T>, advance)
|
|
20
|
+
* - varint — writeUVarint
|
|
21
|
+
*
|
|
22
|
+
* The dynamic AST-based encode path (the inverse of `compile.ts` /
|
|
23
|
+
* `rowBinaryWithNamesAndTypes.ts` / `dynamic.ts`) is intentionally NOT part of
|
|
24
|
+
* this barrel yet.
|
|
25
|
+
*/
|
|
26
|
+
export { Sink, reserve, BufferFull, type Writer } from "./core.js";
|
|
27
|
+
export { writeUVarint } from "./varint.js";
|
|
28
|
+
export {
|
|
29
|
+
writeUInt8,
|
|
30
|
+
writeInt8,
|
|
31
|
+
writeUInt16,
|
|
32
|
+
writeInt16,
|
|
33
|
+
writeUInt32,
|
|
34
|
+
writeInt32,
|
|
35
|
+
writeUInt64,
|
|
36
|
+
writeInt64,
|
|
37
|
+
writeUInt128,
|
|
38
|
+
writeInt128,
|
|
39
|
+
writeUInt256,
|
|
40
|
+
writeInt256,
|
|
41
|
+
} from "./integers.js";
|
|
42
|
+
export { writeBool } from "./bool.js";
|
|
43
|
+
export { writeEnum8, writeEnum16 } from "./enums.js";
|
|
44
|
+
export { writeFloat32, writeFloat64, writeBFloat16 } from "./floats.js";
|
|
45
|
+
export {
|
|
46
|
+
writeDecimal32,
|
|
47
|
+
writeDecimal64,
|
|
48
|
+
writeDecimal128,
|
|
49
|
+
writeDecimal256,
|
|
50
|
+
parseDecimal,
|
|
51
|
+
} from "./decimals.js";
|
|
52
|
+
export {
|
|
53
|
+
writeString,
|
|
54
|
+
writeStringBytes,
|
|
55
|
+
writeFixedString,
|
|
56
|
+
writeFixedStringBytes,
|
|
57
|
+
} from "./strings.js";
|
|
58
|
+
export {
|
|
59
|
+
writeUUID,
|
|
60
|
+
writeUUIDBigInt,
|
|
61
|
+
writeUUIDHiLo,
|
|
62
|
+
parseUUID,
|
|
63
|
+
} from "./uuid.js";
|
|
64
|
+
export { writeIPv4, writeIPv6, parseIPv4, parseIPv6 } from "./ip.js";
|
|
65
|
+
export {
|
|
66
|
+
writeDate,
|
|
67
|
+
writeDate32,
|
|
68
|
+
writeDateTime,
|
|
69
|
+
writeDateTime64,
|
|
70
|
+
writeDateTime64P3,
|
|
71
|
+
writeDateTime64P6,
|
|
72
|
+
writeDateTime64P9,
|
|
73
|
+
} from "./datetime.js";
|
|
74
|
+
export { writeTime, writeTime64, parseTime, parseTime64 } from "./time.js";
|
|
75
|
+
export { writeInterval } from "./interval.js";
|
|
76
|
+
export {
|
|
77
|
+
writeNullable,
|
|
78
|
+
writeArray,
|
|
79
|
+
writeQBit,
|
|
80
|
+
writeTuple,
|
|
81
|
+
writeTupleNamed,
|
|
82
|
+
writeMap,
|
|
83
|
+
writeVariant,
|
|
84
|
+
type VariantValue,
|
|
85
|
+
} from "./composite.js";
|
|
86
|
+
export { writeRows, FLUSH_CHANNEL_NAME, type WriteRowsFlush } from "./rows.js";
|
|
87
|
+
export {
|
|
88
|
+
writePoint,
|
|
89
|
+
writeRing,
|
|
90
|
+
writeLineString,
|
|
91
|
+
writePolygon,
|
|
92
|
+
writeMultiLineString,
|
|
93
|
+
writeMultiPolygon,
|
|
94
|
+
writeGeometry,
|
|
95
|
+
type GeometryValue,
|
|
96
|
+
} from "./geo.js";
|
|
97
|
+
export { writeLowCardinality } from "./lowCardinality.js";
|
|
98
|
+
export { writeSimpleAggregateFunction } from "./simpleAggregateFunction.js";
|
|
99
|
+
export { writeNested } from "./nested.js";
|
|
100
|
+
export { writeNothing } from "./nothing.js";
|
|
101
|
+
export { writeAggregateFunction } from "./aggregateFunction.js";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# RowBinary writer (encode) for Node.js
|
|
2
|
+
|
|
3
|
+
Encoding JS values into a `RowBinary` payload to send to ClickHouse. Read
|
|
4
|
+
[SKILL.md](SKILL.md) first for the format gate ("is RowBinary even the right
|
|
5
|
+
format?") and the principles that apply to **both** directions; this file covers
|
|
6
|
+
what's specific to **writing**. Reading? See [reader.md](reader.md).
|
|
7
|
+
|
|
8
|
+
Each `writeX` encodes one value, appending its RowBinary bytes to a `Sink` (a
|
|
9
|
+
caller-supplied byte buffer plus the current write offset — state only, no write
|
|
10
|
+
methods). Leaf writers (`writeUInt8`, `writeString`, …) encode directly;
|
|
11
|
+
combinators (`writeArray`, `writeTuple`, …) take sub-writers and return a writer,
|
|
12
|
+
so composite types compose with no per-element closures. For the `Sink`/`Writer<T>`
|
|
13
|
+
types and how to drain the encoded bytes, see `src/writers/core.ts`. Import the
|
|
14
|
+
barrel as `@clickhouse/rowbinary/writer`, or a per-type module for just what you
|
|
15
|
+
need. (Structurally this is the mirror of the decode side in
|
|
16
|
+
[reader.md](reader.md), but you don't need the read side to write.)
|
|
17
|
+
|
|
18
|
+
## Writer guidance
|
|
19
|
+
|
|
20
|
+
On top of the shared principles in [SKILL.md](SKILL.md), the write path has its own:
|
|
21
|
+
|
|
22
|
+
- **Reserve before you write.** Every fixed-width write goes through `reserve()`,
|
|
23
|
+
which bounds-checks against the fixed-length buffer and throws the `BufferFull`
|
|
24
|
+
sentinel when the chunk is full — your cue to flush what's written and continue
|
|
25
|
+
into a fresh buffer. Exact signature, return value, and throw contract are in
|
|
26
|
+
`src/writers/core.ts`.
|
|
27
|
+
|
|
28
|
+
- **Coalesce `reserve()` across a run of adjacent fixed-width columns.** Their
|
|
29
|
+
combined size is statically known, so reserve ONCE for the whole run and write
|
|
30
|
+
each value at a constant offset off the returned base — one bounds-check instead
|
|
31
|
+
of one per column. Only applies where every column in the run is fixed-width (a
|
|
32
|
+
variable-width writer like `writeString` reserves on its own).
|
|
33
|
+
|
|
34
|
+
- **Hoist sink state into locals in the generated writer.** `Sink.buf`/`Sink.view`
|
|
35
|
+
are `readonly`, so bind them to locals once at the top and address them directly
|
|
36
|
+
instead of through `sink.` on every write. Keep the write position (`sink.pos`)
|
|
37
|
+
in a local too — but sync it back to `sink.pos` before any `reserve()` or
|
|
38
|
+
`BufferFull` throw, since those read and mutate it to decide capacity and where a
|
|
39
|
+
flushed buffer resumes.
|
|
40
|
+
|
|
41
|
+
- **Stream the whole result with `writeRows`, not a one-shot writer.** When you
|
|
42
|
+
need to encode a large or unbounded row source, reach for `writeRows` rather than
|
|
43
|
+
a `Writer<readonly T[]>`: it owns a fixed buffer and yields it as a generator,
|
|
44
|
+
streaming the result out chunk by chunk instead of demanding it all fit at once.
|
|
45
|
+
It never leaks a half-written row (it rewinds to the last whole-row boundary
|
|
46
|
+
before flushing) and never fails on a single big row (it grows the buffer to fit).
|
|
47
|
+
It also publishes a per-flush diagnostics-channel event for buffer-utilization
|
|
48
|
+
metrics. Signature, default buffer size, channel name and payload type, the
|
|
49
|
+
growth/rewind details, and a usage example are all in `src/writers/rows.ts`.
|
|
50
|
+
|
|
51
|
+
- **No defensive validation on the hot path.** Don't add `isFinite`/`NaN`/range
|
|
52
|
+
checks to `writeX`; document the precondition in JSDoc instead. Two narrow
|
|
53
|
+
exceptions — framing-keeping checks and zero-cost parse-time helpers — are
|
|
54
|
+
spelled out in [AGENTS.md](AGENTS.md); `src/writers/ip.ts` is the worked
|
|
55
|
+
example (`writeIPv6`, `parseIPv6`).
|
|
56
|
+
|
|
57
|
+
- **Lossy time conversions floor, never round.** Every date/time writer in
|
|
58
|
+
`src/writers/datetime.ts` drops the sub-unit it can't encode by flooring toward
|
|
59
|
+
−∞, so a caller's value is never silently shifted _up_ to the wrong
|
|
60
|
+
day/second/tick and pre-1970 instants stay correct (not rounded toward the
|
|
61
|
+
epoch). See its JSDoc for the per-function specifics.
|
|
62
|
+
|
|
63
|
+
## Writer type family references
|
|
64
|
+
|
|
65
|
+
The writers live as real code under `src/writers/`, one file per type family
|
|
66
|
+
(same basenames as the readers, under the `writers/` directory).
|
|
67
|
+
|
|
68
|
+
| Value to encode (trigger) | Open |
|
|
69
|
+
| --------------------------------------------------------------------------------------- | ---------------------------------------- |
|
|
70
|
+
| **Always** — sink state, `reserve()`, `BufferFull`, `Writer<T>` | `src/writers/core.ts` |
|
|
71
|
+
| LEB128 length/count prefixes for `String`/`Array`/`Map` (`writeUVarint`) | `src/writers/varint.ts` |
|
|
72
|
+
| `Int8`–`Int256`, `UInt8`–`UInt256` | `src/writers/integers.ts` |
|
|
73
|
+
| `Bool` (`writeBool`) | `src/writers/bool.ts` |
|
|
74
|
+
| `Enum8`, `Enum16` (`writeEnum8`/`writeEnum16`; pass the raw int) | `src/writers/enums.ts` |
|
|
75
|
+
| `Float32`, `Float64`, `BFloat16` | `src/writers/floats.ts` |
|
|
76
|
+
| `Decimal32/64/128/256`, `Decimal(P, S)` (`parseDecimal`) | `src/writers/decimals.ts` |
|
|
77
|
+
| `String`, `FixedString(N)` (`writeString`/`writeStringBytes`/`writeFixedString`) | `src/writers/strings.ts` |
|
|
78
|
+
| `UUID` (`writeUUID`, `parseUUID`) | `src/writers/uuid.ts` |
|
|
79
|
+
| `IPv4`, `IPv6` (`writeIPv4`/`writeIPv6`, `parseIPv4`/`parseIPv6`) | `src/writers/ip.ts` |
|
|
80
|
+
| `Date`, `Date32`, `DateTime`, `DateTime(tz)`, `DateTime64(P[, tz])` | `src/writers/datetime.ts` |
|
|
81
|
+
| `Time`, `Time64(P)` (`parseTime`/`parseTime64`) | `src/writers/time.ts` |
|
|
82
|
+
| `IntervalNanosecond` … `IntervalYear` | `src/writers/interval.ts` |
|
|
83
|
+
| `Array(T)`, `Map(K, V)`, `Tuple(...)`, `Nullable(T)`, `Variant(...)`, `QBit(...)` | `src/writers/composite.ts` |
|
|
84
|
+
| `Point`, `Ring`, `LineString`, `MultiLineString`, `Polygon`, `MultiPolygon`, `Geometry` | `src/writers/geo.ts` |
|
|
85
|
+
| The whole result — write rows from a value source (`writeRows`) | `src/writers/rows.ts` |
|
|
86
|
+
| `LowCardinality(T)` — transparent, encode as `T` | `src/writers/lowCardinality.ts` |
|
|
87
|
+
| `SimpleAggregateFunction(f, T)` — transparent, encode as `T` | `src/writers/simpleAggregateFunction.ts` |
|
|
88
|
+
| `Nested(...)` — no wire of its own; `Array(Tuple(...))` | `src/writers/nested.ts` |
|
|
89
|
+
| `Nothing` — zero-width, never encoded (only wrapped) | `src/writers/nothing.ts` |
|
|
90
|
+
| `AggregateFunction(...)` — opaque state; produce server-side | `src/writers/aggregateFunction.ts` |
|
|
91
|
+
|
|
92
|
+
**No writer counterpart yet** — these reader paths are decode-only for now:
|
|
93
|
+
`dynamic.ts`, `json.ts`, `stream.ts`, the `RowBinaryWithNamesAndTypes`
|
|
94
|
+
header/compile/runtime path (`header.ts`, `compile.ts`,
|
|
95
|
+
`rowBinaryWithNamesAndTypes.ts`), and the columnar typed-array path
|
|
96
|
+
(`columnar.ts`). The AST-based dynamic encode path is intentionally not built.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Cursor, advance } from "./core.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Read an `Enum8`: the value's underlying signed `Int8`. The name<->value map
|
|
5
|
-
* lives in the column's type, not the bytes. Two strategies, both better than one
|
|
6
|
-
* shared name-resolving reader:
|
|
7
|
-
*
|
|
8
|
-
* - Keep the number: carry the raw Int8 and map to a name only where needed —
|
|
9
|
-
* most hot loops never need it.
|
|
10
|
-
* - Or generate a per-enum reader with a baked-in constant map, so the JIT can
|
|
11
|
-
* monomorphize each enum's decode:
|
|
12
|
-
*
|
|
13
|
-
* const STATUS = { 1: "active", 2: "closed" } as const;
|
|
14
|
-
* const readStatusEnum = (s) => STATUS[readInt8(s) as keyof typeof STATUS];
|
|
15
|
-
*/
|
|
16
|
-
export function readEnum8(state: Cursor): number {
|
|
17
|
-
return state.view.getInt8(advance(state, 1));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Read an `Enum16`: the value's underlying signed `Int16` (2 bytes). The
|
|
22
|
-
* name<->value map lives in the column's type definition, not the bytes. Prefer
|
|
23
|
-
* keeping the number, or a generated per-enum reader with a baked-in constant
|
|
24
|
-
* map so the JIT can optimize each enum's decode independently.
|
|
25
|
-
*/
|
|
26
|
-
export function readEnum16(state: Cursor): number {
|
|
27
|
-
return state.view.getInt16(advance(state, 2), true);
|
|
28
|
-
}
|
/package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/EXAMPLES.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|