@clickhouse/client 1.23.0-head.dbc2960.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/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 +78 -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} +51 -131
- 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-parser/src → clickhouse-js-node-rowbinary/src/readers}/compile.ts +18 -8
- 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/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}/header.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}/reader.ts +0 -0
- /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/rowBinaryWithNamesAndTypes.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
package/README.md
CHANGED
|
@@ -63,12 +63,12 @@ npm i @clickhouse/client-web
|
|
|
63
63
|
|
|
64
64
|
Node.js must be available in the environment to run the Node.js client. The client is compatible with all the [maintained](https://github.com/nodejs/release#readme) Node.js releases.
|
|
65
65
|
|
|
66
|
-
| Node.js version | Supported?
|
|
67
|
-
| --------------- |
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
66
|
+
| Node.js version | Supported? |
|
|
67
|
+
| --------------- | ---------- |
|
|
68
|
+
| 26.x | ✔ |
|
|
69
|
+
| 24.x | ✔ |
|
|
70
|
+
| 22.x | ✔ |
|
|
71
|
+
| 20.x | ✔ |
|
|
72
72
|
|
|
73
73
|
### TypeScript
|
|
74
74
|
|
|
@@ -110,6 +110,18 @@ See more examples in the [examples directory](./examples).
|
|
|
110
110
|
|
|
111
111
|
See the [ClickHouse website](https://clickhouse.com/docs/integrations/javascript) for the full documentation.
|
|
112
112
|
|
|
113
|
+
## Changelog
|
|
114
|
+
|
|
115
|
+
Each package keeps its own changelog:
|
|
116
|
+
|
|
117
|
+
- `@clickhouse/client` — [`packages/client-node/CHANGELOG.md`](./packages/client-node/CHANGELOG.md)
|
|
118
|
+
- `@clickhouse/client-web` — [`packages/client-web/CHANGELOG.md`](./packages/client-web/CHANGELOG.md)
|
|
119
|
+
- `@clickhouse/client-common` (deprecated) — [`packages/client-common/CHANGELOG.md`](./packages/client-common/CHANGELOG.md)
|
|
120
|
+
- `@clickhouse/datatype-parser` — [`packages/datatype-parser/CHANGELOG.md`](./packages/datatype-parser/CHANGELOG.md)
|
|
121
|
+
- `@clickhouse/rowbinary` — [`skills/clickhouse-js-node-rowbinary/CHANGELOG.md`](./skills/clickhouse-js-node-rowbinary/CHANGELOG.md)
|
|
122
|
+
|
|
123
|
+
History through `@clickhouse/client` 1.23.0 lives in the now-frozen repository-wide [`CHANGELOG.md`](./CHANGELOG.md).
|
|
124
|
+
|
|
113
125
|
## AI Agent Skills
|
|
114
126
|
|
|
115
127
|
This repository contains agent skills for working with the client:
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.23.0
|
|
1
|
+
declare const _default: "1.23.0";
|
|
2
2
|
export default _default;
|
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,QAAQ,CAAC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@clickhouse/client",
|
|
3
3
|
"description": "Official JS client for ClickHouse DB - Node.js implementation",
|
|
4
4
|
"homepage": "https://clickhouse.com",
|
|
5
|
-
"version": "1.23.0
|
|
5
|
+
"version": "1.23.0",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"clickhouse",
|
|
@@ -15,13 +15,14 @@
|
|
|
15
15
|
},
|
|
16
16
|
"private": false,
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=20"
|
|
19
19
|
},
|
|
20
20
|
"main": "dist/index.js",
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
24
|
-
"skills"
|
|
24
|
+
"skills",
|
|
25
|
+
"CHANGELOG.md"
|
|
25
26
|
],
|
|
26
27
|
"agents": {
|
|
27
28
|
"skills": [
|
|
@@ -34,14 +35,14 @@
|
|
|
34
35
|
"path": "./skills/clickhouse-js-node-troubleshooting"
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
|
-
"name": "clickhouse-js-node-rowbinary
|
|
38
|
-
"path": "./skills/clickhouse-js-node-rowbinary
|
|
38
|
+
"name": "clickhouse-js-node-rowbinary",
|
|
39
|
+
"path": "./skills/clickhouse-js-node-rowbinary"
|
|
39
40
|
}
|
|
40
41
|
]
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
44
|
"pack": "npm pack",
|
|
44
|
-
"prepack": "rm -rf skills && cp ../../README.md ../../LICENSE . && cp -r ../../skills . && RBP=skills/clickhouse-js-node-rowbinary
|
|
45
|
+
"prepack": "rm -rf skills && cp ../../README.md ../../LICENSE . && cp -r ../../skills . && RBP=skills/clickhouse-js-node-rowbinary && rm -rf $RBP/tests $RBP/node_modules $RBP/dist $RBP/package.json $RBP/package-lock.json $RBP/tsconfig.json $RBP/tsconfig.build.json $RBP/vitest.config.ts $RBP/.gitignore $RBP/LICENSE $RBP/eval_result*.md",
|
|
45
46
|
"typecheck": "tsc --noEmit",
|
|
46
47
|
"lint": "eslint --max-warnings=0 .",
|
|
47
48
|
"lint:fix": "eslint . --fix",
|
package/skills/AGENTS.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Recommendations for AI agents — `skills/`
|
|
2
|
+
|
|
3
|
+
Guidance for the shipped agent skills. See the [repo-root `AGENTS.md`](../AGENTS.md) for cross-cutting guidance. The `@clickhouse/rowbinary` skill has its own [`clickhouse-js-node-rowbinary/AGENTS.md`](clickhouse-js-node-rowbinary/AGENTS.md).
|
|
4
|
+
|
|
5
|
+
- Each shipped skill must also be listed in the `agents.skills` array of
|
|
6
|
+
[`packages/client-node/package.json`](../packages/client-node/package.json) so downstream tooling can
|
|
7
|
+
discover it. The [`Skills E2E`](../.github/workflows/e2e-skills.yml) workflow
|
|
8
|
+
(`tests/e2e/skills/check.js`) asserts that the packaged tarball contains the declared skills.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Recommendations for AI agents — `@clickhouse/rowbinary`
|
|
2
|
+
|
|
3
|
+
Guidance for the [`clickhouse-js-node-rowbinary`](.) package (the RowBinary codec library and agent skill). See the [repo-root `AGENTS.md`](../../AGENTS.md) for cross-cutting guidance.
|
|
4
|
+
|
|
5
|
+
This package has a symmetric reader/writer codebase.
|
|
6
|
+
|
|
7
|
+
## Tests
|
|
8
|
+
|
|
9
|
+
The tests follow a few conventions worth preserving:
|
|
10
|
+
|
|
11
|
+
- **Reader and writer tests are separate files.** Readers are tested in `tests/*.test.ts`; writers in
|
|
12
|
+
`tests/*.write.test.ts`. Keep the two independent: a writer test must **never** decode its bytes back
|
|
13
|
+
through a reader (and vice versa), so a bug on one side cannot mask a bug on the other. Writer tests
|
|
14
|
+
assert the encoded bytes against **live ClickHouse output** as the source of truth.
|
|
15
|
+
- **Each case is an isolated `it()` with a fully-inline body.** Write the assertion out per case, e.g.
|
|
16
|
+
`expect(encode(writer, value)).toEqual(await query("SELECT … FORMAT RowBinary"))`. Do **not** hide the
|
|
17
|
+
assertion behind a thunk-factory helper (`it("name", expectFoo(...))`), and do **not** wrap the query
|
|
18
|
+
in a per-file helper — embed the literal SQL inline, including any `SETTINGS` clause, so the full
|
|
19
|
+
query is visible in the test. The only shared helpers are the generic `query()` (`tests/clickhouse.ts`,
|
|
20
|
+
runs SQL → bytes) and `encode()` (`tests/encode.ts`, value → bytes). Repeating SQL across cases is
|
|
21
|
+
fine; reviewability beats DRY here. See [`tests/Integers.write.test.ts`](tests/Integers.write.test.ts)
|
|
22
|
+
as the canonical example.
|
|
23
|
+
|
|
24
|
+
## No defensive validation in readers/writers
|
|
25
|
+
|
|
26
|
+
These are hot-path codecs. **Do not add runtime validation of input values** (`isFinite`,
|
|
27
|
+
range/`NaN` checks, type guards, etc.) to the `readX`/`writeX` functions. The data at this level is
|
|
28
|
+
expected to be correct, and an invalid value is a programming error — document the precondition in the
|
|
29
|
+
JSDoc instead (see `writeUVarint` and the `writeDate*`/`writeDateTime` writers as the canonical
|
|
30
|
+
examples). A `Math.round`-style transform that silently shifts a _valid_ value to the wrong encoding is
|
|
31
|
+
a correctness bug and must be fixed; rejecting an _invalid_ value is not our job here.
|
|
32
|
+
|
|
33
|
+
Two narrow exceptions where a check **is** warranted:
|
|
34
|
+
|
|
35
|
+
1. **It keeps the protocol in sync.** A check belongs in only when skipping it would desync the wire
|
|
36
|
+
stream — e.g. `writeIPv6` requires exactly 16 bytes because a wrong length shifts every subsequent
|
|
37
|
+
field, and `parseIPv6` rejects malformed groups because it can't otherwise produce 16 well-defined
|
|
38
|
+
bytes. These guard the _framing_, not the user's data semantics.
|
|
39
|
+
2. **The cost is genuinely zero or it can't reach the server.** Pure parse-time helpers (string →
|
|
40
|
+
bytes, before anything is on the wire) may validate, since there's no hot loop and no server to fall
|
|
41
|
+
back on.
|
|
42
|
+
|
|
43
|
+
Otherwise, prefer letting the ClickHouse server reject bad bytes server-side over guarding client-side:
|
|
44
|
+
it already validates, and duplicating that on the encode path costs throughput for no real safety.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog — `@clickhouse/rowbinary`
|
|
2
|
+
|
|
3
|
+
> This file tracks the standalone `@clickhouse/rowbinary` package (the RowBinary
|
|
4
|
+
> parser library and agent skill). Entries relevant to it (through
|
|
5
|
+
> `@clickhouse/client` 1.23.0) were previously recorded in the now-frozen
|
|
6
|
+
> repository-wide [`CHANGELOG.md`](../../CHANGELOG.md).
|
|
7
|
+
|
|
8
|
+
# 0.2.0
|
|
9
|
+
|
|
10
|
+
## New features
|
|
11
|
+
|
|
12
|
+
- Added the RowBinary **writer** — the encode mirror of the reader: type-specific `writeX(sink, value)` building blocks and combinators that compose with no per-element closures, exported from `@clickhouse/rowbinary/writer` (or the per-type modules). ([#911])
|
|
13
|
+
|
|
14
|
+
`writeRows(writeRow)` drives an `Iterable<T>` of rows into a plain `RowBinary` payload. Note its shape: it is a **streaming generator**, not a one-shot `Writer<readonly T[]>`. It writes into a fixed-size buffer (`bufferSize`, default 64 KiB), yields each batch of whole rows when the buffer fills — rewinding so a half-written row never leaks — and starts a fresh buffer for the rest, so a result larger than the buffer (or an unbounded row source) streams out chunk by chunk. An oversized row grows the buffer (doubling) rather than failing, and per-buffer fill is published on the `@clickhouse/rowbinary:writeRows.flush` `node:diagnostics_channel` for buffer-utilization metrics. ([#915])
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import {
|
|
18
|
+
writeRows,
|
|
19
|
+
writeTupleNamed,
|
|
20
|
+
writeUInt64,
|
|
21
|
+
writeString,
|
|
22
|
+
} from "@clickhouse/rowbinary/writer";
|
|
23
|
+
|
|
24
|
+
const writeRow = writeTupleNamed({ id: writeUInt64, name: writeString });
|
|
25
|
+
for (const chunk of writeRows(writeRow)(rows, 64 * 1024)) send(chunk);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Writer edge-case hardening: `writeDate`/`writeDate32`/`writeDateTime` floor to the calendar day / whole second instead of rounding (so a non-midnight `Date` or sub-second `DateTime` no longer rounds up); `parseIPv6` rejects malformed hex groups instead of silently encoding `0`; and `writeGeometry` validates the discriminant before writing its byte, so an out-of-range value can't leave a partial payload. ([#916])
|
|
29
|
+
|
|
30
|
+
[#911]: https://github.com/ClickHouse/clickhouse-js/pull/911
|
|
31
|
+
[#915]: https://github.com/ClickHouse/clickhouse-js/pull/915
|
|
32
|
+
[#916]: https://github.com/ClickHouse/clickhouse-js/pull/916
|
|
33
|
+
|
|
34
|
+
# 0.1.2
|
|
35
|
+
|
|
36
|
+
## Improvements
|
|
37
|
+
|
|
38
|
+
- Bumped the bundled `@clickhouse/datatype-parser` dependency to `0.1.2`. ([#895])
|
|
39
|
+
- Patch release. ([#903])
|
|
40
|
+
|
|
41
|
+
# 0.1.1
|
|
42
|
+
|
|
43
|
+
## New features
|
|
44
|
+
|
|
45
|
+
- Initial release of `@clickhouse/rowbinary`: a RowBinary reader library (and the companion agent skill) shipping type-specific, monomorphizable building blocks for decoding `RowBinary` / `RowBinaryWithNames` / `RowBinaryWithNamesAndTypes` streams (full-buffer and chunked), plus a skill that guides an agent to generate bespoke high-performance parsers from a query's column types. The same library is also bundled into `@clickhouse/client` (registered in `agents.skills`). Requires Node.js `>=20`. A matching RowBinary writer is planned. ([#864])
|
|
46
|
+
|
|
47
|
+
[#864]: https://github.com/ClickHouse/clickhouse-js/pull/864
|
|
48
|
+
[#895]: https://github.com/ClickHouse/clickhouse-js/pull/895
|
|
49
|
+
[#903]: https://github.com/ClickHouse/clickhouse-js/pull/903
|
package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/README.md
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# ClickHouse Node.js RowBinary
|
|
1
|
+
# ClickHouse Node.js RowBinary Codec Generator
|
|
2
2
|
|
|
3
|
-
**If JS had a -O3 compiler flag, this skill would be it.** (for RowBinary
|
|
3
|
+
**If JS had a -O3 compiler flag, this skill would be it.** (for RowBinary read & write)
|
|
4
4
|
|
|
5
|
-
A skill and a library that lets a coding agent generate bespoke RowBinary
|
|
5
|
+
A skill and a library that lets a coding agent generate bespoke RowBinary codecs on the first pass from the column type definitions of a ClickHouse response. The [spirit](#the-spirit) behind the approach.
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Reads and writes.** Both directions are covered: readers (decode bytes → values) and writers (encode values → bytes), split under `src/readers/` and `src/writers/`. The reader path is the more mature one — the writers mirror it type-for-type, with a few decode-only paths (`Dynamic`, `JSON`, the runtime header/compile path, and the columnar typed-array path) not yet mirrored.
|
|
8
8
|
|
|
9
9
|
## Status
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ A skill and a library that lets a coding agent generate bespoke RowBinary parser
|
|
|
12
12
|
- ✅ Opus 4.8: 71% -> 94.7% pass rate
|
|
13
13
|
- ✅ Haiku 4.5: 52% -> 86.0% pass rate
|
|
14
14
|
- ✅ Composer 2.5 Fast: 3x parser performance
|
|
15
|
-
- ✅
|
|
15
|
+
- ✅ 724/724 tests (readers + writers)
|
|
16
16
|
- ✅ type-checked
|
|
17
17
|
- ✅ benchmarked
|
|
18
18
|
|
|
@@ -35,7 +35,7 @@ const readOrderRow: Reader<OrderRow> = (s) => ({
|
|
|
35
35
|
id: readUInt8(s),
|
|
36
36
|
uid: formatUUID(readUUID(s)),
|
|
37
37
|
price: readDecimal64(2)(s),
|
|
38
|
-
status:
|
|
38
|
+
status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
|
|
39
39
|
});
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -70,14 +70,74 @@ npx skills-npm setup
|
|
|
70
70
|
As a skill only:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
npx skills add ClickHouse/clickhouse-js/skills/clickhouse-js-node-rowbinary
|
|
73
|
+
npx skills add ClickHouse/clickhouse-js/skills/clickhouse-js-node-rowbinary
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
```console
|
|
77
|
-
> Hey, Claude, tell me what the rowbinary
|
|
78
|
-
> A lot! It generates custom, high-performance RowBinary
|
|
79
|
-
> Super, generate a
|
|
80
|
-
< Reading skill clickhouse-js-node-rowbinary
|
|
77
|
+
> Hey, Claude, tell me what the rowbinary skill can do for me.
|
|
78
|
+
> A lot! It generates custom, high-performance RowBinary readers and writers…
|
|
79
|
+
> Super, generate a reader for the queries in app/src/model.ts.
|
|
80
|
+
< Reading skill clickhouse-js-node-rowbinary…
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Using it with the ClickHouse JS client
|
|
84
|
+
|
|
85
|
+
This library only **decodes** the bytes — it doesn't open connections. Pair it
|
|
86
|
+
with the official client to fetch a `RowBinary` response and feed the byte chunks
|
|
87
|
+
into `streamRowBatches(chunks, readRow)`.
|
|
88
|
+
|
|
89
|
+
`RowBinary` isn't one of the formats the client decodes itself, so don't use
|
|
90
|
+
`client.query({ format: ... })` for it. Instead use `client.exec({ query })` with
|
|
91
|
+
the `FORMAT RowBinary` clause written into the SQL yourself — `exec` hands back the
|
|
92
|
+
**raw, undecoded byte stream** of the response, which is exactly what this library
|
|
93
|
+
consumes. (Use plain `RowBinary`, not `RowBinaryWithNamesAndTypes`, unless your
|
|
94
|
+
reader also skips the leading names/types header.)
|
|
95
|
+
|
|
96
|
+
The row reader below is the `orders` example from [EXAMPLES.md](EXAMPLES.md); swap
|
|
97
|
+
in the reader the skill generates for your own columns.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import {
|
|
101
|
+
type Reader,
|
|
102
|
+
readUInt8,
|
|
103
|
+
readInt8,
|
|
104
|
+
readUUID,
|
|
105
|
+
formatUUID,
|
|
106
|
+
readDecimal64,
|
|
107
|
+
type DecimalValue,
|
|
108
|
+
streamRowBatches,
|
|
109
|
+
} from "@clickhouse/rowbinary";
|
|
110
|
+
import { createClient } from "@clickhouse/client";
|
|
111
|
+
|
|
112
|
+
type OrderRow = {
|
|
113
|
+
id: number;
|
|
114
|
+
uid: string;
|
|
115
|
+
price: DecimalValue;
|
|
116
|
+
status: number;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const readOrderRow: Reader<OrderRow> = (s) => ({
|
|
120
|
+
id: readUInt8(s),
|
|
121
|
+
uid: formatUUID(readUUID(s)),
|
|
122
|
+
price: readDecimal64(2)(s),
|
|
123
|
+
status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// `exec` resolves to a Node `Stream.Readable`. It is already an
|
|
127
|
+
// `AsyncIterable<Uint8Array>` (chunks are `Buffer`/`Uint8Array`, which
|
|
128
|
+
// `streamRowBatches` normalizes), so pass `stream` straight in:
|
|
129
|
+
|
|
130
|
+
const client = createClient();
|
|
131
|
+
|
|
132
|
+
const { stream } = await client.exec({
|
|
133
|
+
query: "SELECT id, uid, price, status FROM orders FORMAT RowBinary",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
for await (const rows of streamRowBatches(stream, readOrderRow)) {
|
|
137
|
+
for (const row of rows) console.log(row); // { id, uid, price: [unscaled, scale], status }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await client.close();
|
|
81
141
|
```
|
|
82
142
|
|
|
83
143
|
## Why it's worth it
|
|
@@ -208,18 +268,22 @@ Measure, don't assume.
|
|
|
208
268
|
|
|
209
269
|
## Scope
|
|
210
270
|
|
|
211
|
-
- **In scope:** `RowBinary`, `RowBinaryWithNames`, and
|
|
271
|
+
- **In scope (reading):** `RowBinary`, `RowBinaryWithNames`, and
|
|
212
272
|
`RowBinaryWithNamesAndTypes` decoding for Node.js — full-buffer and streaming
|
|
213
273
|
(chunked) via `advance()`/`NeedMoreData`, `readRows()`, and the async
|
|
214
274
|
`streamRowBatches()` (with a built-in small-chunk warning and the optional
|
|
215
275
|
`coalesceChunks()` debounce filter).
|
|
216
|
-
- **
|
|
276
|
+
- **In scope (writing):** the inverse encode path — a `writeX` mirroring every
|
|
277
|
+
`readX`, appending bytes to a `Sink`, plus `writeRows()`. Imported from
|
|
278
|
+
`@clickhouse/rowbinary/writer`. A handful of decode-only paths are not yet
|
|
279
|
+
mirrored: `Dynamic`, `JSON`, the runtime header/compile path, and the columnar
|
|
280
|
+
typed-array path.
|
|
217
281
|
- **Out of scope (for now):** browsers and Edge runtimes, non-RowBinary formats
|
|
218
282
|
(JSON / CSV / TSV / Parquet), and big-endian hosts.
|
|
219
283
|
|
|
220
284
|
## The spirit
|
|
221
285
|
|
|
222
|
-
A RowBinary
|
|
286
|
+
A RowBinary codec generator is a narrow thing. But it's built as an instance of
|
|
223
287
|
a broader bet about what libraries become once a capable LLM is part of the
|
|
224
288
|
toolchain. Three shifts, each already visible in this repo:
|
|
225
289
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clickhouse-js-node-rowbinary
|
|
3
|
+
description: >
|
|
4
|
+
Generate TypeScript/JavaScript code that reads/decodes AND writes/encodes
|
|
5
|
+
ClickHouse RowBinary streams for the ClickHouse HTTP server.
|
|
6
|
+
Use this skill whenever a user wants to parse or produce `RowBinary`,
|
|
7
|
+
`RowBinaryWithNames`, or `RowBinaryWithNamesAndTypes`.
|
|
8
|
+
Node.js only, doesn't cover browsers.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# ClickHouse JS RowBinary Codec Generator for Node.js
|
|
12
|
+
|
|
13
|
+
This skill generates both directions of the wire format: **readers** (decode
|
|
14
|
+
bytes → values) and **writers** (encode values → bytes, the mirror). A given
|
|
15
|
+
task normally needs only one side. This file is the shared entry point — the
|
|
16
|
+
format gate plus the principles common to both directions; the per-direction
|
|
17
|
+
decisions, guidance, and the per-type reference tables live in two sibling files.
|
|
18
|
+
|
|
19
|
+
**Pick your side — read only the one you need:**
|
|
20
|
+
|
|
21
|
+
- **Decoding a `RowBinary*` response** from ClickHouse into JS values →
|
|
22
|
+
**[reader.md](reader.md)**. Streaming vs whole-buffer, row-objects vs columnar,
|
|
23
|
+
fixed vs runtime schema, and the per-type reader reference.
|
|
24
|
+
- **Encoding JS values into a `RowBinary` payload** to send to ClickHouse →
|
|
25
|
+
**[writer.md](writer.md)**. The `Sink`/`writeX` building blocks, `writeRows`
|
|
26
|
+
streaming, and the per-type writer reference.
|
|
27
|
+
|
|
28
|
+
The per-type code is real, split by direction under `src/readers/` and
|
|
29
|
+
`src/writers/`.
|
|
30
|
+
|
|
31
|
+
## First: is RowBinary even the right format?
|
|
32
|
+
|
|
33
|
+
RowBinary exists for throughput, but it is **not automatically the fastest
|
|
34
|
+
path** — match the format to the shape of the data before committing to a
|
|
35
|
+
bespoke parser.
|
|
36
|
+
|
|
37
|
+
**Prefer a `JSON*` format (e.g. `JSONEachRow`) when** the result is mostly
|
|
38
|
+
strings / JSON-like values that you consume wholesale — randomly accessing
|
|
39
|
+
essentially every field, running string/regexp methods on them, treating values
|
|
40
|
+
as text. V8's native `JSON.parse` is heavily optimized C++ and builds JS strings
|
|
41
|
+
and objects faster than a JS-level RowBinary decoder can; pair it with HTTP
|
|
42
|
+
response compression (`gzip` / `zstd`, which crushes JSON's repetitive keys) and
|
|
43
|
+
the wire cost shrinks too.
|
|
44
|
+
|
|
45
|
+
**RowBinary clearly wins when** the result is dominated by:
|
|
46
|
+
|
|
47
|
+
- **Wide numerics** — `Int128`/`Int256`/`UInt128`/`UInt256`,
|
|
48
|
+
`Decimal128`/`Decimal256`.
|
|
49
|
+
- **Binary / fixed-width blobs** — `IPv4`, `IPv6`, `UUID`, `FixedString`.
|
|
50
|
+
- **High-volume fixed-width numeric columns** generally, where each value is a
|
|
51
|
+
single `DataView` read.
|
|
52
|
+
|
|
53
|
+
**Prefer the `Native` format when** columnar load and client-side analytics are
|
|
54
|
+
the main goal (fold/scan/filter columns, feed typed arrays to a Worker or WASM).
|
|
55
|
+
`Native` is column-major, so it loads straight into one typed array per column
|
|
56
|
+
with no transpose.
|
|
57
|
+
|
|
58
|
+
For help choosing and consuming a `JSON*` format (or CSV / TSV) instead, use the
|
|
59
|
+
**`clickhouse-js-node-coding`** skill.
|
|
60
|
+
|
|
61
|
+
## Core guidance (both directions)
|
|
62
|
+
|
|
63
|
+
These principles apply whether you are generating a reader or a writer; the
|
|
64
|
+
side-specific operational guidance is in [reader.md](reader.md) /
|
|
65
|
+
[writer.md](writer.md).
|
|
66
|
+
|
|
67
|
+
- **Little-endian only.** RowBinary is little-endian; target x86/ARM. Read and
|
|
68
|
+
write every multi-byte number with `DataView` accessors passing a **literal**
|
|
69
|
+
`true` for the `littleEndian` flag.
|
|
70
|
+
|
|
71
|
+
- **Correct first, then optimize.** First emit a correct codec built from the
|
|
72
|
+
plain per-type API. Only after it's correct (and tested) specialize it. Don't
|
|
73
|
+
bake performance assumptions in before correctness.
|
|
74
|
+
|
|
75
|
+
- **Monomorphize generic/composite types.** Emit specialized, inlined code per
|
|
76
|
+
type combination instead of passing functions as arguments where the type is
|
|
77
|
+
known ahead of time.
|
|
78
|
+
|
|
79
|
+
- **Inline the leaf ops.** The per-type `readX`/`writeX` functions are the
|
|
80
|
+
correct, composable reference; the generated codec should INLINE their bodies,
|
|
81
|
+
not call them, so the row loop is straight-line with no per-field indirection
|
|
82
|
+
(and so the fixed-width coalescing can fold the offset arithmetic together).
|
|
83
|
+
|
|
84
|
+
- **Annotate the type per column.** Inlining erases the type structure, so put a
|
|
85
|
+
short comment above each column's encode/decode block naming the ClickHouse
|
|
86
|
+
type it handles.
|
|
87
|
+
|
|
88
|
+
- **Shared scratch is not reentrant.** Some hot methods reuse a module-level
|
|
89
|
+
scratch buffer as a write-then-read pair — correct only because the access is
|
|
90
|
+
fully synchronous. An `async`/`yield` boundary between populating and reading
|
|
91
|
+
it corrupts the value.
|
|
92
|
+
|
|
93
|
+
- **TypeScript by default.** Generate TypeScript code and helpers unless the user
|
|
94
|
+
explicitly asks for plain JavaScript.
|
|
95
|
+
|
|
96
|
+
## Worked examples
|
|
97
|
+
|
|
98
|
+
Six end-to-end examples with real speedup are catalogued in [EXAMPLES.md](EXAMPLES.md).
|
|
99
|
+
|
|
100
|
+
## Out of scope
|
|
101
|
+
|
|
102
|
+
- **JSON / CSV / TSV / Parquet parsing** → use `clickhouse-js-node-coding`.
|
|
103
|
+
- **Connection errors, hangs, type mismatches** → use
|
|
104
|
+
`clickhouse-js-node-troubleshooting`.
|
|
105
|
+
- **Browser / Web Worker / Edge** → `@clickhouse/client-web`.
|
|
106
|
+
|
|
107
|
+
## Still Stuck?
|
|
108
|
+
|
|
109
|
+
- [ClickHouse RowBinary format](https://clickhouse.com/docs/interfaces/formats#rowbinary)
|
|
110
|
+
- [ClickHouse data types](https://clickhouse.com/docs/sql-reference/data-types)
|
|
111
|
+
- [ClickHouse JS client docs](https://clickhouse.com/docs/integrations/javascript)
|