@clickhouse/client 1.20.0-head.5423b05.1 → 1.20.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/README.md CHANGED
@@ -12,8 +12,12 @@
12
12
  <img alt="NPM Downloads" src="https://img.shields.io/npm/dw/%40clickhouse%2Fclient?color=%233178C6&logo=npm">
13
13
  </a>
14
14
 
15
- <a href="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests.yml">
16
- <img src="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests.yml/badge.svg?branch=main">
15
+ <a href="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests-node.yml">
16
+ <img src="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests-node.yml/badge.svg?branch=main">
17
+ </a>
18
+
19
+ <a href="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests-web.yml">
20
+ <img src="https://github.com/ClickHouse/clickhouse-js/actions/workflows/tests-web.yml/badge.svg?branch=main">
17
21
  </a>
18
22
 
19
23
  <a href="https://codecov.io/gh/ClickHouse/clickhouse-js">
@@ -135,3 +139,5 @@ If you have any questions or need help, feel free to reach out to us in the [Com
135
139
  ## Contributing
136
140
 
137
141
  Check out our [contributing guide](./CONTRIBUTING.md).
142
+
143
+ If you'd like to build a client for an alternative runtime (such as Bun or Cloudflare Workers) or an alternative protocol (such as the native ClickHouse protocol or gRPC over a proxy), see [Building specialized clients for alternative runtimes and protocols](./ALTERNATIVE_CLIENTS.md).
@@ -29,9 +29,13 @@ async function getAsText(stream) {
29
29
  return text;
30
30
  }
31
31
  catch (err) {
32
+ // V8 (Node.js) throws a RangeError with "Invalid string length" once a
33
+ // string grows past MAX_STRING_LENGTH; JavaScriptCore (Bun) throws a
34
+ // RangeError with "Out of memory" in the same situation.
32
35
  if (err instanceof RangeError &&
33
- err.message.includes('Invalid string length')) {
34
- throw new Error(`The response length exceeds the maximum allowed size of V8 String: ${MAX_STRING_LENGTH} characters.`, { cause: err });
36
+ (err.message.includes('Invalid string length') ||
37
+ err.message.includes('Out of memory'))) {
38
+ throw new Error(`The response length exceeds the maximum allowed size of a string: ${MAX_STRING_LENGTH} characters.`, { cause: err });
35
39
  }
36
40
  throw err;
37
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":";;;;;AAKA,4BASC;AAED,8BAyBC;AAED,8BASC;AApDD,oDAA2B;AAC3B,mCAAkC;AAElC,MAAM,EAAE,iBAAiB,EAAE,GAAG,kBAAS,CAAA;AAEvC,SAAgB,QAAQ,CAAC,GAAY;IACnC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,MAAM,IAAI,GAAG;QACb,OAAO,GAAG,CAAC,IAAI,KAAK,UAAU;QAC9B,IAAI,IAAI,GAAG;QACX,OAAO,GAAG,CAAC,EAAE,KAAK,UAAU,CAC7B,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,SAAS,CAAC,MAAuB;IACrD,IAAI,CAAC;QACH,IAAI,IAAI,GAAG,EAAE,CAAA;QAEb,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;QACrC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,CAAC;QAED,yCAAyC;QACzC,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,CAAA;QAE5B,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IACE,GAAG,YAAY,UAAU;YACzB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAC7C,CAAC;YACD,MAAM,IAAI,KAAK,CACb,sEAAsE,iBAAiB,cAAc,EACrG,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CACvB,MAAkC;IAElC,OAAO,IAAI,gBAAM,CAAC,SAAS,CAAC;QAC1B,UAAU,EAAE,IAAI;QAChB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ;YACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/B,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":";;;;;AAKA,4BASC;AAED,8BA6BC;AAED,8BASC;AAxDD,oDAA2B;AAC3B,mCAAkC;AAElC,MAAM,EAAE,iBAAiB,EAAE,GAAG,kBAAS,CAAA;AAEvC,SAAgB,QAAQ,CAAC,GAAY;IACnC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,MAAM,IAAI,GAAG;QACb,OAAO,GAAG,CAAC,IAAI,KAAK,UAAU;QAC9B,IAAI,IAAI,GAAG;QACX,OAAO,GAAG,CAAC,EAAE,KAAK,UAAU,CAC7B,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,SAAS,CAAC,MAAuB;IACrD,IAAI,CAAC;QACH,IAAI,IAAI,GAAG,EAAE,CAAA;QAEb,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;QACrC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,CAAC;QAED,yCAAyC;QACzC,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,CAAA;QAE5B,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uEAAuE;QACvE,qEAAqE;QACrE,yDAAyD;QACzD,IACE,GAAG,YAAY,UAAU;YACzB,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC;gBAC5C,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,EACxC,CAAC;YACD,MAAM,IAAI,KAAK,CACb,qEAAqE,iBAAiB,cAAc,EACpG,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CACvB,MAAkC;IAElC,OAAO,IAAI,gBAAM,CAAC,SAAS,CAAC;QAC1B,UAAU,EAAE,IAAI;QAChB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ;YACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/B,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "1.20.0-head.5423b05.1";
1
+ declare const _default: "1.20.0";
2
2
  export default _default;
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.20.0-head.5423b05.1';
3
+ exports.default = '1.20.0';
4
4
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,uBAAuB,CAAA"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,QAAQ,CAAA"}
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.20.0-head.5423b05.1",
5
+ "version": "1.20.0",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
8
8
  "clickhouse",
@@ -44,7 +44,7 @@
44
44
  "build": "rm -rf dist; tsc"
45
45
  },
46
46
  "dependencies": {
47
- "@clickhouse/client-common": "1.20.0-head.5423b05.1"
47
+ "@clickhouse/client-common": "1.20.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "simdjson": "^0.9.2"
@@ -65,6 +65,7 @@ Reference: https://clickhouse.com/docs/integrations/javascript
65
65
  - `TupleParam` and JS `Map` in `query_params`: client `>= 1.9.0`.
66
66
  - Configurable `json.parse` / `json.stringify`: client `>= 1.14.0`.
67
67
  - `Time` / `Time64` data types: ClickHouse server `>= 25.6`.
68
+ - `QBit` data type: ClickHouse server `>= 25.10` (GA on `26.x`).
68
69
  - `Dynamic` / `Variant` / new `JSON` types: ClickHouse server `>= 24.1` /
69
70
  `24.5` / `24.8` (no longer experimental since `25.3`).
70
71
 
@@ -74,19 +75,19 @@ Reference: https://clickhouse.com/docs/integrations/javascript
74
75
 
75
76
  Identify the user's task and read the matching reference file.
76
77
 
77
- | Task | Triggers / symptoms | Reference file |
78
- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------- |
79
- | **Configure / connect the client** | Building a `createClient` call, URL parameters, `clickhouse_settings`, default format, custom HTTP headers | `reference/client-configuration.md` |
80
- | **Ping the server** | Health checks, readiness probes, "is ClickHouse up?" | `reference/ping.md` |
81
- | **Choose an insert format** | "Which format should I use to insert?", JSON vs raw, `JSONEachRow` vs `JSON` vs `JSONObjectEachRow` | `reference/insert-formats.md` |
82
- | **Insert into a subset of columns / different database** | `insert({ columns })`, excluding columns, ephemeral columns, cross-DB inserts | `reference/insert-columns.md` |
83
- | **Insert values, expressions, dates, decimals** | `INSERT … VALUES` with SQL functions, `Date`/`DateTime` from JS, `Decimal` precision, `INSERT … SELECT` | `reference/insert-values.md` |
84
- | **Async inserts (server-side batching)** | `async_insert=1`, fire-and-forget vs wait-for-ack | `reference/async-insert.md` |
85
- | **Select and parse results** | `JSONEachRow` reads, `JSON` with metadata, picking a select format | `reference/select-formats.md` |
86
- | **Parameterize queries** | Binding values, special characters / escaping, "SQL injection?", `{name: Type}` syntax | `reference/query-parameters.md` |
87
- | **Sessions & temporary tables** | `session_id`, `CREATE TEMPORARY TABLE`, per-session `SET` commands | `reference/sessions.md` |
88
- | **Modern data types** | `Dynamic`, `Variant`, `JSON` (object), `Time`, `Time64` | `reference/data-types.md` |
89
- | **Custom JSON parse/stringify** | Plug in `JSONBig` / `safe-stable-stringify` / a `BigInt`-aware serializer | `reference/custom-json.md` |
78
+ | Task | Triggers / symptoms | Reference file |
79
+ | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
80
+ | **Configure / connect the client** | Building a `createClient` call, URL parameters, `clickhouse_settings`, default format, custom HTTP headers | `reference/client-configuration.md` |
81
+ | **Ping the server** | Health checks, readiness probes, "is ClickHouse up?" | `reference/ping.md` |
82
+ | **Choose an insert format** | "Which format should I use to insert?", JSON vs raw, `JSONEachRow` vs `JSON` vs `JSONObjectEachRow` | `reference/insert-formats.md` |
83
+ | **Insert into a subset of columns / different database** | `insert({ columns })`, excluding columns, ephemeral columns, cross-DB inserts | `reference/insert-columns.md` |
84
+ | **Insert values, expressions, dates, decimals** | `INSERT … VALUES` with SQL functions, `Date`/`DateTime` from JS, `Decimal` precision, `INSERT … SELECT`; inserting a UUID into a `UInt128` column is tricky — use when the user is writing code that stores a UUID as `UInt128` | `reference/insert-values.md` |
85
+ | **Async inserts (server-side batching)** | `async_insert=1`, fire-and-forget vs wait-for-ack | `reference/async-insert.md` |
86
+ | **Select and parse results** | `JSONEachRow` reads, `JSON` with metadata, picking a select format | `reference/select-formats.md` |
87
+ | **Parameterize queries** | Binding values, special characters / escaping, "SQL injection?", `{name: Type}` syntax | `reference/query-parameters.md` |
88
+ | **Sessions & temporary tables** | `session_id`, `CREATE TEMPORARY TABLE`, per-session `SET` commands | `reference/sessions.md` |
89
+ | **Modern data types** | `Dynamic`, `Variant`, `JSON` (object), `Time`, `Time64`, `QBit` (vector search) | `reference/data-types.md` |
90
+ | **Custom JSON parse/stringify** | Plug in `JSONBig` / `safe-stable-stringify` / a `BigInt`-aware serializer | `reference/custom-json.md` |
90
91
 
91
92
  ---
92
93
 
@@ -1,4 +1,4 @@
1
- # Modern Data Types: Dynamic, Variant, JSON, Time, Time64
1
+ # Modern Data Types: Dynamic, Variant, JSON, Time, Time64, QBit
2
2
 
3
3
  > **Applies to** (server side):
4
4
  >
@@ -9,6 +9,8 @@
9
9
  > you must enable the corresponding `allow_experimental_*_type` setting.
10
10
  > - `Time` / `Time64`: ClickHouse `>= 25.6` and require
11
11
  > `enable_time_time64_type: 1`.
12
+ > - `QBit`: ClickHouse `>= 25.10` (experimental, gated by
13
+ > `allow_experimental_qbit_type`); GA on `26.x`.
12
14
 
13
15
  ## Answer checklist
14
16
 
@@ -205,6 +207,109 @@ await client.insert({
205
207
  millisecond precision and will silently truncate. Store nanosecond values
206
208
  separately and provide on stringify as needed.
207
209
 
210
+ ## `QBit` (vector search)
211
+
212
+ `QBit(element_type, dimension)` stores float vectors in bit-sliced
213
+ ("transposed") form so approximate vector search can read only the most
214
+ significant bit planes at query time, trading precision for I/O and CPU.
215
+
216
+ - `element_type`: `BFloat16` | `Float32` | `Float64`.
217
+ - `dimension`: number of elements in each vector.
218
+
219
+ Introduced in ClickHouse `25.10` as an experimental type (gated by
220
+ `allow_experimental_qbit_type`) and GA on `26.x`; the setting is a no-op on
221
+ newer servers but required on `25.10`.
222
+
223
+ Internally a `QBit` column is a `Tuple(FixedString(N), ...)` of bit planes, so
224
+ the raw bytes are not valid UTF-8. JSON\* formats handle this transparently: the
225
+ server serializes the column as the original numeric array on `SELECT` and
226
+ accepts the same array shape on `INSERT`. Query the column as a vector and let
227
+ ClickHouse handle the bit-plane layout — don't feed raw `FixedString` bytes
228
+ through JSON yourself.
229
+
230
+ ```ts
231
+ import { createClient } from '@clickhouse/client'
232
+
233
+ const tableName = `chjs_qbit`
234
+ const client = createClient({
235
+ clickhouse_settings: {
236
+ // QBit introduced in ClickHouse 25.10 (experimental), GA since 26.x.
237
+ // This setting is required only on 25.10; harmless/no-op on >= 26.x.
238
+ allow_experimental_qbit_type: 1,
239
+ },
240
+ })
241
+
242
+ await client.command({
243
+ query: `
244
+ CREATE OR REPLACE TABLE ${tableName}
245
+ (
246
+ id UInt64,
247
+ vec QBit(Float32, 8)
248
+ )
249
+ ENGINE MergeTree
250
+ ORDER BY id
251
+ `,
252
+ })
253
+
254
+ // Even though QBit is stored internally as a Tuple of FixedString bit planes,
255
+ // JSON* formats accept (and return) the original Array(Float32) shape.
256
+ const values = [
257
+ { id: 1, vec: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] },
258
+ { id: 2, vec: [8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0] },
259
+ { id: 3, vec: [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5] },
260
+ ]
261
+ await client.insert({
262
+ table: tableName,
263
+ format: 'JSONEachRow',
264
+ values,
265
+ })
266
+
267
+ // Round-trip via JSONEachRow: the vec column comes back as an array of numbers.
268
+ const rs = await client.query({
269
+ query: `SELECT id, vec FROM ${tableName} ORDER BY id`,
270
+ format: 'JSONEachRow',
271
+ })
272
+ const rows = await rs.json<{ id: number; vec: number[] }>()
273
+ // vec comes back unchanged as the original Float32 array.
274
+ console.log(rows)
275
+
276
+ // Approximate vector search via L2DistanceTransposed.
277
+ // The third argument is the precision in bits: lower = less I/O, less accurate.
278
+ const search = await client.query({
279
+ query: `
280
+ SELECT id,
281
+ L2DistanceTransposed(vec, {ref:Array(Float32)}, {bits:UInt8}) AS dist
282
+ FROM ${tableName}
283
+ ORDER BY dist ASC
284
+ `,
285
+ query_params: {
286
+ ref: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
287
+ bits: 16,
288
+ },
289
+ format: 'JSONEachRow',
290
+ })
291
+ const nearest = await search.json<{ id: number; dist: number }>()
292
+ // The reference vector is exactly row #1, so it's the closest match (dist 0).
293
+ console.log(nearest)
294
+
295
+ await client.close()
296
+ ```
297
+
298
+ ### Notes
299
+
300
+ - Insert and read `QBit` columns as plain numeric arrays with `JSONEachRow`;
301
+ the server transposes them into bit planes for you.
302
+ - Use `L2DistanceTransposed(vec, ref, bits)` for approximate nearest-neighbour
303
+ search. Bind `ref` and `bits` via `query_params` (`{ref:Array(Float32)}`,
304
+ `{bits:UInt8}`) — never interpolate them into the SQL string.
305
+ - The `bits` argument is the precision in bits: lower values read fewer bit
306
+ planes (less I/O, faster, less accurate); higher values are more precise.
307
+ - The bit-plane subcolumns (`vec.N`) are `FixedString` and **not** valid
308
+ UTF-8. Selecting them directly with a JSON\* format forces the server to
309
+ escape every byte as `\uXXXX`. If you need the raw planes, use a binary
310
+ format such as `RowBinary`, or read them as hex/base64 (e.g.
311
+ `hex(vec.1)`) to keep the JSON output UTF-8 safe.
312
+
208
313
  ## Common pitfalls
209
314
 
210
315
  - **Targeting old ClickHouse servers without the `allow_experimental_*`
@@ -219,3 +324,6 @@ await client.insert({
219
324
  - **Avoid parsing Variant/Dynamic/JSON columns that mix strings and 64-bit**
220
325
  without checking their returned types first. Otherwise a number stored in
221
326
  a string will come back as a number or vice versa.
327
+ - **Selecting `QBit` bit-plane subcolumns (`vec.N`) directly in JSON formats.**
328
+ They are `FixedString` and not UTF-8; read them as hex/base64 or use a
329
+ binary format. Read the whole vector (`vec`) as a numeric array instead.
@@ -138,5 +138,4 @@ await client.insert({
138
138
  - For type guidance (`Decimal` strings, `Date` objects, `BigInt`), see
139
139
  `insert-values.md` and `custom-json.md`.
140
140
  - **Use runtime type checkers like `zod` or `io-ts` if your app ingests untrusted JSON.**
141
- It's easier to debug mismatches between your data and the format's expected shape with a validation library used at the place of ingestion than with ClickHouse errors.
142
- This is especially true in the middle of a large insert batch or streaming operation.
141
+ It's easier to debug mismatches between your data and the format's expected shape with a validation library used at the place of ingestion than with ClickHouse errors, especially in the middle of a large insert batch or streaming operation.
@@ -129,6 +129,72 @@ const rs = await client.query({
129
129
  })
130
130
  ```
131
131
 
132
+ ## Inserting a `UUID` into a `UInt128` column
133
+
134
+ ClickHouse converts a `UUID` into `UInt128` implicitly **only for the `VALUES`
135
+ clause**. With the row-oriented JSON formats the client uses (e.g.
136
+ `JSONEachRow`), sending a UUID string such as
137
+ `'019982cb-3abf-7e12-9668-c788a9e3639c'` for a `UInt128` column fails with
138
+ `CANNOT_PARSE_INPUT_ASSERTION_FAILED`. Use one of two patterns instead.
139
+
140
+ **Pattern 1 — convert the UUID on the client and send it as a decimal string**
141
+ (recommended). A JS `number` cannot hold 128 bits without precision loss, so
142
+ always pass `UInt128` as a string:
143
+
144
+ ```ts
145
+ import * as crypto from 'node:crypto'
146
+
147
+ function uuidToUInt128(uuid: string): string {
148
+ // 8-4-4-4-12 hex digits → 32 hex digits → BigInt → decimal string
149
+ return BigInt('0x' + uuid.replace(/-/g, '')).toString()
150
+ }
151
+
152
+ await client.command({
153
+ query: `
154
+ CREATE OR REPLACE TABLE events (id UInt128, description String)
155
+ ENGINE MergeTree ORDER BY id
156
+ `,
157
+ })
158
+
159
+ const uuid = crypto.randomUUID()
160
+ await client.insert({
161
+ table: 'events',
162
+ format: 'JSONEachRow',
163
+ values: [{ id: uuidToUInt128(uuid), description: 'converted on the client' }],
164
+ })
165
+ ```
166
+
167
+ `UInt128` values are also too wide for a JS `number` when reading back — cast
168
+ them with `toString(id)` in the `SELECT` to avoid precision loss.
169
+
170
+ **Pattern 2 — declare the UUID column as `EPHEMERAL`** and let ClickHouse
171
+ populate the `UInt128` column via its `DEFAULT` expression:
172
+
173
+ ```ts
174
+ await client.command({
175
+ query: `
176
+ CREATE OR REPLACE TABLE events
177
+ (
178
+ id UInt128 DEFAULT id_uuid,
179
+ id_uuid UUID EPHEMERAL,
180
+ description String
181
+ )
182
+ ENGINE MergeTree ORDER BY id
183
+ `,
184
+ })
185
+
186
+ await client.insert({
187
+ table: 'events',
188
+ format: 'JSONEachRow',
189
+ values: [{ id_uuid: uuid, description: 'populated via EPHEMERAL column' }],
190
+ // The ephemeral column must be listed so the DEFAULT on `id` is evaluated.
191
+ columns: ['id_uuid', 'description'],
192
+ })
193
+ ```
194
+
195
+ See `reference/insert-columns.md` for more on `EPHEMERAL` columns and why they
196
+ must appear in `columns`.
197
+
132
198
  ## Common pitfalls
133
199
 
134
200
  - **Using `client.insert()` for `INSERT … SELECT`.** There's nothing to
@@ -139,3 +205,7 @@ const rs = await client.query({
139
205
  - **Hand-building `VALUES` with user input.** Always parameterize user data;
140
206
  see `reference/query-parameters.md`.
141
207
  - **Using floats in the app and expect `Decimal` columns to store them safely.** Use a proper decimal library and pass them as strings to avoid precision loss.
208
+ - **Sending a UUID string for a `UInt128` column in `JSONEachRow`.** The
209
+ implicit UUID → UInt128 cast only happens in the `VALUES` clause; in JSON
210
+ formats convert the UUID to its 128-bit decimal string on the client (or use
211
+ an `EPHEMERAL` UUID column with a `UInt128` `DEFAULT`).
@@ -32,16 +32,17 @@ Reference: https://clickhouse.com/docs/integrations/javascript
32
32
 
33
33
  Identify the user's issue from the list below and read the corresponding reference file for detailed troubleshooting steps.
34
34
 
35
- | Issue | Symptoms | Reference file |
36
- | ------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------- |
37
- | **Socket Hang-Up / ECONNRESET** | `socket hang up`, `ECONNRESET`, intermittent connection drops, long-running queries timing out | `reference/socket-hangup.md` |
38
- | **Data Type Mismatches** | Large integers returned as strings, decimal precision loss, Date/DateTime insertion failures | `reference/data-types.md` |
39
- | **Read-Only User Errors** | Errors when using response compression with `readonly=1` users | `reference/readonly-users.md` |
40
- | **Proxy / Pathname URL Confusion** | Wrong database selected, requests failing behind a proxy with a path prefix | `reference/proxy-pathname.md` |
41
- | **TLS / Certificate Errors** | TLS handshake failures, certificate verification issues, mutual TLS setup | `reference/tls.md` |
42
- | **Compression Not Working** | GZIP compression not activating for requests or responses | `reference/compression.md` |
43
- | **Logging Not Showing Anything** | No log output, need custom logger integration | `reference/logging.md` |
44
- | **Query Parameters Not Interpolated** | Parameterized queries not working, SQL injection concerns | `reference/query-params.md` |
35
+ | Issue | Symptoms | Reference file |
36
+ | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
37
+ | **Socket Hang-Up / ECONNRESET** | `socket hang up`, `ECONNRESET`, intermittent connection drops, long-running queries timing out | `reference/socket-hangup.md` |
38
+ | **Data Type Mismatches** | Large integers returned as strings, decimal precision loss, Date/DateTime insertion failures, `CANNOT_PARSE_INPUT_ASSERTION_FAILED` inserting a UUID into a `UInt128` column | `reference/data-types.md` |
39
+ | **Read-Only User Errors** | Errors when using response compression with `readonly=1` users | `reference/readonly-users.md` |
40
+ | **Proxy / Pathname URL Confusion** | Wrong database selected, requests failing behind a proxy with a path prefix | `reference/proxy-pathname.md` |
41
+ | **TLS / Certificate Errors** | TLS handshake failures, certificate verification issues, mutual TLS setup | `reference/tls.md` |
42
+ | **Compression Not Working** | GZIP compression not activating for requests or responses | `reference/compression.md` |
43
+ | **Logging Not Showing Anything** | No log output, need custom logger integration | `reference/logging.md` |
44
+ | **Query Parameters Not Interpolated** | Parameterized queries not working, SQL injection concerns | `reference/query-params.md` |
45
+ | **FORMAT Clause / `SHOW POLICIES` Errors** | Syntax error from a duplicate `FORMAT`, or `SHOW [ROW] POLICIES` failing even with a format provided | `reference/query-format-clause.md` |
45
46
 
46
47
  ---
47
48
 
@@ -44,6 +44,46 @@ await client.insert({
44
44
  })
45
45
  ```
46
46
 
47
+ ## Inserting a UUID into a `UInt128` column fails (`CANNOT_PARSE_INPUT_ASSERTION_FAILED`)
48
+
49
+ > **Applies to:** all versions. This is a ClickHouse input-parsing behavior, not a client bug.
50
+
51
+ ClickHouse converts a `UUID` into `UInt128` implicitly **only for the `VALUES` clause**. With the row-oriented JSON formats the client uses (e.g. `JSONEachRow`), sending a UUID string such as `'019982cb-3abf-7e12-9668-c788a9e3639c'` for a `UInt128` column fails with `CANNOT_PARSE_INPUT_ASSERTION_FAILED`.
52
+
53
+ Fix it with one of two patterns:
54
+
55
+ **Pattern 1 — convert the UUID on the client and send it as a decimal string** (recommended). A JS `number` cannot hold 128 bits without precision loss, so always pass `UInt128` as a string:
56
+
57
+ ```js
58
+ import * as crypto from 'node:crypto'
59
+
60
+ function uuidToUInt128(uuid) {
61
+ // 8-4-4-4-12 hex digits → 32 hex digits → BigInt → decimal string
62
+ return BigInt('0x' + uuid.replace(/-/g, '')).toString()
63
+ }
64
+
65
+ const uuid = crypto.randomUUID()
66
+ await client.insert({
67
+ table: 'events',
68
+ format: 'JSONEachRow',
69
+ values: [{ id: uuidToUInt128(uuid), description: 'converted on the client' }],
70
+ })
71
+ ```
72
+
73
+ Read `UInt128` back with `toString(id)` in the `SELECT` to avoid the same precision loss.
74
+
75
+ **Pattern 2 — declare the UUID column as `EPHEMERAL`** and let ClickHouse populate the `UInt128` column via its `DEFAULT` expression. The ephemeral column must be listed in `columns` so the `DEFAULT` is evaluated:
76
+
77
+ ```js
78
+ // CREATE TABLE events (id UInt128 DEFAULT id_uuid, id_uuid UUID EPHEMERAL, description String) ...
79
+ await client.insert({
80
+ table: 'events',
81
+ format: 'JSONEachRow',
82
+ values: [{ id_uuid: uuid, description: 'populated via EPHEMERAL column' }],
83
+ columns: ['id_uuid', 'description'],
84
+ })
85
+ ```
86
+
47
87
  ## Format Selection Quick Reference
48
88
 
49
89
  | Use case | Recommended format | Min version |
@@ -0,0 +1,49 @@
1
+ # `query()` FORMAT Clause Errors (incl. `SHOW [ROW] POLICIES`)
2
+
3
+ > **Applies to:** all versions.
4
+
5
+ `client.query()` is for statements that return a result set (such as `SELECT`). It **always appends `FORMAT <format>`** to the end of the `query` string, where `<format>` comes from the `format` option (default `JSON`). You should therefore **not** include a `FORMAT` clause in the `query` yourself.
6
+
7
+ ```js
8
+ // What you write:
9
+ await client.query({ query: 'SELECT 1', format: 'JSONEachRow' })
10
+
11
+ // What the client actually sends:
12
+ // SELECT 1
13
+ // FORMAT JSONEachRow
14
+ ```
15
+
16
+ ## Duplicate `FORMAT` — syntax error
17
+
18
+ If the `query` string already contains a `FORMAT` clause, the client still appends another one, producing a duplicate `FORMAT` and a server-side syntax error. This is intended behavior.
19
+
20
+ ```js
21
+ // ❌ Wrong — ends up as `... FORMAT CSV FORMAT JSON` → syntax error
22
+ await client.query({ query: 'SELECT 1 FORMAT CSV' })
23
+
24
+ // ✓ Correct — let the client append FORMAT via the option
25
+ await client.query({ query: 'SELECT 1', format: 'CSV' })
26
+ ```
27
+
28
+ If you genuinely need to write the full SQL yourself (including the `FORMAT` clause), or you're running a statement where the appended `FORMAT` suffix is not supported, use `client.exec()` instead of `client.query()`. Use `client.insert()` for data insertion and `client.command()` for DDLs.
29
+
30
+ ## `SHOW [ROW] POLICIES` fails even with a format
31
+
32
+ Some statements are not parsed by the server with a trailing `FORMAT` clause, so the `FORMAT` suffix that `query()` always appends triggers a syntax error. The most common case is the **short** form of `SHOW POLICIES` / `SHOW ROW POLICIES` — at the server SQL parser level it does not accept the `FORMAT` suffix.
33
+
34
+ ```js
35
+ // ❌ Fails — query() appends `FORMAT JSON`, which the short SHOW POLICIES syntax rejects
36
+ await client.query({ query: 'SHOW POLICIES', format: 'JSON' })
37
+ await client.query({ query: 'SHOW ROW POLICIES', format: 'JSON' })
38
+ ```
39
+
40
+ **Fix:** use the full syntax `SHOW POLICIES ON *` (or `SHOW POLICIES ON db.table`), which the parser accepts together with the appended `FORMAT`:
41
+
42
+ ```js
43
+ // ✓ Works — full syntax accepts the appended FORMAT clause
44
+ await client.query({ query: 'SHOW POLICIES ON *', format: 'JSON' })
45
+ ```
46
+
47
+ Alternatively, run the short statement through `client.exec()`, where you control the full SQL and no `FORMAT` suffix is appended.
48
+
49
+ This behavior is easy to misdiagnose because the error looks like a generic syntax error rather than a client/format problem. See the upstream issue: https://github.com/ClickHouse/ClickHouse/issues/105899
@@ -113,7 +113,7 @@ const client = createClient({
113
113
 
114
114
  **Node.js defaults to a total received HTTP header limit of approximately 16 KB (this can be increased via the `--max-http-header-size` CLI flag[^max-header-size]).** ClickHouse sends a new progress header with each interval (~200 bytes), and after ~75 progress headers accumulate, Node.js will throw an exception and terminate the request unless that limit is raised.
115
115
 
116
- [^max-header-size]: Since `>= 1.18.5`, the ClickHouse JS client also forwards a per-request limit via the `max_response_headers_size` (bytes) option on `createClient` (Node.js only see the example below). On older versions, the practical workarounds are the `--max-http-header-size` CLI flag / `NODE_OPTIONS` (process-wide) or supplying a custom `http.Agent` configured with `maxHeaderSize`.
116
+ [^max-header-size]: Since `>= 1.18.5`, the ClickHouse JS Node client forwards a per-client header limit via the `max_response_headers_size` (bytes) option on `createClient` (it maps to Node's `http(s).request({ maxHeaderSize })`). On older versions, the practical workarounds are the `--max-http-header-size` CLI flag / `NODE_OPTIONS` (process-wide) or supplying a custom `http.Agent` configured with `maxHeaderSize`.
117
117
 
118
118
  **Maximum safe query duration formula:**
119
119
 
@@ -155,6 +155,7 @@ NODE_OPTIONS="--max-http-header-size=65536" node app.js
155
155
 
156
156
  With `maxHeaderSize = 65536` (64 KB), the formula becomes:
157
157
  Max duration (seconds) ≈ http_headers_progress_interval_ms × 300 ÷ 1000
158
+
158
159
  ```
159
160
  Max duration ≈ http_headers_progress_interval_ms ÷ 1000 × 300
160
161
  ```