From bccbdcfb97023dde804b6ad656669a9aea9bd8b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 19:20:58 +0200 Subject: [PATCH 01/23] chore: bump version to 1.18.5 (#704) Bumps version from `1.18.4` to `1.18.5` (patch bump). Co-authored-by: github-actions[bot] --- package-lock.json | 10 +++++----- packages/client-common/package.json | 2 +- packages/client-common/src/version.ts | 2 +- packages/client-node/package.json | 4 ++-- packages/client-node/src/version.ts | 2 +- packages/client-web/package.json | 4 ++-- packages/client-web/src/version.ts | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32f6720f6..549cbed80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7850,16 +7850,16 @@ }, "packages/client-common": { "name": "@clickhouse/client-common", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "devDependencies": {} }, "packages/client-node": { "name": "@clickhouse/client", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "dependencies": { - "@clickhouse/client-common": "1.18.4" + "@clickhouse/client-common": "1.18.5" }, "devDependencies": { "simdjson": "^0.9.2" @@ -7870,10 +7870,10 @@ }, "packages/client-web": { "name": "@clickhouse/client-web", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "dependencies": { - "@clickhouse/client-common": "1.18.4" + "@clickhouse/client-common": "1.18.5" } }, "vitest-poc": { diff --git a/packages/client-common/package.json b/packages/client-common/package.json index 3db9400c7..544727f3d 100644 --- a/packages/client-common/package.json +++ b/packages/client-common/package.json @@ -2,7 +2,7 @@ "name": "@clickhouse/client-common", "description": "Official JS client for ClickHouse DB - common types", "homepage": "https://clickhouse.com", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "keywords": [ "clickhouse", diff --git a/packages/client-common/src/version.ts b/packages/client-common/src/version.ts index 71fe3020b..67472f30f 100644 --- a/packages/client-common/src/version.ts +++ b/packages/client-common/src/version.ts @@ -1 +1 @@ -export default '1.18.4' +export default '1.18.5' diff --git a/packages/client-node/package.json b/packages/client-node/package.json index 27226ab9f..8e5ad68ac 100644 --- a/packages/client-node/package.json +++ b/packages/client-node/package.json @@ -2,7 +2,7 @@ "name": "@clickhouse/client", "description": "Official JS client for ClickHouse DB - Node.js implementation", "homepage": "https://clickhouse.com", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "keywords": [ "clickhouse", @@ -40,7 +40,7 @@ "build": "rm -rf dist; tsc" }, "dependencies": { - "@clickhouse/client-common": "1.18.4" + "@clickhouse/client-common": "1.18.5" }, "devDependencies": { "simdjson": "^0.9.2" diff --git a/packages/client-node/src/version.ts b/packages/client-node/src/version.ts index 71fe3020b..67472f30f 100644 --- a/packages/client-node/src/version.ts +++ b/packages/client-node/src/version.ts @@ -1 +1 @@ -export default '1.18.4' +export default '1.18.5' diff --git a/packages/client-web/package.json b/packages/client-web/package.json index d4bf6ff90..5b10a8819 100644 --- a/packages/client-web/package.json +++ b/packages/client-web/package.json @@ -2,7 +2,7 @@ "name": "@clickhouse/client-web", "description": "Official JS client for ClickHouse DB - Web API implementation", "homepage": "https://clickhouse.com", - "version": "1.18.4", + "version": "1.18.5", "license": "Apache-2.0", "keywords": [ "clickhouse", @@ -31,6 +31,6 @@ "build": "rm -rf dist; tsc" }, "dependencies": { - "@clickhouse/client-common": "1.18.4" + "@clickhouse/client-common": "1.18.5" } } diff --git a/packages/client-web/src/version.ts b/packages/client-web/src/version.ts index 71fe3020b..67472f30f 100644 --- a/packages/client-web/src/version.ts +++ b/packages/client-web/src/version.ts @@ -1 +1 @@ -export default '1.18.4' +export default '1.18.5' From d988bdc8a1a1074687349e1c0ed5fbc965ea53c6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 01:39:07 +0200 Subject: [PATCH 02/23] Replace copilot-setup-steps.yml with a .claude/skills setup skill (#705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Removes the `.github/workflows/copilot-setup-steps.yml` workflow (which slowed agent startup by pre-installing deps, building, and booting ClickHouse) and replaces it with a repo-local spec-conformant skill at `.claude/skills/setup/SKILL.md` that the agent can consult on demand when it actually needs to run tests, lints, or type checks. - **`.claude/skills/setup/SKILL.md`** — spec-conformant skill with YAML frontmatter (`name`, `description`), matching the format used by `skills/clickhouse-js-node-troubleshooting/SKILL.md`. Documents: - Node version (`>=20`, `.nvmrc` = 22, CI tests Node 20/22/24). - Installing deps in all three package roots (root workspaces + the independent `examples/node` and `examples/web` packages). - `npm run build` so workspace packages can resolve each other. - `docker compose up -d` for integration tests, with a note that unit tests don't need a server, plus how to override `CLICKHOUSE_VERSION`. - Pointers to the relevant `npm run test:*`, `lint`, `typecheck`, and example scripts. - **Deleted `.github/workflows/copilot-setup-steps.yml`** so the setup job no longer runs before every agent session. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .claude/skills/setup/SKILL.md | 74 +++++++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 60 ------------------ 2 files changed, 74 insertions(+), 60 deletions(-) create mode 100644 .claude/skills/setup/SKILL.md delete mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md new file mode 100644 index 000000000..69633c92f --- /dev/null +++ b/.claude/skills/setup/SKILL.md @@ -0,0 +1,74 @@ +--- +name: setup +description: > + Set up the `clickhouse-js` repository in a fresh checkout so the agent can run + tests, lints, type checks, builds, or examples. Use this skill before invoking + any `npm run test:*`, `npm run lint`, `npm run typecheck`, `npm run build`, or + `npm run run-examples` script — or after pulling changes that touch any + `package.json` (root, `examples/node`, or `examples/web`). Covers Node.js + version requirements, installing dependencies across the npm workspaces and + the two independent example packages, building the workspace packages so + inter-package imports resolve, and starting ClickHouse via Docker Compose for + integration tests. Do NOT use this skill for downstream user projects that + merely depend on `@clickhouse/client` or `@clickhouse/client-web`; it is + specific to contributing to the `ClickHouse/clickhouse-js` repo itself. +--- + +# clickhouse-js Repository Setup + +Use this skill before running any of the `npm run test:*`, `npm run lint`, `npm run typecheck`, or `npm run build` scripts in a fresh checkout (or after pulling changes that touch `package.json` files). + +## Prerequisites + +- **Node.js 22 recommended** (matches `.nvmrc`). The root `package.json` declares `"engines": { "node": ">=20" }`, and CI tests Node 20, 22, and 24. +- **Docker** with the Compose plugin (`docker compose ...`). Required only for integration tests and any example that talks to a real server. + +## 1. Install dependencies + +This is an npm workspaces repo (`packages/*`), with two additional independent example packages (`examples/node`, `examples/web`) that have their own `package.json` and are **not** part of the workspaces. + +Install all three: + +```bash +npm install +npm --prefix examples/node install +npm --prefix examples/web install +``` + +The root `postinstall` script patches `node_modules/parquet-wasm/package.json`; it runs automatically as part of `npm install`. + +## 2. Build the workspace packages + +The workspace packages (`@clickhouse/client-common`, `@clickhouse/client`, `@clickhouse/client-web`) must be built before some tests, examples, and typechecks can resolve their inter-package imports: + +```bash +npm run build +``` + +This runs `build` in every workspace package. + +## 3. Start ClickHouse (only for integration tests / examples) + +Unit tests do **not** need a server. Integration tests (`npm run test:*:integration*`) and the example runners do. + +From the repo root: + +```bash +docker compose up -d +``` + +This starts both the single-node setup (`clickhouse` on 8123/9000, `clickhouse_tls` on 8443/9440) and the two-node cluster (`clickhouse1`, `clickhouse2`, plus the `nginx` round-robin entrypoint on 8127). All services use non-overlapping ports so a single `up -d` covers every integration test mode. + +To override the server version, set `CLICKHOUSE_VERSION` when starting Compose; for example: `CLICKHOUSE_VERSION=head docker compose up -d`, `CLICKHOUSE_VERSION=latest docker compose up -d`, or `CLICKHOUSE_VERSION=24.8 docker compose up -d` to use an explicit version tag. + +## 4. Verify + +After the steps above you can run, for example: + +- `npm run lint` — lint every workspace package +- `npm run typecheck` — typecheck every workspace package +- `npm run test:node:unit` / `npm run test:web:unit` — unit tests, no server required +- `npm run test:node:integration` / `npm run test:web:integration` — integration tests, server required +- From `examples/node` or `examples/web`: `npm run lint`, `npm run typecheck`, `npm run run-examples` + +See `npm run` from the repo root for the full list of test scripts. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml deleted file mode 100644 index 83a5a5476..000000000 --- a/.github/workflows/copilot-setup-steps.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: 'Copilot Setup Steps' - -# Set the permissions to the lowest permissions possible needed for your steps. -# Copilot will be given its own token for its operations. -permissions: - # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. - # If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. - contents: read - -# Automatically run the setup steps when they are changed to allow for easy validation, and -# allow manual testing through the repository's "Actions" tab -on: - workflow_dispatch: - push: - paths: - - .github/workflows/copilot-setup-steps.yml - pull_request: - paths: - - .github/workflows/copilot-setup-steps.yml - -jobs: - # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. - copilot-setup-steps: - runs-on: ubuntu-latest - - # You can define any steps you want, and they will run before the agent starts. - # If you do not check out your code, Copilot will do this for you. - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup NodeJS - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: 24 - - - name: Install dependencies (Node examples) - working-directory: examples/node - run: | - npm install || echo "Failed to install dependencies in examples/node" - - - name: Install dependencies (Web examples) - working-directory: examples/web - run: | - npm install || echo "Failed to install dependencies in examples/web" - - - name: Install dependencies - run: | - npm install || echo "Failed to install dependencies" - - - name: Build packages - run: | - npm run build || echo "Failed to build packages" - - - name: Start ClickHouse - uses: isbang/compose-action@3846bcd61da338e9eaaf83e7ed0234a12b099b72 # v2.4.2 - env: - CLICKHOUSE_VERSION: 'latest' - with: - compose-file: 'docker-compose.yml' - down-flags: '--volumes' From 360dc24ba1cfb84908058bd82b51ae5993ca8dbc Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 01:46:49 +0200 Subject: [PATCH 03/23] Add `clickhouse-js-node-coding` AI agent skill (#702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Drafts a new AI-agent skill at `skills/clickhouse-js-node-coding/`, sourced from the `examples/node/coding/*.ts` corpus and scoped to day-to-day coding against `@clickhouse/client` (Node). Mirrors the structure of the existing `clickhouse-js-node-troubleshooting` skill so the two compose cleanly. - **`SKILL.md`** — frontmatter (name + trigger description), Node-only banner, How-To-Use rules (right method per task, version-constraint callouts, `JSONEachRow` default, never template-literal user input), Task Index mapping intent → reference file, and an "Out of scope" section that delegates only to the existing `clickhouse-js-node-troubleshooting` skill and routes the other topics (performance, security, schema-and-deployments, web) to the corresponding `examples/node/{performance,security,schema-and-deployments}/` and `examples/web/` directories instead of pointing at sibling skill names that don't exist in this repo. - **`reference/` (11 files)** — one per Task Index row, each opening with a version-applies banner and citing the backing example(s): - `client-configuration.md`, `ping.md`, `insert-formats.md`, `insert-columns.md`, `insert-values.md`, `async-insert.md`, `select-formats.md`, `query-parameters.md`, `sessions.md`, `data-types.md`, `custom-json.md`. - **`evals/evals.json`** — 8 starter evals using the same `{ id, prompt, expected_output, files, expectations }` shape as the troubleshooting skill, covering one prompt per major reference file (config, ping, insert, columns, parameter binding, sessions, JSON column type, custom JSON parser). In response to review feedback, the "Configuration via URL parameters" section in `reference/client-configuration.md` was tightened to spell out the fixed allowlist (plus the `clickhouse_setting_` / `ch_` / `http_header_` prefixes) and to call out the `Unknown URL parameters: ...` throw, with a citation to `packages/client-common/src/config.ts`. The equivalent config-object example in the same file was corrected to import `ClickHouseLogLevel` from `@clickhouse/client` and use `log: { level: ClickHouseLogLevel.TRACE }` instead of the invalid top-level `log_level: 'TRACE'`, with a note clarifying the URL-parameter-to-config-object mapping for log levels. Additionally, `reference/select-formats.md` was updated so `ResultSet.close()` is documented as synchronous (no `await`) in both the methods table row and the "stream half-consumed" common-pitfall note, matching the actual `close(): void` API. Following further review, every remaining reference to the non-existent `clickhouse-js-node-performance` sibling skill was replaced with a link to the in-repo [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance) directory across `SKILL.md`, `reference/async-insert.md`, `reference/insert-formats.md`, and `reference/select-formats.md` (intro, methods table, and Parquet-related notes), so the skill no longer points at unavailable skill identifiers. The "Session-level `SET` commands" snippet in `reference/sessions.md` was also made self-contained by adding the `createClient` and `node:crypto` imports, setting `max_open_connections: 1` alongside `session_id` (consistent with the per-client concurrency guidance earlier in the same doc), and ending with `await client.close()`. No runtime code or build config is touched. The `client-node` `prepack` script already copies the repo-root `skills/` directory into the published package, so the new skill ships to npm without further changes. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- skills/clickhouse-js-node-coding/SKILL.md | 146 +++++++++++++++ .../evals/evals.json | 103 +++++++++++ .../reference/async-insert.md | 103 +++++++++++ .../reference/client-configuration.md | 159 ++++++++++++++++ .../reference/custom-json.md | 149 +++++++++++++++ .../reference/data-types.md | 169 ++++++++++++++++++ .../reference/insert-columns.md | 113 ++++++++++++ .../reference/insert-formats.md | 145 +++++++++++++++ .../reference/insert-values.md | 141 +++++++++++++++ .../reference/ping.md | 120 +++++++++++++ .../reference/query-parameters.md | 152 ++++++++++++++++ .../reference/select-formats.md | 111 ++++++++++++ .../reference/sessions.md | 152 ++++++++++++++++ 13 files changed, 1763 insertions(+) create mode 100644 skills/clickhouse-js-node-coding/SKILL.md create mode 100644 skills/clickhouse-js-node-coding/evals/evals.json create mode 100644 skills/clickhouse-js-node-coding/reference/async-insert.md create mode 100644 skills/clickhouse-js-node-coding/reference/client-configuration.md create mode 100644 skills/clickhouse-js-node-coding/reference/custom-json.md create mode 100644 skills/clickhouse-js-node-coding/reference/data-types.md create mode 100644 skills/clickhouse-js-node-coding/reference/insert-columns.md create mode 100644 skills/clickhouse-js-node-coding/reference/insert-formats.md create mode 100644 skills/clickhouse-js-node-coding/reference/insert-values.md create mode 100644 skills/clickhouse-js-node-coding/reference/ping.md create mode 100644 skills/clickhouse-js-node-coding/reference/query-parameters.md create mode 100644 skills/clickhouse-js-node-coding/reference/select-formats.md create mode 100644 skills/clickhouse-js-node-coding/reference/sessions.md diff --git a/skills/clickhouse-js-node-coding/SKILL.md b/skills/clickhouse-js-node-coding/SKILL.md new file mode 100644 index 000000000..265039768 --- /dev/null +++ b/skills/clickhouse-js-node-coding/SKILL.md @@ -0,0 +1,146 @@ +--- +name: clickhouse-js-node-coding +description: > + Write idiomatic application code with the ClickHouse Node.js client + (`@clickhouse/client`). Use this skill whenever a user is *building* against + the Node.js client — configuring the client, pinging, inserting rows in JSON + or raw formats, selecting and parsing results, binding query parameters, + managing sessions and temporary tables, working with data types like + `Date`/`DateTime`/`Decimal`/`Time`/`Time64`/`Dynamic`/`Variant`/`JSON`, or + customizing JSON parsing. Trigger on phrases like "how do I insert…", "how + do I select…", "what format should I use…", "how do I parameterize…", "how + do I configure the client…". Do NOT use for browser/Web client code, for + performance/streaming/Parquet questions (see `examples/node/performance/`), + or for diagnosing errors and unexpected behavior (see + clickhouse-js-node-troubleshooting). +--- + +# ClickHouse Node.js Client — Coding + +Reference: https://clickhouse.com/docs/integrations/javascript + +> **⚠️ Node.js runtime only.** This skill covers the `@clickhouse/client` +> package running in a **Node.js runtime** exclusively — including **Next.js +> Node runtime** API routes, React Server Components, Server Actions, and +> standard Node.js processes. Do **not** apply this skill to browser client +> components, Web Workers, **Next.js Edge runtime**, Cloudflare Workers, or +> any usage of `@clickhouse/client-web`. For browser/edge environments, the +> correct package is `@clickhouse/client-web`. + +--- + +## How to Use This Skill + +1. **Match the user's intent** to a row in the Task Index below and read the + corresponding reference file before writing code. After reading it, scan any + **Answer checklist** in that reference and make sure the final answer covers + each relevant item; those checklists capture details users usually need but + are easy to omit in short answers. +2. **Always import from `@clickhouse/client`** (never `@clickhouse/client-web`) + and create a single client with `createClient({ url })` or rely on + supported defaults when appropriate. Close it with `await client.close()` + during graceful shutdown. +3. **Prefer `JSONEachRow` for typical row inserts/selects** unless the user + has already chosen another format or is streaming raw bytes (CSV / TSV / + Parquet — see `examples/node/performance/`). + **Note on `clickhouse_settings`:** settings passed to `createClient` are + defaults for every request; they can be overridden per-call by passing + `clickhouse_settings` directly to `insert()`, `query()`, or `command()`. + Always mention this when the user configures settings at the client level. +4. **Always use `query_params` for user-supplied values** — never template- + literal-interpolate them into SQL. See `reference/query-parameters.md`. +5. **Pick the right method for the job:** + - `client.insert()` — write rows. + - `client.query()` + `resultSet.json()` / `.text()` / `.stream()` — read + rows that return data. + - `client.command()` — DDL and other statements that don't return rows + (`CREATE`, `DROP`, `TRUNCATE`, `ALTER`, `SET` in a session, etc.). + - `client.exec()` — when you need the raw response stream of an arbitrary + statement (rare in coding scenarios). + - `client.ping()` — health check; returns `{ success, error? }`, never + throws on connection failure. +6. **Note version constraints** when relevant. Examples: + - `pathname` config option: client `>= 1.0.0`. + - `BigInt` values in `query_params`: client `>= 1.15.0`. + - `TupleParam` and JS `Map` in `query_params`: client `>= 1.9.0`. + - Configurable `json.parse` / `json.stringify`: client `>= 1.14.0`. + - `Time` / `Time64` data types: ClickHouse server `>= 25.6`. + - `Dynamic` / `Variant` / new `JSON` types: ClickHouse server `>= 24.1` / + `24.5` / `24.8` (no longer experimental since `25.3`). +7. **Show a runnable snippet**, not pseudo-code. The examples in + [`examples/node/coding/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/coding) + are all self-contained and runnable against the repo's `docker-compose up` + setup — pattern your snippet after them. + +--- + +## Task Index + +Identify the user's task and read the matching reference file. + +| Task | Triggers / symptoms | Reference file | +| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| **Configure / connect the client** | Building a `createClient` call, URL parameters, `clickhouse_settings`, default format, custom HTTP headers | `reference/client-configuration.md` | +| **Ping the server** | Health checks, readiness probes, "is ClickHouse up?" | `reference/ping.md` | +| **Choose an insert format** | "Which format should I use to insert?", JSON vs raw, `JSONEachRow` vs `JSON` vs `JSONObjectEachRow` | `reference/insert-formats.md` | +| **Insert into a subset of columns / different database** | `insert({ columns })`, excluding columns, ephemeral columns, cross-DB inserts | `reference/insert-columns.md` | +| **Insert values, expressions, dates, decimals** | `INSERT … VALUES` with SQL functions, `Date`/`DateTime` from JS, `Decimal` precision, `INSERT … SELECT` | `reference/insert-values.md` | +| **Async inserts (server-side batching)** | `async_insert=1`, fire-and-forget vs wait-for-ack | `reference/async-insert.md` | +| **Select and parse results** | `JSONEachRow` reads, `JSON` with metadata, picking a select format | `reference/select-formats.md` | +| **Parameterize queries** | Binding values, special characters / escaping, "SQL injection?", `{name: Type}` syntax | `reference/query-parameters.md` | +| **Sessions & temporary tables** | `session_id`, `CREATE TEMPORARY TABLE`, per-session `SET` commands | `reference/sessions.md` | +| **Modern data types** | `Dynamic`, `Variant`, `JSON` (object), `Time`, `Time64` | `reference/data-types.md` | +| **Custom JSON parse/stringify** | Plug in `JSONBig` / `safe-stable-stringify` / a `BigInt`-aware serializer | `reference/custom-json.md` | + +--- + +## Conventions used in answers + +- Always show `import { createClient } from '@clickhouse/client'` (Node, never + Web). For things that require a runtime API, prefer `node:` built-ins + (e.g., `import * as crypto from 'node:crypto'`). +- Always `await client.close()` at the end of self-contained snippets; in + long-running services, close on graceful shutdown. +- Prefer top-level `await` in snippets to match the style of + `examples/node/coding/*.ts`. +- For inserts, prefer `format: 'JSONEachRow'` and `values: [...]` unless the + user's scenario requires otherwise. +- For selects, prefer `await (await client.query({...})).json()` for + small / medium result sets; for streaming, see `examples/node/performance/`. +- When showing parameter binding, use ClickHouse's native `{name: Type}` + syntax — never `$1`, `?`, or `:name`. +- For DDL inside a cluster or behind a load balancer, set + `clickhouse_settings: { wait_end_of_query: 1 }` on the `command()` call so + the server only acknowledges after the change is applied. See + https://clickhouse.com/docs/en/interfaces/http/#response-buffering. + +--- + +## Out of scope + +This skill covers day-to-day coding against `@clickhouse/client` (Node). +The following topics are intentionally **not** covered here: + +- **Errors, hangs, type mismatches, proxy pathname surprises, log silence, + socket hang-ups, `ECONNRESET`** → use the + `clickhouse-js-node-troubleshooting` skill. +- **Streaming, Parquet, file streams, server-side bulk moves, progress + streaming, async-insert throughput tuning** — see + [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance). +- **TLS, RBAC / read-only users, deeper SQL-injection guidance** — see + [`examples/node/security/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/security). +- **`CREATE TABLE` patterns, deployment-shaped connection strings, + replication / sharding choices** — see + [`examples/node/schema-and-deployments/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/schema-and-deployments). +- **Browser, Web Worker, Next.js Edge, Cloudflare Workers** — use + `@clickhouse/client-web` and see + [`examples/web/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/web). + +--- + +## Still Stuck? + +- [`examples/node/coding/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/coding) — the runnable corpus this skill is built on. +- [ClickHouse JS client docs](https://clickhouse.com/docs/integrations/javascript) +- [ClickHouse supported formats](https://clickhouse.com/docs/interfaces/formats) +- [ClickHouse data types](https://clickhouse.com/docs/sql-reference/data-types) diff --git a/skills/clickhouse-js-node-coding/evals/evals.json b/skills/clickhouse-js-node-coding/evals/evals.json new file mode 100644 index 000000000..50cf85025 --- /dev/null +++ b/skills/clickhouse-js-node-coding/evals/evals.json @@ -0,0 +1,103 @@ +{ + "skill_name": "clickhouse-js-node-coding", + "evals": [ + { + "id": 0, + "prompt": "I'm setting up clickhouse client in a Node service. I want to point it at https://my.host:8124, use database 'analytics', user 'bob' / password 'secret', set application name 'my_app', and turn on async_insert without waiting for ack. What's the cleanest way to express that?", + "expected_output": "A createClient call (Node, not Web) that sets url, username/password (or embeds them in the URL), database, application, and clickhouse_settings: { async_insert: 1, wait_for_async_insert: 0 }. Optionally mentions the equivalent URL-parameter form (ch_async_insert=1&ch_wait_for_async_insert=0) and that URL params override the config object.", + "files": [], + "expectations": [ + "Uses createClient from @clickhouse/client (Node, not Web).", + "Either passes a single URL string with auth + ?ch_async_insert=1&ch_wait_for_async_insert=0, or a config object with database, username, password, application, clickhouse_settings.", + "Mentions or implies that URL parameters override the config object if both are provided.", + "Does not suggest using URL parameters in code and instead suggests that the URL should be read from environment variables or a config file.", + "Does not construct the URL with parameters directly in code neither using string concatenation nor query objects.", + "Suggests using `await client.close()` during graceful shutdown.", + "Suggests that settings in `clickhouse_settings` can be overridden per-query by passing them inside the individual `insert()` or `query()` call for finer control." + ] + }, + { + "id": 1, + "prompt": "How do I do a health check against ClickHouse from Node? I want to return 200/503 from an Express endpoint based on whether ClickHouse is reachable.", + "expected_output": "Use await client.ping(), branch on { success } (no try/catch needed) — return 200 on success and 503 on failure, optionally surfacing the error.", + "files": [], + "expectations": [ + "Uses await client.ping() and reads { success, error } directly — does NOT wrap it in try/catch as the only check.", + "Maps success === true to 200 and success === false to 503.", + "Suggests lowering request_timeout to make the probe fail fast.", + "Explains the difference between `client.ping()` (checks connectivity only and ignores credentials by default) and `client.ping({ select: true })` (lightweight query that also checks auth and query processing) and when to use each." + ] + }, + { + "id": 2, + "prompt": "I have an array of about 10k plain JS objects I want to insert into a MergeTree table. What's the right format and call?", + "expected_output": "client.insert with format: 'JSONEachRow' and values: . No streaming / Parquet needed for this size.", + "files": [], + "expectations": [ + "Uses client.insert({ table, values, format: 'JSONEachRow' }).", + "Notes that the array can be passed directly to values — no need to stringify or stream for a few thousand rows.", + "Does NOT recommend a streaming/Parquet flow for this size.", + "Mentions `JSONCompact*` formats as an alternative for bigger payloads" + ] + }, + { + "id": 3, + "prompt": "My table has columns (id, name, created_at, internal_hash) but the rows I have only contain id and name. How do I insert just those two columns?", + "expected_output": "Use the columns option on client.insert: either columns: ['id', 'name'] (allowlist) or columns: { except: ['created_at', 'internal_hash'] } (excludelist). Omitted columns get their declared defaults.", + "files": [], + "expectations": [ + "Uses client.insert with the columns: ['id', 'name'] option.", + "Mentions the alternative columns: { except: [...] } form.", + "Notes that omitted columns will receive their server-side defaults." + ] + }, + { + "id": 4, + "prompt": "I want to call: SELECT * FROM users WHERE country = '' AND signup_date > ''. How should I pass those values from JS?", + "expected_output": "Use parameterized queries with the ClickHouse {name: Type} syntax and query_params: { country: ..., signup_date: ... }. Explicitly warn against template-literal interpolation (SQL injection).", + "files": [], + "expectations": [ + "Uses {name: Type} parameter syntax (e.g., {country: String}, {signup_date: Date}) and query_params.", + "Explicitly warns against template-literal interpolation as a SQL injection risk.", + "Does NOT suggest $1/?/:name placeholders." + ] + }, + { + "id": 5, + "prompt": "I'm doing CREATE TEMPORARY TABLE and then SELECT from it in a follow-up call. They keep disappearing between calls. What am I missing?", + "expected_output": "Temporary tables are scoped to a session — set a stable session_id (e.g., crypto.randomUUID()) on the client (or per-call) so consecutive requests share server-side state. Also flag the load-balancer/Cloud caveat (replica-aware routing).", + "files": [], + "expectations": [ + "Explains that temporary tables are scoped to a session and require a stable session_id across calls.", + "Shows setting session_id either on createClient or via per-call session_id.", + "Mentions the load-balancer / ClickHouse Cloud caveat (sessions are pinned to a node; recommend replica-aware routing or a single-node connection).", + "Explicitly explains that parallel calls with the same session_id will result in an error as ClickHouse does not allow concurrent queries within the same session_id.", + "Explicitly advises against using session_id in client configuration for a global / module static client", + "When session_id is used as a client option it should suggest configuring the maximum number of connections to 1 to minimize concurrency issues at the client level." + ] + }, + { + "id": 6, + "prompt": "I'm running 25.x ClickHouse. I want to store a JSON object per row and read it back as a real JS object, not a JSON string. How do I do that with the Node client?", + "expected_output": "Use the new JSON column type (>= 24.8, no longer experimental since 25.3). CREATE TABLE with a JSON column, insert with format: 'JSONEachRow' passing JS objects, select with JSONEachRow — values come back as parsed JS objects with no manual JSON.parse.", + "files": [], + "expectations": [ + "Uses the JSON column type and format: 'JSONEachRow' for both insert and select.", + "Inserts a real JS object as the column value (no JSON.stringify) and shows it returns as a parsed object.", + "Mentions the relevant ClickHouse server version (>= 24.8 introduced; non-experimental since 25.3) and, if needed on older servers, allow_experimental_json_type." + ] + }, + { + "id": 7, + "prompt": "Our IDs are UInt64 and we don't want them coming back as strings or losing precision.", + "expected_output": "Yes — pass a custom { parse, stringify } via the json config option (>= 1.14.0). Show wiring up json-bigint (or similar) so 64-bit integers are parsed as BigInt. Mention output_format_json_quote_64bit_integers: 0 so the server emits unquoted ints. Note that switching to native Number would lose precision and is the wrong fix.", + "files": [], + "expectations": [ + "Shows passing custom { parse, stringify } via the json config option on createClient.", + "Notes the >= 1.14.0 client requirement for the json option.", + "Mentions output_format_json_quote_64bit_integers: 0 (default since 25.8) so 64-bit integers come back unquoted and parseable as BigInt.", + "Warns that switching to native Number would lose precision and is the wrong fix." + ] + } + ] +} diff --git a/skills/clickhouse-js-node-coding/reference/async-insert.md b/skills/clickhouse-js-node-coding/reference/async-insert.md new file mode 100644 index 000000000..09c32ffbf --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/async-insert.md @@ -0,0 +1,103 @@ +# Async Inserts + +> **Applies to:** all client versions; the relevant settings are server-side. +> See https://clickhouse.com/docs/en/optimize/asynchronous-inserts. + +Backing example: +[`examples/node/coding/async_insert.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/async_insert.ts). + +> **When to use async inserts:** when many small inserts arrive concurrently +> (e.g., one per HTTP request) and you don't want to maintain a client-side +> batching layer. ClickHouse will batch them server-side. This is also the +> recommended ingestion pattern for **ClickHouse Cloud**. + +> **When _not_ to use async inserts:** when you already build large batches +> client-side (e.g., from a stream). Plain inserts are simpler and lower +> latency. For raw throughput tuning of large async-insert workloads, see +> [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance). + +## Setup + +Enable on the client (or per-request) via `clickhouse_settings`: + +```ts +import { createClient, ClickHouseError } from '@clickhouse/client' + +const client = createClient({ + url: process.env.CLICKHOUSE_URL, + password: process.env.CLICKHOUSE_PASSWORD, + max_open_connections: 10, + clickhouse_settings: { + async_insert: 1, + wait_for_async_insert: 1, // wait for ack from server + async_insert_max_data_size: '1000000', + async_insert_busy_timeout_ms: 1000, + }, +}) +``` + +## Concurrent small inserts + +Each call still uses the client's normal `insert()` API — the server merges +the batches. + +```ts +const promises = [...new Array(10)].map(async () => { + const values = [...new Array(1000).keys()].map(() => ({ + id: Math.floor(Math.random() * 100_000) + 1, + data: Math.random().toString(36).slice(2), + })) + + await client + .insert({ table: 'async_insert_example', values, format: 'JSONEachRow' }) + .catch((err) => { + if (err instanceof ClickHouseError) { + // err.code matches a row in system.errors + console.error(`ClickHouse error ${err.code}:`, err) + return + } + console.error('Insert failed:', err) + }) +}) + +await Promise.all(promises) +``` + +## `wait_for_async_insert` — fire-and-forget vs ack + +| `wait_for_async_insert` | Promise resolves when… | Trade-off | +| ----------------------- | ------------------------------------------------- | ------------------------------------------------------------------- | +| `1` (default) | Server has flushed the batch to the table | Slower per call; insert errors surface to the client | +| `0` | Server accepted the row into its in-memory buffer | Faster; flush errors won't surface — only validation/parsing errors | + +With `wait_for_async_insert: 1`, expect each insert call to take roughly +`async_insert_busy_timeout_ms` to resolve when traffic is light, because the +server waits for more rows or for the timer to fire before flushing. + +## Combining DDL with async inserts + +When creating tables in scripts that immediately insert, ack the DDL with +`wait_end_of_query: 1` so the table is ready before the first insert: + +```ts +await client.command({ + query: ` + CREATE OR REPLACE TABLE async_insert_example (id Int32, data String) + ENGINE MergeTree ORDER BY id + `, + clickhouse_settings: { wait_end_of_query: 1 }, +}) +``` + +## Common pitfalls + +- **Setting `async_insert` per call but expecting client-side batching.** + The client still issues each `insert()` as a separate HTTP request — the + batching happens on the server. +- **Confusing `wait_for_async_insert` (async-insert ack) with + `wait_end_of_query` (DDL ack).** They are unrelated. +- **Treating a resolved insert under `wait_for_async_insert: 0` as + durably written.** It only means the server accepted the bytes; flush + failures will not surface to the client. +- **Not handling `ClickHouseError`.** It exposes `err.code`, which maps to + rows in the `system.errors` table — use it to decide whether to retry. diff --git a/skills/clickhouse-js-node-coding/reference/client-configuration.md b/skills/clickhouse-js-node-coding/reference/client-configuration.md new file mode 100644 index 000000000..894fd2fc4 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/client-configuration.md @@ -0,0 +1,159 @@ +# Client Configuration + +> **Applies to:** all versions, with these notable additions: +> +> - `pathname` config option: client `>= 1.0.0`. +> - `clickhouse_setting_*` / `ch_*` URL parameters: client `>= 1.0.0`. +> - `keep_alive.idle_socket_ttl` (Node-only): client `>= 1.0.0`. + +Backing examples: +[`examples/node/coding/url_configuration.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/url_configuration.ts), +[`examples/node/coding/clickhouse_settings.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/clickhouse_settings.ts), +[`examples/node/coding/default_format_setting.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/default_format_setting.ts). + +## Answer checklist + +When answering configuration questions, include the relevant points: + +- Show `createClient` from `@clickhouse/client` with explicit fields when the + user is writing code; this is easier to read and review than encoding + everything into a URL string. +- When mentioning the URL form for environment variables / DSNs: show a **Bash** + `export` with the literal URL value, and `createClient({ url: process.env.CLICKHOUSE_URL })` + in the Node code. **Never construct a URL in application code** — no string + concatenation, no template literals, no query-string builders. +- If URL parameters and object fields both set the same option, URL parameters + override the rest of the configuration object. +- If `clickhouse_settings` appear on `createClient`, explain that they are + defaults for every request and can be overridden on individual `query()`, + `insert()`, `command()`, or `exec()` calls. +- Remind long-running services to close the client during graceful shutdown. +- The `application` field sets the name that appears in `system.query_log`. + Do **not** mention any specific HTTP header name — the client handles header + mapping internally and the header names are an implementation detail. + +## Minimal client + +```ts +import { createClient } from '@clickhouse/client' + +const client = createClient({ + url: process.env.CLICKHOUSE_URL, // defaults to 'http://localhost:8123' + username: process.env.CLICKHOUSE_USER, // defaults to 'default' + password: process.env.CLICKHOUSE_PASSWORD, // defaults to '' + database: 'analytics', // defaults to 'default' +}) +// ... your queries ... +await client.close() +``` + +`url` accepts a string or a `URL` object. The accepted string format is: + +``` +http[s]://[username:password@]hostname:port[/database][?param1=value1¶m2=value2] +``` + +## Configuration via URL parameters + +A fixed allowlist of config fields can be set as URL query parameters +(plus any key prefixed with `clickhouse_setting_` / `ch_` / `http_header_`). +**Supported URL parameters override the corresponding values in the rest of +the configuration object** — when they do, the client logs a warning. +Unknown URL parameters cause `createClient` to throw +`Unknown URL parameters: ...` +(see [`packages/client-common/src/config.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/packages/client-common/src/config.ts) for the shared allowlist, and [`packages/client-node/src/config.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/packages/client-node/src/config.ts) for Node-specific URL parameters). + +Supported non-prefixed keys parsed by `client-common`: `application`, +`session_id`, `pathname`, `access_token`, `request_timeout`, +`max_open_connections`, `compression_request`, `compression_response`, +`log_level`, `keep_alive_enabled`. Additionally, Node supports +`keep_alive_idle_socket_ttl` via the Node-specific config implementation. +Anything else must be passed via the config object on `createClient`. + +Prefer explicit object fields in application code. Use the URL form when the +application receives one connection string from an environment variable, secret +manager, or config file. The URL value belongs in the environment, not in the +source code — show it as a shell export and read it in Node: + +```bash +# In your shell environment / deployment config (e.g. .env, Kubernetes secret): +export CLICKHOUSE_URL='https://bob:secret@my.host:8124/analytics?application=my_analytics_app&ch_async_insert=1&ch_wait_for_async_insert=0' +``` + +```ts +// In your Node.js code — no URL construction needed: +const client = createClient({ url: process.env.CLICKHOUSE_URL }) +``` + +The same connection can also be expressed as an explicit config object (useful when you want to document each field individually): + +```ts +import { createClient } from '@clickhouse/client' + +createClient({ + url: 'https://my.host:8124', + username: 'bob', + password: 'secret', + database: 'analytics', + application: 'my_analytics_app', + clickhouse_settings: { async_insert: 1, wait_for_async_insert: 0 }, +}) +``` + +## Per-client vs per-request `clickhouse_settings` ⭐ + +> **Always mention this when discussing `clickhouse_settings`:** settings set +> on `createClient` are defaults; any individual call can override them. + +Settings on `createClient` apply to every request. Settings on a single +operation (`query`, `insert`, `command`, `exec`) override the client defaults +for **that call only**. + +```ts +const client = createClient({ + clickhouse_settings: { + date_time_input_format: 'best_effort', // applied to every request + }, +}) + +const rows = await client.query({ + query: 'SELECT number FROM system.numbers LIMIT 2', + format: 'JSONEachRow', + clickhouse_settings: { + output_format_json_quote_64bit_integers: 1, // overrides client default for this call + }, +}) +``` + +## `default_format` for `exec()` + +`client.exec()` runs an arbitrary statement and returns a stream. If your +query has no trailing `FORMAT …` clause, set `default_format` so the server +knows what to send back, then wrap the response in a `ResultSet`: + +```ts +import { createClient, ResultSet } from '@clickhouse/client' + +const client = createClient() +const format = 'JSONCompactEachRowWithNamesAndTypes' +const { stream, query_id } = await client.exec({ + query: 'SELECT database, name, engine FROM system.tables LIMIT 5', + clickhouse_settings: { default_format: format }, +}) +const rs = new ResultSet(stream, format, query_id) +console.log(await rs.json()) +await client.close() +``` + +For ordinary `SELECT`s prefer `client.query({ format })` — `default_format` is +only needed for raw `exec()`. + +## Common pitfalls + +- **Don't put a path in `url` and expect it to be the database name when + you're behind a proxy.** Use `pathname` for the proxy path and `database` + for the DB. (Symptom: "wrong database selected.") See the + troubleshooting skill for diagnosis. +- **Don't create a client per request.** `createClient` opens a connection + pool; share one client across the process and `close()` on shutdown. +- **`max_open_connections` must be `>= 1`** when set explicitly. diff --git a/skills/clickhouse-js-node-coding/reference/custom-json.md b/skills/clickhouse-js-node-coding/reference/custom-json.md new file mode 100644 index 000000000..2e6e4d242 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/custom-json.md @@ -0,0 +1,149 @@ +# Custom JSON `parse` / `stringify` + +> **Requires:** client `>= 1.14.0` (configurable `json.parse` and +> `json.stringify`). Earlier versions cannot swap the JSON implementation. + +Backing example: +[`examples/node/coding/custom_json_handling.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/custom_json_handling.ts). + +## Answer checklist + +When the user wants `UInt64`/`Int64` values back as `BigInt`: + +- State that configurable `json.parse` / `json.stringify` requires + `@clickhouse/client >= 1.14.0`. +- Show the supported `createClient({ json: { parse, stringify } })` option, + usually with `json-bigint` and `useNativeBigInt: true`. +- Combine it with `output_format_json_quote_64bit_integers: 0` so the server + emits unquoted 64-bit integers that the parser can turn into `BigInt`. +- Mention that `output_format_json_quote_64bit_integers: 0` is the default + since ClickHouse `25.8`, but setting it explicitly is useful for older + servers or portable examples. +- Warn that casting to JavaScript `Number` / `parseInt` / `parseFloat` loses + precision above `Number.MAX_SAFE_INTEGER`. + +## Why customize? + +The default `JSON.stringify` / `JSON.parse`: + +- Throws on `BigInt`. +- Calls `Date.prototype.toJSON()` (ISO string) — fine for `DateTime` with + `date_time_input_format: 'best_effort'`, surprising in some workflows. +- Loses precision for 64-bit integers returned as numbers (a separate + issue — covered in the troubleshooting skill). + +A custom `{ parse, stringify }` lets you plug in `JSONBig`, +`safe-stable-stringify`, your own `BigInt`-aware serializer, etc. + +## Recipe: BigInt-safe stringify, custom Date handling + +```ts +import { createClient } from '@clickhouse/client' + +const valueSerializer = (value: unknown): unknown => { + // Serialize Date as a UNIX millis number (instead of toJSON's ISO string) + if (value instanceof Date) { + return value.getTime() + } + + // Serialize BigInt as a string so JSON.stringify won't throw + if (typeof value === 'bigint') { + return value.toString() + } + + if (Array.isArray(value)) { + return value.map(valueSerializer) + } + + if (typeof value === 'object' && value !== null) { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, valueSerializer(v)]), + ) + } + + return value +} + +const client = createClient({ + json: { + parse: JSON.parse, + stringify: (obj: unknown) => JSON.stringify(valueSerializer(obj)), + }, +}) + +await client.command({ + query: ` + CREATE OR REPLACE TABLE inserts_custom_json_handling + (id UInt64, dt DateTime64(3, 'UTC')) + ENGINE MergeTree + ORDER BY id + `, +}) + +await client.insert({ + table: 'inserts_custom_json_handling', + format: 'JSONEachRow', + values: [ + { + id: BigInt(250000000000000200), // serialized as a string + dt: new Date(), // serialized as ms since epoch + }, + ], +}) + +const rows = await client.query({ + query: 'SELECT * FROM inserts_custom_json_handling', + format: 'JSONEachRow', +}) +console.info(await rows.json()) +await client.close() +``` + +> The custom `valueSerializer` runs **before** `JSON.stringify`, so values +> are transformed before the standard hooks (`Date.prototype.toJSON`, +> object `toJSON()` methods, etc.) ever run. + +## Recipe: BigInt-safe parsing for 64-bit integer columns + +If you want `UInt64`/`Int64` to come back as `BigInt`s (instead of strings +or precision-lossy numbers), plug in a `BigInt`-aware parser such as +[`json-bigint`](https://www.npmjs.com/package/json-bigint): + +```ts +import { createClient } from '@clickhouse/client' +import JSONBig from 'json-bigint' + +const bigJson = JSONBig({ useNativeBigInt: true }) + +const client = createClient({ + json: { + parse: bigJson.parse, + stringify: bigJson.stringify, + }, + clickhouse_settings: { + output_format_json_quote_64bit_integers: 0, + }, +}) +``` + +This applies to **both** outgoing JSON bodies and incoming JSON-format +responses. Combine with `output_format_json_quote_64bit_integers: 0` (the +default since CH 25.8) so the server emits unquoted 64-bit integers that +`json-bigint` can parse to `BigInt`. + +## Common pitfalls + +- **Setting `json.parse` only.** That only affects reading JSON responses; + outgoing JSON bodies use `json.stringify`. If you want consistent custom + handling in both directions, generally provide a matching `stringify` too. +- **Forgetting `bigint` handling in `stringify`.** Default `JSON.stringify` + throws on `BigInt`; if your data ever contains one, the insert will fail + with `TypeError: Do not know how to serialize a BigInt`. +- **Targeting client `< 1.14.0`.** The `json` option doesn't exist; you'll + need to convert values manually before calling `insert()` / `query()` (or + upgrade). +- **Casting 64-bit integers to `Number`.** JavaScript's `number` type has + only 53 bits of mantissa — values above `Number.MAX_SAFE_INTEGER` (2^53 − 1) + are silently rounded. Do **not** try to fix precision loss by calling + `Number()`, `parseInt()`, or `parseFloat()` on the value. The correct fix + is a `BigInt`-aware parser (shown above), not a lossy cast. diff --git a/skills/clickhouse-js-node-coding/reference/data-types.md b/skills/clickhouse-js-node-coding/reference/data-types.md new file mode 100644 index 000000000..0e09616c5 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/data-types.md @@ -0,0 +1,169 @@ +# Modern Data Types: Dynamic, Variant, JSON, Time, Time64 + +> **Applies to** (server side): +> +> - `Variant`: ClickHouse `>= 24.1`. +> - `Dynamic`: ClickHouse `>= 24.5`. +> - New `JSON` (object) type: ClickHouse `>= 24.8`. +> - All three are **no longer experimental since `25.3`**; on older servers, +> you must enable the corresponding `allow_experimental_*_type` setting. +> - `Time` / `Time64`: ClickHouse `>= 25.6` and require +> `enable_time_time64_type: 1`. + +Backing examples: +[`examples/node/coding/dynamic_variant_json.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/dynamic_variant_json.ts), +[`examples/node/coding/time_time64.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/time_time64.ts). + +## Answer checklist + +When answering about storing and reading JSON objects: + +- Use the new `JSON` column type, introduced in ClickHouse `>= 24.8`. +- Say `JSON` is no longer experimental since ClickHouse `25.3`; on older + supported versions, enable `allow_experimental_json_type`. +- Insert real JS objects with `format: 'JSONEachRow'`; do not + `JSON.stringify()` the column value. +- Read with a JSON output format such as `JSONEachRow` and `resultSet.json()`; + `JSON` column values come back as parsed JS objects. + +## `Dynamic`, `Variant(...)`, `JSON` + +```ts +import { createClient } from '@clickhouse/client' + +const client = createClient({ + // Required only on ClickHouse < 25.3 — harmless to leave on + clickhouse_settings: { + allow_experimental_variant_type: 1, + allow_experimental_dynamic_type: 1, + allow_experimental_json_type: 1, + }, +}) + +await client.command({ + query: ` + CREATE OR REPLACE TABLE chjs_dynamic_variant_json + ( + id UInt64, + var Variant(Int64, String), + dynamic Dynamic, + json JSON + ) + ENGINE MergeTree + ORDER BY id + `, +}) + +await client.insert({ + table: 'chjs_dynamic_variant_json', + format: 'JSONEachRow', + values: [ + { id: 1, var: 42, dynamic: 'foo', json: { foo: 'x' } }, + { id: 2, var: 'str', dynamic: 144, json: { bar: 10 } }, + ], +}) + +const rs = await client.query({ + query: ` + SELECT *, + variantType(var), + dynamicType(dynamic), + dynamicType(json.foo), + dynamicType(json.bar) + FROM chjs_dynamic_variant_json + `, + format: 'JSONEachRow', +}) +console.log(await rs.json()) +``` + +### Notes + +- The `JSON` column type accepts a real JS object on insert and returns one + on select — no need for `JSON.stringify` / `JSON.parse` in your app code. +- A JS number written into a `Dynamic` or `Variant` column defaults to + `Int64` on the server. In JSON formats, `output_format_json_quote_64bit_integers` + controls how 64-bit integers are returned: `1` returns them as JSON strings, + while `0` returns them as JSON numbers (and `0` is the default since CH `25.8`). + In JS, large 64-bit integers returned as numbers can lose precision, so use + quoted output if you need exact integer values in application code. +- Use `variantType(...)`, `dynamicType(...)` to introspect what the server + ended up storing. + +## `Time` and `Time64(p)` + +`Time` is signed seconds (`-999:59:59` … `999:59:59`). `Time64(p)` adds +sub-second precision (`p` digits, up to `9` for nanoseconds). Both require +`enable_time_time64_type: 1` on `>= 25.6`. + +```ts +const client = createClient({ + clickhouse_settings: { enable_time_time64_type: 1 }, +}) + +await client.command({ + query: ` + CREATE OR REPLACE TABLE chjs_time_time64 + ( + id UInt64, + t Time, + t64_0 Time64(0), + t64_3 Time64(3), + t64_6 Time64(6), + t64_9 Time64(9), + ) + ENGINE MergeTree + ORDER BY id + `, +}) + +await client.insert({ + table: 'chjs_time_time64', + format: 'JSONEachRow', + values: [ + { + id: 1, + t: '12:34:56', + t64_0: '12:34:56', + t64_3: '12:34:56.123', + t64_6: '12:34:56.123456', + t64_9: '12:34:56.123456789', + }, + { + id: 2, + t: '999:59:59', + t64_0: '999:59:59', + t64_3: '999:59:59.999', + t64_6: '999:59:59.999999', + t64_9: '999:59:59.999999999', + }, + { + id: 3, + t: '-999:59:59', + t64_0: '-999:59:59', + t64_3: '-999:59:59.999', + t64_6: '-999:59:59.999999', + t64_9: '-999:59:59.999999999', + }, + ], +}) +``` + +### Notes + +- Pass values as **strings** in the `HH:MM:SS[.fraction]` format. Negatives + are supported; the magnitude can exceed 24 hours. +- For `Time64(p)` with `p > 3`, do not use JS `Date` — it tops out at + millisecond precision and will silently truncate. + +## Common pitfalls + +- **Targeting old ClickHouse servers without the `allow_experimental_*` + setting.** On `< 25.3`, `CREATE TABLE` will fail without them. +- **Expecting `JSON`-column reads to be raw strings.** They come back as + parsed objects in JSON formats. +- **Inserting `Time64(9)` from JS `Date` and losing precision.** Use a + string instead. +- **Reading a `Variant`/`Dynamic` value of type `Int64` and being surprised + it's a string.** That's the standard 64-bit-integers-in-JSON behavior; + see the troubleshooting skill if you need to change it. diff --git a/skills/clickhouse-js-node-coding/reference/insert-columns.md b/skills/clickhouse-js-node-coding/reference/insert-columns.md new file mode 100644 index 000000000..79d6a22b3 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/insert-columns.md @@ -0,0 +1,113 @@ +# Insert into Specific Columns / Other Databases + +> **Applies to:** all versions. The `columns` option (both forms) and the +> `database` config field are universally supported. + +Backing examples: +[`examples/node/coding/insert_specific_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_specific_columns.ts), +[`examples/node/coding/insert_exclude_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_exclude_columns.ts), +[`examples/node/coding/insert_ephemeral_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_ephemeral_columns.ts), +[`examples/node/coding/insert_into_different_db.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_into_different_db.ts). + +## Answer checklist + +When explaining partial-column inserts: + +- Show `columns: ['col_a', 'col_b']` for the allowlist form. +- Also mention the inverse `columns: { except: ['col_to_skip'] }` form so the + user knows both supported shapes. +- Explain that omitted columns receive their server-side defaults + (`DEFAULT`, `MATERIALIZED`, `ALIAS`, nullable/type defaults) and inserts can + still fail or produce surprising zero/empty values if the table definition + has no appropriate defaults. + +## Insert into specific columns + +Pass `columns: string[]` to limit the `INSERT` to a subset. Omitted columns +get their declared default. + +```ts +await client.insert({ + table: 'events', + format: 'JSONEachRow', + values: [{ message: 'foo' }], + columns: ['message'], // `id` will get its default (0 for UInt32) +}) +``` + +## Insert excluding columns + +Use `columns: { except: string[] }` for the inverse. Useful when most columns +should default but you want to name only the few to skip. + +```ts +await client.insert({ + table: 'events', + format: 'JSONEachRow', + values: [{ message: 'bar' }], + columns: { except: ['id'] }, +}) +``` + +## Tables with EPHEMERAL columns + +[Ephemeral columns](https://clickhouse.com/docs/en/sql-reference/statements/create/table#ephemeral) +are not stored — they only exist to drive `DEFAULT` expressions of other +columns. To trigger that default logic, **the ephemeral column must be in the +`columns` list**, even though no value will be persisted for it. + +```ts +await client.command({ + query: ` + CREATE OR REPLACE TABLE events + ( + id UInt64, + message String DEFAULT message_default, + message_default String EPHEMERAL + ) + ENGINE MergeTree + ORDER BY id + `, +}) + +await client.insert({ + table: 'events', + format: 'JSONEachRow', + values: [ + { id: '42', message_default: 'foo' }, + { id: '144', message_default: 'bar' }, + ], + // Including the ephemeral column name triggers the DEFAULT expression + columns: ['id', 'message_default'], +}) +``` + +## Insert into a different database + +If the client's default `database` is not the target, qualify the table name +with `db.table`: + +```ts +const client = createClient({ database: 'system' }) + +await client.command({ query: 'CREATE DATABASE IF NOT EXISTS analytics' }) + +await client.insert({ + table: 'analytics.events', // fully qualified + format: 'JSONEachRow', + values: [{ id: 42, message: 'foo' }], +}) +``` + +There is no per-call `database` override on `insert()` / `query()` — qualify +the identifier, or create a second client with the desired `database`. + +## Common pitfalls + +- **Forgetting the ephemeral column in `columns`.** If you list only the + non-ephemeral columns, the `DEFAULT` expression that depends on the + ephemeral value won't fire and you'll get empty/zero defaults instead. +- **Hoping `client.insert({ database: '…' })` works.** It doesn't — qualify + the `table` instead. +- **Mixing the two `columns` forms.** Use either `string[]` _or_ + `{ except: string[] }`, not both. diff --git a/skills/clickhouse-js-node-coding/reference/insert-formats.md b/skills/clickhouse-js-node-coding/reference/insert-formats.md new file mode 100644 index 000000000..0feb6c205 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/insert-formats.md @@ -0,0 +1,145 @@ +# Insert Data Formats + +> **Applies to:** all versions. The `JSON` type column / new JSON family is a +> ClickHouse feature; the JSON _formats_ listed here are universally supported +> by the client. + +Backing examples: +[`examples/node/coding/array_json_each_row.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/array_json_each_row.ts), +[`examples/node/coding/insert_data_formats_overview.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_data_formats_overview.ts). + +> **Raw / binary formats (CSV, TSV, CustomSeparated, Parquet) require a Node +> stream as input.** See +> [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance) +> — defer if the user wants to insert from a file or `Readable`. + +## Answer checklist + +When answering "what format/call should I use for an array of JS objects?": + +- Use `client.insert({ table, values, format: 'JSONEachRow' })`. +- Say the array of plain objects can be passed directly as `values` for + ordinary in-memory batches such as a few thousand or tens of thousands of + rows. +- Do not steer the user to streaming, Parquet, or file APIs unless their input + is already a stream/file or the task is explicitly about throughput. +- Warn not to wrap `JSONEachRow` rows in a `{ data: [...] }` envelope; that + shape belongs to single-document formats. +- Mention `JSONCompactEachRow*` as a denser alternative for larger payloads + when the caller can provide positional arrays or explicit names/types. + +## Default choice: `JSONEachRow` with an array of objects + +This is the right answer for ~90% of inserts. + +```ts +import { createClient } from '@clickhouse/client' + +const client = createClient() + +await client.insert({ + table: 'events', + format: 'JSONEachRow', + values: [ + { id: 42, name: 'foo' }, + { id: 43, name: 'bar' }, + ], +}) + +await client.close() +``` + +The shape of `values` must match the chosen format. + +## Streamable JSON formats (pass an array) + +| Format | `values` shape | +| -------------------------------------------- | --------------------------------------------------- | +| `JSONEachRow` | `Array<{ col: value, ... }>` | +| `JSONStringsEachRow` | `Array<{ col: stringifiedValue, ... }>` | +| `JSONCompactEachRow` | `Array<[v1, v2, ...]>` | +| `JSONCompactStringsEachRow` | `Array<[stringV1, stringV2, ...]>` | +| `JSONCompactEachRowWithNames` | First row = column names, then data rows | +| `JSONCompactEachRowWithNamesAndTypes` | Row 1 = names, row 2 = types, then data | +| `JSONCompactStringsEachRowWithNames` | First row = names, then stringified data rows | +| `JSONCompactStringsEachRowWithNamesAndTypes` | Row 1 = names, row 2 = types, then stringified data | + +```ts +await client.insert({ + table: 'events', + format: 'JSONCompactEachRowWithNamesAndTypes', + values: [ + ['id', 'name', 'sku'], + ['UInt32', 'String', 'Array(UInt32)'], + [11, 'foo', [1, 2, 3]], + [12, 'bar', [4, 5, 6]], + ], +}) +``` + +These formats can be **streamed** — pass a Node stream of rows instead of an +array. See +[`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance) +for streaming guidance. + +## Single-document JSON formats (pass an object) + +These cannot be streamed — the entire body is sent in one shot. + +| Format | `values` shape (typed via `InputJSON` / `InputJSONObjectEachRow`) | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `JSON` | `{ meta: [], data: Array<{ col: value, ... }> }` — for TypeScript/client usage, pass `meta: []` if metadata is not needed | +| `JSONCompact` | `{ meta: [{ name, type }, ...], data: Array<[v1, v2, ...]> }` | +| `JSONColumnsWithMetadata` | `{ meta: [...], data: { col1: [v, ...], col2: [v, ...] } }` | +| `JSONObjectEachRow` | `Record` (the record key labels each row but is not stored) | + +```ts +import type { InputJSON, InputJSONObjectEachRow } from '@clickhouse/client' + +const meta: InputJSON['meta'] = [ + { name: 'id', type: 'UInt32' }, + { name: 'name', type: 'String' }, +] + +await client.insert({ + table: 'events', + format: 'JSONCompact', + values: { + meta, + data: [ + [19, 'foo'], + [20, 'bar'], + ], + }, +}) + +await client.insert({ + table: 'events', + format: 'JSONObjectEachRow', + values: { + row_1: { id: 23, name: 'foo' }, + row_2: { id: 24, name: 'bar' }, + } satisfies InputJSONObjectEachRow<{ id: number; name: string }>, +}) +``` + +## Quick chooser + +| Use case | Format | +| -------------------------------------------- | ------------------------------------------------- | +| Insert plain JS objects | `JSONEachRow` _(default)_ | +| Insert tuples / column-positional rows | `JSONCompactEachRow` | +| Insert with explicit column ordering / types | `JSONCompactEachRow*WithNames…` | +| Insert a single document with metadata | `JSON`, `JSONCompact` | +| Insert from a CSV / TSV / Parquet file | Raw format + Node stream → `examples/node/performance/` | + +## Common pitfalls + +- **Wrong shape for the format.** The most common cause of insert failures — + e.g., passing `Array<{...}>` to `JSONCompact` (which expects + `{ meta, data }`). +- **Don't wrap a `JSONEachRow` array in a `{ data: [...] }` envelope.** That + envelope only belongs to single-document formats (`JSON` / `JSONCompact` / + `JSONColumnsWithMetadata`). +- For type guidance (`Decimal` strings, `Date` objects, `BigInt`), see + `insert-values.md` and `custom-json.md`. diff --git a/skills/clickhouse-js-node-coding/reference/insert-values.md b/skills/clickhouse-js-node-coding/reference/insert-values.md new file mode 100644 index 000000000..6875f05ba --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/insert-values.md @@ -0,0 +1,141 @@ +# Insert Values, SQL Expressions, Dates, Decimals + +> **Applies to:** all versions. `wait_end_of_query: 1` is a server-side +> setting available on every supported ClickHouse version. + +Backing examples: +[`examples/node/coding/insert_from_select.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_from_select.ts), +[`examples/node/coding/insert_values_and_functions.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_values_and_functions.ts), +[`examples/node/coding/insert_js_dates.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_js_dates.ts), +[`examples/node/coding/insert_decimals.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_decimals.ts). + +## `INSERT … SELECT` (no values payload) + +When the data already lives in ClickHouse, use `client.command()` with a raw +`INSERT … SELECT`: + +```ts +await client.command({ + query: ` + INSERT INTO target + SELECT '42', quantilesBFloat16State(0.5)(arrayJoin([toFloat32(10), toFloat32(20)])) + `, +}) +``` + +Use `command()` (not `insert()`) — there is no row payload to send. + +## `INSERT … VALUES` with SQL functions + +When you need `unhex(...)`, `toUUID(...)`, `now()`, or any other SQL +function around a value, keep the SQL shape static and pass values with +ClickHouse `{name: Type}` parameters. Run it via `command()` and set +`wait_end_of_query: 1` for safety in clustered setups. + +```ts +await client.command({ + query: ` + INSERT INTO events (id, timestamp, email, name) + VALUES ( + unhex({id: String}), + {timestamp: DateTime}, + {email: String}, + {name: Nullable(String)} + ) + `, + query_params: { + id: '00112233445566778899aabbccddeeff', + timestamp: '2026-05-06 12:34:56', + email: 'alice@example.com', + name: 'Alice', + }, + clickhouse_settings: { wait_end_of_query: 1 }, +}) +``` + +Do not build `VALUES` rows with string interpolation or manual escaping. If +you need to insert many ordinary JS rows, prefer `client.insert()` with +`format: 'JSONEachRow'`; use this `command()` pattern when the SQL itself needs +functions or expressions around the values. + +## Inserting JS `Date` objects + +JS `Date` objects work for `DateTime` and `DateTime64` columns once the +server is set to accept ISO-8601 strings. Either set +`date_time_input_format: 'best_effort'` per request, on the client, or +session-wide. + +```ts +await client.insert({ + table: 'events', + format: 'JSONEachRow', + values: [{ id: '42', dt: new Date() }], + clickhouse_settings: { + date_time_input_format: 'best_effort', + }, +}) +``` + +> JS `Date` objects do **not** work for the `Date` type (date-only) — pass +> `'YYYY-MM-DD'` strings for that. + +## Inserting `Decimal*` values + +Decimals must be passed as **strings** in JSON formats to avoid precision +loss in JavaScript: + +```ts +await client.command({ + query: ` + CREATE OR REPLACE TABLE prices ( + id UInt32, + dec32 Decimal(9, 2), + dec64 Decimal(18, 3), + dec128 Decimal(38, 10), + dec256 Decimal(76, 20) + ) + ENGINE MergeTree ORDER BY id + `, +}) + +await client.insert({ + table: 'prices', + format: 'JSONEachRow', + values: [ + { + id: 1, + dec32: '1234567.89', + dec64: '123456789123456.789', + dec128: '1234567891234567891234567891.1234567891', + dec256: + '12345678912345678912345678911234567891234567891234567891.12345678911234567891', + }, + ], +}) +``` + +When reading them back, cast to string in the SELECT to avoid the same +precision loss: + +```ts +const rs = await client.query({ + query: ` + SELECT toString(dec64) AS decimal64, + toString(dec128) AS decimal128 + FROM prices + `, + format: 'JSONEachRow', +}) +``` + +## Common pitfalls + +- **Passing decimals as JS `number`s.** Anything beyond `Number.MAX_SAFE_INTEGER` + silently loses precision before it ever reaches the server. +- **Using `client.insert()` for `INSERT … SELECT`.** There's nothing to + upload — use `client.command()` with the full SQL. +- **Forgetting `date_time_input_format: 'best_effort'`** when inserting + `Date` objects (or ISO strings). The default input format does not accept + ISO-8601 with the `T`/`Z` separators. +- **Hand-building `VALUES` with user input.** Always parameterize user data; + see `reference/query-parameters.md`. diff --git a/skills/clickhouse-js-node-coding/reference/ping.md b/skills/clickhouse-js-node-coding/reference/ping.md new file mode 100644 index 000000000..8069ae3fb --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/ping.md @@ -0,0 +1,120 @@ +# Ping the Server + +> **Applies to:** all versions. `ping()` returns a discriminated +> `PingResult = { success: true } | { success: false, error: Error }` — +> it does **not** throw on connection failures. + +Backing examples: +[`examples/node/coding/ping_existing_host.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/ping_existing_host.ts), +[`examples/node/coding/ping_non_existing_host.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/ping_non_existing_host.ts). + +## Successful ping + +```ts +import { createClient } from '@clickhouse/client' + +const client = createClient({ + url: process.env.CLICKHOUSE_URL, + password: process.env.CLICKHOUSE_PASSWORD, +}) + +const pingResult = await client.ping() +if (pingResult.success) { + console.info('ClickHouse is reachable') +} else { + console.error('Ping failed:', pingResult.error) +} +await client.close() +``` + +Use `ping()` to: + +- Probe ClickHouse at application startup. +- Wake up a ClickHouse Cloud instance that may be idling (a ping is enough to + bring it out of sleep). +- Implement a `/healthz` / readiness endpoint. + +## Failure: host unreachable + +`ping()` does **not** throw — it resolves with +`{ success: false, error: Error }`, so you can branch without `try/catch`: + +```ts +import type { PingResult } from '@clickhouse/client' +import { createClient } from '@clickhouse/client' + +const client = createClient({ + url: 'http://localhost:8100', // non-existing host + request_timeout: 50, // keep failure fast +}) + +const pingResult = await client.ping() +if (hasConnectionRefusedError(pingResult)) { + console.info('Connection refused, as expected') +} else { + console.error('Ping expected ECONNREFUSED, got:', pingResult) +} +await client.close() + +function hasConnectionRefusedError( + pingResult: PingResult, +): pingResult is PingResult & { error: { code: 'ECONNREFUSED' } } { + return ( + !pingResult.success && + 'code' in pingResult.error && + pingResult.error.code === 'ECONNREFUSED' + ) +} +``` + +## Mapping to an HTTP health endpoint + +```ts +app.get('/healthz', async (_req, res) => { + const r = await client.ping() + if (r.success) { + res.status(200).json({ ok: true }) + } else { + res.status(503).json({ ok: false, error: String(r.error) }) + } +}) +``` + +## `ping()` vs `ping({ select: true })` + +The default `ping()` hits ClickHouse's `/ping` HTTP endpoint — it verifies +network connectivity but **does not check credentials or query processing**. +A server that is reachable but has a bad password (or a broken query +pipeline) will still return `{ success: true }` from a plain `ping()`. + +Pass `{ select: true }` to run a lightweight `SELECT 1` instead: + +```ts +const r = await client.ping({ select: true }) +// success only if the server is reachable AND auth is correct AND it can run queries +``` + +| | `client.ping()` | `client.ping({ select: true })` | +| ----------------------- | --------------- | ------------------------------- | +| Endpoint | `/ping` (HTTP) | `SELECT 1` query | +| Checks auth | **No** | Yes | +| Checks query processing | No | **Yes** | +| Overhead | Minimal | Slightly higher | + +**When to use which:** + +- **Liveness probe** (is the process alive?) — plain `ping()` is fine. +- **Readiness probe** (can it serve traffic?) — use `ping({ select: true })` + so the probe fails if credentials are wrong or the query layer is broken. +- **Waking a ClickHouse Cloud idle instance** — plain `ping()` is enough. + +## Common pitfalls + +- **Do not wrap `ping()` in `try/catch` as your only check.** It resolves on + failure; the `success` boolean is the source of truth. +- **Lower `request_timeout` if you want pings to fail fast** (the example + above uses `50` ms). The default is high enough to be unsuitable for + liveness probes. +- **Plain `ping()` does not check credentials.** If auth is part of what you + want to verify, use `ping({ select: true })`. +- For ping that times out specifically, see the troubleshooting skill. diff --git a/skills/clickhouse-js-node-coding/reference/query-parameters.md b/skills/clickhouse-js-node-coding/reference/query-parameters.md new file mode 100644 index 000000000..ca9e35788 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/query-parameters.md @@ -0,0 +1,152 @@ +# Query Parameter Binding + +> **Applies to:** all versions. NULL parameter binding fixed in `0.0.16`. +> Special-character (tab/newline/quote/backslash) binding `>= 0.3.1`. +> `TupleParam` and JS `Map` parameters `>= 1.9.0`. Boolean formatting in +> `Array`/`Tuple`/`Map` parameters fixed in `>= 1.13.0`. `BigInt` query +> parameters `>= 1.15.0`. + +Backing examples: +[`examples/node/coding/query_with_parameter_binding.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/query_with_parameter_binding.ts), +[`examples/node/coding/query_with_parameter_binding_special_chars.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/query_with_parameter_binding_special_chars.ts). + +## Answer checklist + +When the user passes user-controlled values into SQL: + +- Use ClickHouse `{name: Type}` placeholders and a `query_params` object. +- Explicitly call template-literal/string interpolation of user input a + **SQL injection risk**. +- Do not suggest PostgreSQL/MySQL-style `$1`, `?`, or `:name` placeholders. +- Pick the placeholder type to match the ClickHouse column type (`String`, + `Date`, `DateTime`, `Nullable(T)`, etc.). + +## Syntax: `{name: Type}` + +ClickHouse uses `{name: Type}` placeholders — **not** `$1`, `?`, or `:name`. + +```ts +await client.query({ + query: 'SELECT plus({a: Int32}, {b: Int32})', + format: 'JSONEachRow', + query_params: { a: 10, b: 20 }, +}) +``` + +The `Type` must be a valid ClickHouse type (`Int32`, `String`, `Date`, +`Array(UInt32)`, `Tuple(Int32, String)`, `Map(K, V)`, `Nullable(T)`, etc.). + +## ⚠️ Never use template literals for user values + +Interpolating user input into the SQL string bypasses server-side escaping +and opens the door to SQL injection: + +```ts +// ❌ Dangerous — never do this with user-controlled values +const userId = req.params.id +await client.query({ query: `SELECT * FROM users WHERE id = ${userId}` }) + +// ✓ Safe — parameterized +await client.query({ + query: 'SELECT * FROM users WHERE id = {id: UInt32}', + query_params: { id: userId }, +}) +``` + +This is the most common mistake for users coming from PostgreSQL/MySQL. Call +it out explicitly when the user shows template-literal interpolation. + +## Common types + +```ts +import { TupleParam } from '@clickhouse/client' + +await client.query({ + query: ` + SELECT + {var_int: Int32} AS var_int, + {var_float: Float32} AS var_float, + {var_str: String} AS var_str, + {var_array: Array(Int32)} AS var_array, + {var_tuple: Tuple(Int32, String)} AS var_tuple, + {var_map: Map(Int, Array(String))} AS var_map, + {var_date: Date} AS var_date, + {var_datetime: DateTime} AS var_datetime, + {var_datetime64_3: DateTime64(3)} AS var_datetime64_3, + {var_datetime64_9: DateTime64(9)} AS var_datetime64_9, + {var_decimal: Decimal(9, 2)} AS var_decimal, + {var_uuid: UUID} AS var_uuid, + {var_ipv4: IPv4} AS var_ipv4, + {var_null: Nullable(String)} AS var_null + `, + format: 'JSONEachRow', + query_params: { + var_int: 10, + var_float: '10.557', + var_str: 'hello', + var_array: [42, 144], + var_tuple: new TupleParam([42, 'foo']), // >= 1.9.0 + var_map: new Map([ + [42, ['a', 'b']], + [144, ['c', 'd']], + ]), // >= 1.9.0 + var_date: '2022-01-01', + var_datetime: '2022-01-01 12:34:56', // or a Date + var_datetime64_3: '2022-01-01 12:34:56.789', // or a Date + var_datetime64_9: '2022-01-01 12:34:56.123456789', // string for ns precision + var_decimal: '123.45', // string to avoid precision loss + var_uuid: '01234567-89ab-cdef-0123-456789abcdef', + var_ipv4: '192.168.0.1', + var_null: null, // fixed in 0.0.16 + }, +}) +``` + +### Type-by-type tips + +- **Decimals** — pass as strings to avoid JS number precision loss. +- **`DateTime64(>3)`** — pass as a string; JS `Date` only has millisecond + precision and will lose sub-millisecond digits. +- **`DateTime64`** — strings can also be UNIX timestamps, including + fractional ones (e.g., `'1651490755.123456789'`). +- **`BigInt`** — supported in `query_params` since `>= 1.15.0`. On older + clients, pass as a string. +- **`Tuple(...)`** — wrap in `new TupleParam([...])` (`>= 1.9.0`); on older + clients, build the literal manually as a string. +- **`Map(K, V)`** — pass a JS `Map` (`>= 1.9.0`); on older clients, build + it manually. +- **`Nullable(T)`** — pass `null` directly (`>= 0.0.16`). + +## Special characters in string parameters (`>= 0.3.1`) + +Tabs, newlines, carriage returns, single quotes, and backslashes are +escaped automatically by the client — just pass the JS string as-is: + +```ts +await client.query({ + query: ` + SELECT + 'foo_\t_bar' = {tab: String} AS has_tab, + 'foo_\n_bar' = {newline: String} AS has_newline, + 'foo_\\'_bar' = {single_quote: String} AS has_single_quote, + 'foo_\\_bar' = {backslash: String} AS has_backslash + `, + format: 'JSONEachRow', + query_params: { + tab: 'foo_\t_bar', + newline: 'foo_\n_bar', + single_quote: "foo_'_bar", + backslash: 'foo_\\_bar', + }, +}) +``` + +## Common pitfalls + +- **`$1` / `?` / `:name` placeholders.** None work — use `{name: Type}`. +- **Forgetting the type in the placeholder.** `{id}` is a syntax error; + it must be `{id: UInt32}`. +- **Stringifying tuples/maps manually on `>= 1.9.0`.** Use `TupleParam` + and `Map` — both serialize correctly and respect special characters. +- **Boolean array/tuple/map elements before `1.13.0`.** Boolean formatting + was fixed in 1.13.0 — earlier versions may misformat them. diff --git a/skills/clickhouse-js-node-coding/reference/select-formats.md b/skills/clickhouse-js-node-coding/reference/select-formats.md new file mode 100644 index 000000000..5a0305deb --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/select-formats.md @@ -0,0 +1,111 @@ +# Select Data Formats + +> **Applies to:** all versions. `JSONEachRowWithProgress` requires client +> `>= 1.7.0`; see the in-repo performance examples under +> `examples/node/performance/`. + +Backing examples: +[`examples/node/coding/select_json_each_row.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_json_each_row.ts), +[`examples/node/coding/select_data_formats_overview.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_data_formats_overview.ts), +[`examples/node/coding/select_json_with_metadata.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_json_with_metadata.ts). + +## Default choice: `JSONEachRow` → `.json()` + +Right answer for ~90% of selects when the result fits in memory. + +```ts +import { createClient } from '@clickhouse/client' + +interface Row { + number: string +} + +const client = createClient() +const rows = await client.query({ + query: 'SELECT number FROM system.numbers LIMIT 5', + format: 'JSONEachRow', +}) +const result = await rows.json() // Row[] +result.forEach((r) => console.log(r)) +await client.close() +``` + +`UInt64`/`Int64` and other 64-bit integers are returned as **strings** +when `output_format_json_quote_64bit_integers=1`, to avoid JS precision +loss. If that setting is `0`, they may be returned as unquoted JSON +numbers instead. Note that in ClickHouse `>= 25.8`, this setting can +default to `0`; see the troubleshooting skill for ways to control that. + +## Single-document `JSON` format with metadata + +Use `JSON` (or `JSONCompact`) when you need ClickHouse's response envelope +(rows + meta + statistics + row count). Type the result with +`ResponseJSON`: + +```ts +import { createClient, type ResponseJSON } from '@clickhouse/client' + +const client = createClient() +const rows = await client.query({ + query: 'SELECT number FROM system.numbers LIMIT 2', + format: 'JSON', +}) +const result = await rows.json>() +console.info(result.meta, result.data, result.rows, result.statistics) +await client.close() +``` + +> `JSON`, `JSONCompact`, `JSONStrings`, `JSONCompactStrings`, +> `JSONColumnsWithMetadata`, `JSONObjectEachRow` are **single-document** +> formats — they cannot be streamed. Use a `*EachRow` variant if you want +> to stream. + +## Selecting raw text (CSV / TSV / CustomSeparated) + +Use `.text()` (not `.json()`) for raw textual formats: + +```ts +const rs = await client.query({ + query: 'SELECT number, number * 2 AS doubled FROM system.numbers LIMIT 3', + format: 'CSVWithNames', +}) +console.log(await rs.text()) +``` + +Streaming raw text/Parquet line-by-line belongs in +[`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance) +— in particular, Parquet exports use `client.exec()` and pipe the raw +response stream rather than `ResultSet.stream()` (see +[`select_parquet_as_file.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/performance/select_parquet_as_file.ts)). + +## Format chooser + +| Use case | Format | +| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Read rows as JS objects | `JSONEachRow` _(default)_ | +| Read rows as positional tuples (smaller payload) | `JSONCompactEachRow` | +| Need `meta` / `statistics` / `rows` envelope | `JSON` or `JSONCompact` + `ResponseJSON` | +| Read all values as strings (avoid number-precision loss) | `JSONStringsEachRow` / `JSONCompactStringsEachRow` | +| Stream very large result | `JSONEachRow` / `JSONCompactEachRow` (see [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance)) | +| Export to CSV/TSV/Parquet | `CSV*`, `TabSeparated*`, `Parquet` (see [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance)) | + +## ResultSet methods + +| Method | Returns | Notes | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `await rs.json()` | `T[]` for `*EachRow`, single-doc shape otherwise | Buffers the full response | +| `await rs.text()` | `string` | Buffers the full response — for textual formats only (CSV/TSV/etc.) | +| `rs.stream()` | Node `Readable` of `Row[]` chunks | Use for large newline-delimited results (`JSONEachRow`/`JSONCompactEachRow`/`CSV`/`TSV`); **not** suitable for binary formats like `Parquet` — for those, use `client.exec()` and pipe the raw response stream (see [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance)) | +| `rs.close()` | `void` (synchronous) | Always call if you obtained `stream()` and stop reading early | + +## Common pitfalls + +- **Calling `.json()` on a `JSON` (single-doc) result and expecting an + array.** You get a `ResponseJSON` object; the rows are under + `.data`. Use `JSONEachRow` if you want a flat array. +- **Leaving a `stream()` half-consumed.** This is a top cause of + `ECONNRESET` on the _next_ request — fully iterate the stream or call + `resultSet.close()` (synchronous — no `await`). (Diagnosis details live in the + troubleshooting skill.) +- **Reaching for `.json()` on a CSV/TSV result.** Use `.text()` (or + `.stream()` for large results). diff --git a/skills/clickhouse-js-node-coding/reference/sessions.md b/skills/clickhouse-js-node-coding/reference/sessions.md new file mode 100644 index 000000000..630233755 --- /dev/null +++ b/skills/clickhouse-js-node-coding/reference/sessions.md @@ -0,0 +1,152 @@ +# Sessions and Temporary Tables + +> **Applies to:** all versions. `session_id` is a server-level concept; the +> client just forwards it on every request that names it. + +Backing examples: +[`examples/node/coding/session_id_and_temporary_tables.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_id_and_temporary_tables.ts), +[`examples/node/coding/session_level_commands.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_level_commands.ts). + +## When you need a session + +Use a `session_id` whenever multiple calls must share **server-side state**: + +- `CREATE TEMPORARY TABLE` (the table only exists within its session). +- `SET = ` to apply for subsequent queries on the same + session. +- Any other server feature scoped per session (e.g., session-scoped + variables in newer ClickHouse versions). + +## ⚠️ `session_id` and concurrency + +ClickHouse **rejects concurrent queries within the same session** — if two +requests arrive at the server at the same time sharing the same `session_id`, +the second one gets an error like +`"Session is locked by a concurrent client"`. This has two practical +implications: + +1. **Do not set `session_id` on a global / module-static client** that handles + concurrent requests (e.g., an Express app's shared client). Every + in-flight request would share the same session and collide under load. +2. **If you do set `session_id` on a client**, restrict its concurrency: + set `max_open_connections: 1` so at most one request is in flight at a + time, turning the pool into a serial queue. This is fine for a + dedicated per-workflow client but wrong for a shared application client. + +The right pattern for application code: create a **short-lived client** (or +use per-request `session_id`) scoped to a single logical workflow, not to +the entire process. + +## Per-client `session_id` + +Appropriate when **one client handles exactly one sequential workflow** (a +script, a background job, a single user's session that you've already +serialized). + +```ts +import { createClient } from '@clickhouse/client' +import * as crypto from 'node:crypto' + +const client = createClient({ + session_id: crypto.randomUUID(), + max_open_connections: 1, // prevent concurrent-session errors +}) + +await client.command({ + query: 'CREATE TEMPORARY TABLE temporary_example (i Int32)', +}) + +await client.insert({ + table: 'temporary_example', + values: [{ i: 42 }, { i: 144 }], + format: 'JSONEachRow', +}) + +const rs = await client.query({ + query: 'SELECT * FROM temporary_example', + format: 'JSONEachRow', +}) +console.info(await rs.json()) +await client.close() +``` + +## Session-level `SET` commands + +`SET` only persists within a session. With `session_id` defined on the +client, every subsequent call inherits the change. + +```ts +import { createClient } from '@clickhouse/client' +import * as crypto from 'node:crypto' + +const client = createClient({ + session_id: crypto.randomUUID(), + max_open_connections: 1, // prevent concurrent-session errors +}) + +await client.command({ + query: 'SET output_format_json_quote_64bit_integers = 0', + clickhouse_settings: { wait_end_of_query: 1 }, // ack before next call +}) + +const rs1 = await client.query({ + query: 'SELECT toInt64(42)', + format: 'JSONEachRow', +}) +// → 64-bit integers come back as numbers in this query + +await client.command({ + query: 'SET output_format_json_quote_64bit_integers = 1', + clickhouse_settings: { wait_end_of_query: 1 }, +}) + +const rs2 = await client.query({ + query: 'SELECT toInt64(144)', + format: 'JSONEachRow', +}) +// → 64-bit integers come back as strings again + +await client.close() +``` + +> **`wait_end_of_query: 1` matters here.** Without it, a `SET` on one +> connection in the pool may not yet be applied when the next query lands +> on the same socket. + +## Per-request `session_id` + +You can also pass `session_id` on a single `query()` / `insert()` / +`command()` call to override (or set) it for that one request. + +## ⚠️ Sessions and load balancers / ClickHouse Cloud + +Sessions are bound to a **specific ClickHouse node**. If a load balancer in +front of ClickHouse routes consecutive requests to different nodes, the +temporary table / `SET` won't be visible — you'll get +`UNKNOWN_TABLE` / surprising results. + +Mitigations: + +- Talk to a single node directly. +- For ClickHouse Cloud, use [replica-aware + routing](https://clickhouse.com/docs/manage/replica-aware-routing). +- Avoid sessions for cross-node workflows; persist intermediate state in a + regular (non-temporary) table instead. + +## Common pitfalls + +- **Forgetting `session_id` and being surprised that + `CREATE TEMPORARY TABLE` "disappears."** Without a session, every request + may land on a different connection / server context. +- **Setting `session_id` on a shared application client.** Under concurrent + load, two in-flight requests will share the same session and one will fail + with `"Session is locked by a concurrent client"`. Use per-request + `session_id` or a dedicated short-lived client instead. +- **Reusing the same `session_id` across unrelated workflows.** A second + session-using consumer will trip over your temporary tables and `SET` + values. Generate a fresh UUID per logical session. +- **Leaving session state pinned for the lifetime of the process.** If + long-lived clients accumulate `SET` / temp-table state, consider creating + a short-lived sub-client with its own `session_id` for the unit of work. +- **Skipping `wait_end_of_query: 1` on `SET`** — race conditions between + `SET` and the next query can show up under load. From dbe848697589524a57f41c0e45e7289f1db42940 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 15:30:23 +0200 Subject: [PATCH 04/23] Add Node port of ClickHouse Java's clickhouse-test-runner harness (#706) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Ports `ClickHouse/clickhouse-java/tests/clickhouse-client` to this repo as `tests/clickhouse-test-runner/`, a small Node CLI that wraps `@clickhouse/client` to mimic the upstream `clickhouse-client` binary. With it on `PATH`, the official `tests/clickhouse-test` runner will exercise the JS client against the core ClickHouse SQL test suite. - **Standalone package** (`tests/clickhouse-test-runner/`, not a workspace, only depends on `@clickhouse/client`) with its own `tsconfig`, eslint, and Vitest config. - **`bin/clickhouse`** bash shim — short-circuits `extract-from-config --key listen_host` → `127.0.0.1` (the only key the upstream runner needs), otherwise `exec node dist/main.js`. - **Two backends** selected via `CLICKHOUSE_CLIENT_CLI_IMPL` (mirrors Java's `client`/`jdbc` split): - `client` (default) — `client.exec({ query, clickhouse_settings: { default_format: 'TabSeparated', ... } })` with the response stream piped straight to stdout (raw bytes, no decoding). - `http` — Node `fetch` against `/?default_format=TabSeparated&...` with basic auth, body-streamed to stdout with backpressure. - **Arg parser** (`src/args.ts`) accepting both `--foo bar` and `--foo=bar`, dashed/underscored variants listed as separate canonical entries in `BASE_OPTIONS`, short forms (`-h/-u/-d/-q/-s/-n`), and dynamic long options. Unknown long options are classified against SERVER/CLIENT_ONLY allowlists copied from the Java port: server settings are forwarded, client-only and unknown are silently dropped. Per review feedback, `max_threads` was reclassified from `CLIENT_ONLY_SETTINGS` to `SERVER_SETTINGS` so `--max_threads=N` from the upstream test runner is forwarded to ClickHouse instead of being silently dropped. The unused `aliases` field on `OptionSpec` was removed since the parser never read it (dash/underscore forms work via separate `BASE_OPTIONS` entries). - **Quote-aware multi-query splitter** (`src/split-queries.ts`) — tracks single/double/backtick state and `\`-escapes; only splits on unquoted `;`. - **Logging** to `CLICKHOUSE_CLIENT_CLI_LOG` (default `/tmp/clickhouse-client-cli.log`) with CWD fallback; failures are swallowed so the CLI never breaks tests. - **Exit codes** via `process.exitCode` only — never `process.exit()` — to avoid truncating stdout writes. - **Vitest unit tests** for `split-queries`, `args`, `extract-from-config`, and `log`. Live smoke against `docker compose up -d clickhouse` exercises both backends and `--multiquery` over stdin. Usage: ```bash cd tests/clickhouse-test-runner && npm install && npm run build # Drive the upstream runner (download CH test files; do NOT clone the full repo) CLICKHOUSE_CLIENT_CLI_LOG=./test-run.log \ PATH="$PWD/tests/clickhouse-test-runner/bin:$PATH" \ /path/to/ClickHouse/tests/clickhouse-test 01428_hash_set_nan_key ``` The SERVER/CLIENT_ONLY allowlists are copied from the Java port and will need periodic resync as ClickHouse adds settings. ## Checklist - [x] Unit and integration tests covering the common scenarios were added --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> --- tests/clickhouse-test-runner/.gitignore | 3 + tests/clickhouse-test-runner/README.md | 88 + .../__tests__/args.test.ts | 152 + .../__tests__/extract-from-config.test.ts | 45 + .../__tests__/log.test.ts | 54 + .../__tests__/split-queries.test.ts | 59 + tests/clickhouse-test-runner/bin/clickhouse | 29 + .../clickhouse-test-runner/eslint.config.mjs | 18 + .../clickhouse-test-runner/package-lock.json | 2818 +++++++++++++++++ tests/clickhouse-test-runner/package.json | 32 + tests/clickhouse-test-runner/src/args.ts | 257 ++ .../src/backends/client.ts | 61 + .../src/backends/http.ts | 76 + .../src/extract-from-config.ts | 27 + tests/clickhouse-test-runner/src/log.ts | 42 + tests/clickhouse-test-runner/src/main.ts | 102 + tests/clickhouse-test-runner/src/settings.ts | 162 + .../src/split-queries.ts | 59 + .../tsconfig.build.json | 9 + tests/clickhouse-test-runner/tsconfig.json | 10 + tests/clickhouse-test-runner/vitest.config.ts | 7 + 21 files changed, 4110 insertions(+) create mode 100644 tests/clickhouse-test-runner/.gitignore create mode 100644 tests/clickhouse-test-runner/README.md create mode 100644 tests/clickhouse-test-runner/__tests__/args.test.ts create mode 100644 tests/clickhouse-test-runner/__tests__/extract-from-config.test.ts create mode 100644 tests/clickhouse-test-runner/__tests__/log.test.ts create mode 100644 tests/clickhouse-test-runner/__tests__/split-queries.test.ts create mode 100755 tests/clickhouse-test-runner/bin/clickhouse create mode 100644 tests/clickhouse-test-runner/eslint.config.mjs create mode 100644 tests/clickhouse-test-runner/package-lock.json create mode 100644 tests/clickhouse-test-runner/package.json create mode 100644 tests/clickhouse-test-runner/src/args.ts create mode 100644 tests/clickhouse-test-runner/src/backends/client.ts create mode 100644 tests/clickhouse-test-runner/src/backends/http.ts create mode 100644 tests/clickhouse-test-runner/src/extract-from-config.ts create mode 100644 tests/clickhouse-test-runner/src/log.ts create mode 100644 tests/clickhouse-test-runner/src/main.ts create mode 100644 tests/clickhouse-test-runner/src/settings.ts create mode 100644 tests/clickhouse-test-runner/src/split-queries.ts create mode 100644 tests/clickhouse-test-runner/tsconfig.build.json create mode 100644 tests/clickhouse-test-runner/tsconfig.json create mode 100644 tests/clickhouse-test-runner/vitest.config.ts diff --git a/tests/clickhouse-test-runner/.gitignore b/tests/clickhouse-test-runner/.gitignore new file mode 100644 index 000000000..3c25e1e49 --- /dev/null +++ b/tests/clickhouse-test-runner/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.log diff --git a/tests/clickhouse-test-runner/README.md b/tests/clickhouse-test-runner/README.md new file mode 100644 index 000000000..42447fd9f --- /dev/null +++ b/tests/clickhouse-test-runner/README.md @@ -0,0 +1,88 @@ +# clickhouse-test-runner + +## What this is + +This package is a Node.js port of the Java +[`tests/clickhouse-client`](https://github.com/ClickHouse/clickhouse-java/tree/main/tests/clickhouse-client) +harness from [`ClickHouse/clickhouse-java`](https://github.com/ClickHouse/clickhouse-java). +It wraps [`@clickhouse/client`](../../packages/client-node) in a tiny CLI that +mimics the upstream `clickhouse-client` binary (same flags, same +`extract-from-config` shortcut, same stdin/`--query` behavior) so that the +official ClickHouse Python test runner +([`tests/clickhouse-test`](https://github.com/ClickHouse/ClickHouse/tree/master/tests)) +can drive a subset of the real ClickHouse SQL test suite against this Node.js +client. It lets us see exactly which upstream SQL tests pass or fail when run +through `@clickhouse/client`, without having to reimplement the test runner +itself. + +## Build + +```bash +cd tests/clickhouse-test-runner +npm install +npm run build +``` + +The build emits `dist/main.js`, which is the entry point used by the +`bin/clickhouse` shim. + +## Wrapper executable + +`bin/clickhouse` is a small Bash script that: + +- Handles the `extract-from-config --key …` shortcut directly in shell, since + the upstream Python runner spawns this synchronously during setup and only + ever asks for `listen_host` (we always answer `127.0.0.1`). +- Forwards every other invocation to `node dist/main.js` with the original + arguments. + +To make the official runner use this shim, prepend `bin/` to `PATH` **only in +the shell session that runs the tests** so you don't shadow a real +`clickhouse-client` binary you may have installed system-wide: + +```bash +export PATH="/path/to/clickhouse-js/tests/clickhouse-test-runner/bin:$PATH" +``` + +## Environment variables + +| Variable | Default | Description | +| ---------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- | +| `CLICKHOUSE_CLIENT_CLI_IMPL` | `client` | Backend: `client` (uses `@clickhouse/client`) or `http` (uses Node `fetch` to talk to HTTP 8123). | +| `CLICKHOUSE_CLIENT_CLI_LOG` | `/tmp/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | + +## Examples + +```bash +# Do NOT clone the full ClickHouse repo (too large). Download a zip instead, or +# fetch only the test files you need from +# https://github.com/ClickHouse/ClickHouse/tree/master/tests/queries + +cd ClickHouse-master +CLICKHOUSE_CLIENT_CLI_LOG=./test-run.log \ + PATH="$PATH:/path/to/clickhouse-js/tests/clickhouse-test-runner/bin/" \ + tests/clickhouse-test 01428_hash_set_nan_key + +# Use the raw HTTP backend instead: +CLICKHOUSE_CLIENT_CLI_IMPL=http CLICKHOUSE_CLIENT_CLI_LOG=./test-run.log \ + PATH="$PATH:/path/to/clickhouse-js/tests/clickhouse-test-runner/bin/" \ + tests/clickhouse-test 01428_hash_set_nan_key +``` + +## Local development + +From this directory: + +- `npm run build` — compile TypeScript to `dist/`. +- `npm run typecheck` — run `tsc --noEmit`. +- `npm run lint` — run ESLint with `--max-warnings=0`. +- `npm test` — run the Vitest unit suite (`__tests__/**/*.test.ts`). + +## Status + +This is a developer-facing harness. It is **not** an exhaustive +`clickhouse-client` replacement; only the flags and behaviors that +`tests/clickhouse-test` actually exercises are implemented. The +`SERVER_SETTINGS` and `CLIENT_ONLY_SETTINGS` allowlists in +[`src/settings.ts`](src/settings.ts) are copied from the Java port and may +need to be periodically resynced as ClickHouse adds or reclassifies settings. diff --git a/tests/clickhouse-test-runner/__tests__/args.test.ts b/tests/clickhouse-test-runner/__tests__/args.test.ts new file mode 100644 index 000000000..f5187c504 --- /dev/null +++ b/tests/clickhouse-test-runner/__tests__/args.test.ts @@ -0,0 +1,152 @@ +import { describe, expect, it } from 'vitest' +import { parseArgs } from '../src/args.js' +import { SERVER_SETTINGS } from '../src/settings.js' + +describe('parseArgs', () => { + it('returns defaults for an empty argv', () => { + const args = parseArgs([]) + expect(args.host).toBe('localhost') + expect(args.port).toBe(8123) + expect(args.user).toBe('default') + expect(args.password).toBe('') + expect(args.database).toBe('default') + expect(args.multiquery).toBe(false) + expect(args.secure).toBe(false) + expect(args.query).toBeNull() + expect(args.help).toBe(false) + expect(args.logComment).toBeNull() + expect(args.sendLogsLevel).toBeNull() + expect(args.maxInsertThreads).toBeNull() + expect(args.serverSettings).toEqual({}) + }) + + it('parses long-form options with separate values', () => { + const args = parseArgs([ + '--host', + 'other', + '--port', + '9000', + '--user', + 'u', + '--password', + 'p', + '--database', + 'd', + ]) + expect(args.host).toBe('other') + expect(args.port).toBe(9000) + expect(args.user).toBe('u') + expect(args.password).toBe('p') + expect(args.database).toBe('d') + }) + + it('parses long-form options with = form', () => { + const args = parseArgs([ + '--host=other', + '--port=9000', + '--user=u', + '--password=p', + '--database=d', + ]) + expect(args.host).toBe('other') + expect(args.port).toBe(9000) + expect(args.user).toBe('u') + expect(args.password).toBe('p') + expect(args.database).toBe('d') + }) + + it('parses short options including -q and -s', () => { + const args = parseArgs([ + '-h', + 'other', + '-u', + 'u', + '-d', + 'd', + '-q', + 'SELECT 1', + '-s', + ]) + expect(args.host).toBe('other') + expect(args.user).toBe('u') + expect(args.database).toBe('d') + expect(args.query).toBe('SELECT 1') + expect(args.secure).toBe(true) + }) + + it('accepts both --multiquery and --multi-query', () => { + expect(parseArgs(['--multiquery']).multiquery).toBe(true) + expect(parseArgs(['--multi-query']).multiquery).toBe(true) + }) + + it('silently accepts --multiline and -n', () => { + expect(() => parseArgs(['--multiline'])).not.toThrow() + expect(() => parseArgs(['-n'])).not.toThrow() + const a = parseArgs(['--multiline', '-n']) + expect(a.serverSettings).toEqual({}) + expect(a.query).toBeNull() + }) + + it('sets help=true for --help', () => { + expect(parseArgs(['--help']).help).toBe(true) + }) + + it('accepts both underscore and dash forms for log_comment', () => { + expect(parseArgs(['--log_comment', 'foo']).logComment).toBe('foo') + expect(parseArgs(['--log-comment', 'foo']).logComment).toBe('foo') + }) + + it('accepts both forms for send_logs_level', () => { + expect(parseArgs(['--send_logs_level', 'trace']).sendLogsLevel).toBe( + 'trace', + ) + expect(parseArgs(['--send-logs-level', 'trace']).sendLogsLevel).toBe( + 'trace', + ) + }) + + it('accepts both forms for max_insert_threads', () => { + expect(parseArgs(['--max_insert_threads', '8']).maxInsertThreads).toBe('8') + expect(parseArgs(['--max-insert-threads', '8']).maxInsertThreads).toBe('8') + }) + + it('routes --max_insert_threads=8 into the explicit field, not serverSettings', () => { + const a = parseArgs(['--max_insert_threads=8']) + expect(a.maxInsertThreads).toBe('8') + expect(a.serverSettings).toEqual({}) + }) + + it('exports a non-empty SERVER_SETTINGS allowlist', () => { + expect(SERVER_SETTINGS).toBeInstanceOf(Set) + expect(SERVER_SETTINGS.size).toBeGreaterThan(0) + }) + + it('silently drops unknown options', () => { + const a = parseArgs(['--brand-new-thing=xyz']) + expect(a.serverSettings).toEqual({}) + expect(a.query).toBeNull() + }) + + it('forwards --max_threads=4 as a server setting', () => { + const a = parseArgs(['--max_threads=4']) + expect(a.serverSettings).toEqual({ max_threads: '4' }) + expect(a.query).toBeNull() + }) + + it('silently drops a CLIENT_ONLY setting like --max_block_size=1024', () => { + const a = parseArgs(['--max_block_size=1024']) + expect(a.serverSettings).toEqual({}) + expect(a.query).toBeNull() + }) + + it('does not place --unknown_thing=42 into serverSettings or throw', () => { + expect(() => parseArgs(['--unknown_thing=42'])).not.toThrow() + const a = parseArgs(['--unknown_thing=42']) + expect(a.serverSettings).toEqual({}) + }) + + it('treats --max-threads=4 the same as --max_threads=4 (forwarded as server setting)', () => { + const a = parseArgs(['--max-threads=4']) + expect(a.serverSettings).toEqual({ max_threads: '4' }) + }) +}) diff --git a/tests/clickhouse-test-runner/__tests__/extract-from-config.test.ts b/tests/clickhouse-test-runner/__tests__/extract-from-config.test.ts new file mode 100644 index 000000000..be4dcb3b9 --- /dev/null +++ b/tests/clickhouse-test-runner/__tests__/extract-from-config.test.ts @@ -0,0 +1,45 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { handleExtractFromConfig } from '../src/extract-from-config.js' + +describe('handleExtractFromConfig', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + + function captureStdout(): { writes: string[] } { + const writes: string[] = [] + vi.spyOn(process.stdout, 'write').mockImplementation((( + chunk: string | Uint8Array, + ): boolean => { + writes.push( + typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'), + ) + return true + }) as typeof process.stdout.write) + return { writes } + } + + it('writes 127.0.0.1\\n for --key listen_host', () => { + const { writes } = captureStdout() + handleExtractFromConfig(['--key', 'listen_host']) + expect(writes.join('')).toBe('127.0.0.1\n') + }) + + it('writes 127.0.0.1\\n for --key=listen_host', () => { + const { writes } = captureStdout() + handleExtractFromConfig(['--key=listen_host']) + expect(writes.join('')).toBe('127.0.0.1\n') + }) + + it('writes nothing for --key foo', () => { + const { writes } = captureStdout() + handleExtractFromConfig(['--key', 'foo']) + expect(writes.join('')).toBe('') + }) + + it('writes nothing for empty args', () => { + const { writes } = captureStdout() + handleExtractFromConfig([]) + expect(writes.join('')).toBe('') + }) +}) diff --git a/tests/clickhouse-test-runner/__tests__/log.test.ts b/tests/clickhouse-test-runner/__tests__/log.test.ts new file mode 100644 index 000000000..aad77886d --- /dev/null +++ b/tests/clickhouse-test-runner/__tests__/log.test.ts @@ -0,0 +1,54 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { + mkdtempSync, + readFileSync, + rmSync, + existsSync, + unlinkSync, +} from 'node:fs' +import { EOL } from 'node:os' +import path from 'node:path' +import { appendLog, safeForLog } from '../src/log.js' + +describe('appendLog', () => { + let tmpDir: string + + beforeAll(() => { + tmpDir = mkdtempSync(path.join(process.cwd(), '.test-tmp-cli-log-')) + }) + + afterAll(() => { + rmSync(tmpDir, { recursive: true, force: true }) + }) + + it('appends a line followed by EOL to the given file', () => { + const file = path.join(tmpDir, 'log.txt') + appendLog(file, 'hello') + expect(readFileSync(file, 'utf8')).toBe('hello' + EOL) + }) + + it('does not throw when the path is unwritable', () => { + const fallback = path.resolve(process.cwd(), 'clickhouse-client-cli.log') + const fallbackPreExisting = existsSync(fallback) + expect(() => + appendLog('/dev/null/non_writable_xyz/log', 'should-not-throw'), + ).not.toThrow() + if (!fallbackPreExisting && existsSync(fallback)) { + unlinkSync(fallback) + } + }) +}) + +describe('safeForLog', () => { + it('returns for null', () => { + expect(safeForLog(null)).toBe('') + }) + + it('returns for undefined', () => { + expect(safeForLog(undefined)).toBe('') + }) + + it('returns the original string otherwise', () => { + expect(safeForLog('x')).toBe('x') + }) +}) diff --git a/tests/clickhouse-test-runner/__tests__/split-queries.test.ts b/tests/clickhouse-test-runner/__tests__/split-queries.test.ts new file mode 100644 index 000000000..1cc72a946 --- /dev/null +++ b/tests/clickhouse-test-runner/__tests__/split-queries.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest' +import { splitQueries } from '../src/split-queries.js' + +describe('splitQueries', () => { + it('splits two simple statements', () => { + expect(splitQueries('SELECT 1; SELECT 2')).toEqual(['SELECT 1', 'SELECT 2']) + }) + + it('ignores a trailing semicolon', () => { + expect(splitQueries('SELECT 1;')).toEqual(['SELECT 1']) + }) + + it('handles a single statement without trailing semicolon', () => { + expect(splitQueries('SELECT 1')).toEqual(['SELECT 1']) + }) + + it('returns [] for an empty string', () => { + expect(splitQueries('')).toEqual([]) + }) + + it('returns [] for whitespace-only input', () => { + expect(splitQueries(' \n\t ')).toEqual([]) + }) + + it('does not split on semicolons inside single quotes', () => { + expect(splitQueries("SELECT ';'; SELECT 2")).toEqual([ + "SELECT ';'", + 'SELECT 2', + ]) + }) + + it('does not split on semicolons inside double quotes', () => { + expect(splitQueries('SELECT ";"; SELECT 2')).toEqual([ + 'SELECT ";"', + 'SELECT 2', + ]) + }) + + it('does not split on semicolons inside backticks', () => { + expect(splitQueries('SELECT `a;b`; SELECT 2')).toEqual([ + 'SELECT `a;b`', + 'SELECT 2', + ]) + }) + + it('handles escaped quote inside a single-quoted string', () => { + expect(splitQueries("SELECT '\\''; SELECT 2")).toEqual([ + "SELECT '\\''", + 'SELECT 2', + ]) + }) + + it('trims whitespace around statements', () => { + expect(splitQueries(' SELECT 1 ; SELECT 2 ')).toEqual([ + 'SELECT 1', + 'SELECT 2', + ]) + }) +}) diff --git a/tests/clickhouse-test-runner/bin/clickhouse b/tests/clickhouse-test-runner/bin/clickhouse new file mode 100755 index 000000000..219d9eed5 --- /dev/null +++ b/tests/clickhouse-test-runner/bin/clickhouse @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ENTRYPOINT="${SCRIPT_DIR}/../dist/main.js" + +if [[ "${1:-}" == "extract-from-config" ]]; then + shift + key="" + while [[ $# -gt 0 ]]; do + case "$1" in + --key) key="${2:-}"; shift 2 ;; + --key=*) key="${1#--key=}"; shift ;; + *) shift ;; + esac + done + if [[ "${key}" == "listen_host" ]]; then + echo "127.0.0.1" + fi + exit 0 +fi + +if [[ ! -f "${ENTRYPOINT}" ]]; then + echo "Entry point not found: ${ENTRYPOINT}" >&2 + echo "Build it first: (cd ${SCRIPT_DIR}/.. && npm install && npm run build)" >&2 + exit 1 +fi + +exec node "${ENTRYPOINT}" "$@" diff --git a/tests/clickhouse-test-runner/eslint.config.mjs b/tests/clickhouse-test-runner/eslint.config.mjs new file mode 100644 index 000000000..7f83c97b0 --- /dev/null +++ b/tests/clickhouse-test-runner/eslint.config.mjs @@ -0,0 +1,18 @@ +import js from '@eslint/js' +import { defineConfig } from 'eslint/config' +import tseslint from 'typescript-eslint' +import { + typescriptEslintConfig, + testFilesOverrides, +} from '../../eslint.config.base.mjs' + +export default defineConfig( + js.configs.recommended, + ...tseslint.configs.strict, + ...tseslint.configs.stylistic, + typescriptEslintConfig(import.meta.dirname), + testFilesOverrides(), + { + ignores: ['dist', 'node_modules', 'bin', 'eslint.config.mjs'], + }, +) diff --git a/tests/clickhouse-test-runner/package-lock.json b/tests/clickhouse-test-runner/package-lock.json new file mode 100644 index 000000000..b87e792c7 --- /dev/null +++ b/tests/clickhouse-test-runner/package-lock.json @@ -0,0 +1,2818 @@ +{ + "name": "@clickhouse/clickhouse-test-runner", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@clickhouse/clickhouse-test-runner", + "dependencies": { + "@clickhouse/client": "*" + }, + "bin": { + "clickhouse-js-test-runner": "dist/main.js" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "25.5.0", + "eslint": "^9.39.4", + "eslint-plugin-prettier": "^5.5.5", + "prettier": "3.8.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.0", + "vitest": "^4.0.16" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@clickhouse/client": { + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.18.4.tgz", + "integrity": "sha512-jjCrddI+e2OVXGh/MQY92K9r8Z/iwqaZtUXNI/MfZ/y9VGYwfbQsXRzp4Jv6w4Hgxvr4sLcz9YwIvkCBQ6X/mw==", + "license": "Apache-2.0", + "dependencies": { + "@clickhouse/client-common": "1.18.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@clickhouse/client-common": { + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-1.18.4.tgz", + "integrity": "sha512-kPPtv8yQmplNAxfrAJvwBJq5dd+IWRewEbXSpUvtyEJXlrB8lt/ZH63jUS81Nmd+lK5MRvpOFXPoN3iogkvg+A==", + "license": "Apache-2.0" + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", + "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz", + "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz", + "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", + "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", + "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.128.0", + "@rolldown/pluginutils": "1.0.0-rc.18" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-x64": "1.0.0-rc.18", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", + "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.0-rc.18", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/tests/clickhouse-test-runner/package.json b/tests/clickhouse-test-runner/package.json new file mode 100644 index 000000000..65f4a73a2 --- /dev/null +++ b/tests/clickhouse-test-runner/package.json @@ -0,0 +1,32 @@ +{ + "name": "@clickhouse/clickhouse-test-runner", + "private": true, + "description": "Node.js port of ClickHouse/clickhouse-java tests/clickhouse-client harness", + "engines": { + "node": ">=20.19.0" + }, + "type": "module", + "bin": { + "clickhouse-js-test-runner": "./dist/main.js" + }, + "scripts": { + "build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/main.js", + "typecheck": "tsc --noEmit", + "lint": "eslint --max-warnings=0 .", + "lint:fix": "eslint . --fix", + "test": "vitest run --root ." + }, + "dependencies": { + "@clickhouse/client": "*" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "25.5.0", + "eslint": "^9.39.4", + "eslint-plugin-prettier": "^5.5.5", + "prettier": "3.8.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.0", + "vitest": "^4.0.16" + } +} diff --git a/tests/clickhouse-test-runner/src/args.ts b/tests/clickhouse-test-runner/src/args.ts new file mode 100644 index 000000000..72b1e5b29 --- /dev/null +++ b/tests/clickhouse-test-runner/src/args.ts @@ -0,0 +1,257 @@ +import { classifySetting } from './settings.js' + +export interface ParsedArgs { + host: string + port: number + user: string + password: string + database: string + secure: boolean + query: string | null + logComment: string | null + sendLogsLevel: string | null + maxInsertThreads: string | null + multiquery: boolean + help: boolean + serverSettings: Record + rawArgv: string[] +} + +interface OptionSpec { + long: string + short?: string + hasArg: boolean +} + +const BASE_OPTIONS: readonly OptionSpec[] = [ + { long: 'host', short: 'h', hasArg: true }, + { long: 'port', hasArg: true }, + { long: 'user', short: 'u', hasArg: true }, + { long: 'password', hasArg: true }, + { long: 'database', short: 'd', hasArg: true }, + { long: 'query', short: 'q', hasArg: true }, + { long: 'log_comment', hasArg: true }, + { long: 'log-comment', hasArg: true }, + { long: 'send_logs_level', hasArg: true }, + { long: 'send-logs-level', hasArg: true }, + { long: 'max_insert_threads', hasArg: true }, + { long: 'max-insert-threads', hasArg: true }, + { long: 'secure', short: 's', hasArg: false }, + { long: 'multiline', short: 'n', hasArg: false }, + { long: 'multiquery', hasArg: false }, + { long: 'multi-query', hasArg: false }, + { long: 'help', hasArg: false }, +] + +export const KNOWN_LONG_OPTIONS: ReadonlySet = new Set( + BASE_OPTIONS.map((o) => o.long), +) + +const SHORT_TO_LONG: ReadonlyMap = new Map( + BASE_OPTIONS.filter((o) => o.short !== undefined).map((o) => [ + o.short as string, + o.long, + ]), +) + +const LONG_HAS_ARG: ReadonlyMap = new Map( + BASE_OPTIONS.map((o) => [o.long, o.hasArg]), +) + +const USAGE_TEXT = [ + 'usage: clickhouse-client [options]', + '', + 'Known server settings are forwarded to ClickHouse.', + 'Client-only and unknown settings are accepted but not sent to server.', + 'If --query is not specified, the query is read from stdin.', + '', + 'Options:', + ' -h, --host HOST Server host (default: localhost)', + ' --port PORT HTTP port (default: 8123)', + ' -u, --user USER Username (default: default)', + ' --password PASSWORD Password (default: empty)', + ' -d, --database DB Database (default: default)', + ' -q, --query SQL SQL query to execute', + ' --log_comment VALUE Comment for query_log records', + ' --send_logs_level VALUE Server log level to send with result', + ' --max_insert_threads VALUE', + ' Max insert threads setting', + ' -s, --secure Use HTTPS', + ' -n, --multiline (ignored, accepted for compatibility)', + ' --multiquery Execute multiple ";"-separated queries', + ' --help Print this help', + '', + 'Environment variables:', + " CLICKHOUSE_CLIENT_CLI_IMPL Backend implementation: 'client' (default) or 'http'", + ' CLICKHOUSE_CLIENT_CLI_LOG Path to log file for troubleshooting', + '', +].join('\n') + +export function printUsage( + stream: NodeJS.WritableStream = process.stdout, +): void { + stream.write(USAGE_TEXT) +} + +export function parseArgs(argv: string[]): ParsedArgs { + const parsed: ParsedArgs = { + host: 'localhost', + port: 8123, + user: 'default', + password: '', + database: 'default', + secure: false, + query: null, + logComment: null, + sendLogsLevel: null, + maxInsertThreads: null, + multiquery: false, + help: false, + serverSettings: {}, + rawArgv: [...argv], + } + + // Map of canonical long option -> value (string) or true for flags. + const seen = new Map() + + let i = 0 + while (i < argv.length) { + const arg = argv[i] + if (arg === undefined) { + i++ + continue + } + if (arg === '--') { + break + } + + if (arg.startsWith('--')) { + const eq = arg.indexOf('=') + const name = eq >= 0 ? arg.substring(2, eq) : arg.substring(2) + const inlineValue = eq >= 0 ? arg.substring(eq + 1) : undefined + + if (name.length === 0) { + i++ + continue + } + + if (LONG_HAS_ARG.has(name)) { + const hasArg = LONG_HAS_ARG.get(name) === true + if (!hasArg) { + seen.set(name, true) + i++ + continue + } + let value: string + if (inlineValue !== undefined) { + value = inlineValue + } else { + const next = argv[i + 1] + if (next === undefined) { + // Missing required arg: skip silently to mirror lenient behavior. + i++ + continue + } + value = next + i++ + } + seen.set(name, value) + i++ + continue + } + + // Dynamic / unknown long option. Optional arg. + let value: string | undefined = inlineValue + if (value === undefined) { + const next = argv[i + 1] + if (next !== undefined) { + const isNegativeNumber = + /^-(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(next) + const isKnownLongOption = (() => { + if (!next.startsWith('--')) return false + const nextEq = next.indexOf('=') + const nextName = + nextEq >= 0 ? next.substring(2, nextEq) : next.substring(2) + return nextName.length > 0 && LONG_HAS_ARG.has(nextName) + })() + + if (!next.startsWith('-') || isNegativeNumber || !isKnownLongOption) { + value = next + i++ + } + } + } + const settingName = name.split('-').join('_') + if (classifySetting(settingName) === 'server') { + parsed.serverSettings[settingName] = value ?? '1' + } + // CLIENT_ONLY / UNKNOWN: silently dropped. + i++ + continue + } + + if (arg.startsWith('-') && arg.length > 1) { + // Short option (single-char). We do not bundle. + const shortName = arg.substring(1) + const longName = SHORT_TO_LONG.get(shortName) + if (longName === undefined) { + i++ + continue + } + const hasArg = LONG_HAS_ARG.get(longName) === true + if (!hasArg) { + seen.set(longName, true) + i++ + continue + } + const next = argv[i + 1] + if (next === undefined) { + i++ + continue + } + seen.set(longName, next) + i += 2 + continue + } + + // Positional argument: ignored. + i++ + } + + const firstNonNull = (...names: string[]): string | null => { + for (const n of names) { + const v = seen.get(n) + if (typeof v === 'string') { + return v + } + } + return null + } + + const hostVal = firstNonNull('host') + if (hostVal !== null) parsed.host = hostVal + const portVal = firstNonNull('port') + if (portVal !== null) { + const n = Number.parseInt(portVal, 10) + if (!Number.isNaN(n)) parsed.port = n + } + const userVal = firstNonNull('user') + if (userVal !== null) parsed.user = userVal + const passwordVal = firstNonNull('password') + if (passwordVal !== null) parsed.password = passwordVal + const databaseVal = firstNonNull('database') + if (databaseVal !== null) parsed.database = databaseVal + parsed.query = firstNonNull('query') + parsed.logComment = firstNonNull('log_comment', 'log-comment') + parsed.sendLogsLevel = firstNonNull('send_logs_level', 'send-logs-level') + parsed.maxInsertThreads = firstNonNull( + 'max_insert_threads', + 'max-insert-threads', + ) + parsed.secure = seen.get('secure') === true + parsed.multiquery = + seen.get('multiquery') === true || seen.get('multi-query') === true + parsed.help = seen.get('help') === true + + return parsed +} diff --git a/tests/clickhouse-test-runner/src/backends/client.ts b/tests/clickhouse-test-runner/src/backends/client.ts new file mode 100644 index 000000000..73a708019 --- /dev/null +++ b/tests/clickhouse-test-runner/src/backends/client.ts @@ -0,0 +1,61 @@ +import { pipeline } from 'node:stream/promises' +import { createClient } from '@clickhouse/client' +import type { ParsedArgs } from '../args.js' +import { appendLog } from '../log.js' + +export interface BackendOptions { + args: ParsedArgs + queries: string[] + logPath: string +} + +function buildClickHouseSettings( + args: ParsedArgs, +): Record { + const settings: Record = {} + settings['default_format'] = 'TabSeparated' + if (args.logComment !== null && args.logComment.length > 0) { + settings['log_comment'] = args.logComment + } + if (args.sendLogsLevel !== null && args.sendLogsLevel.length > 0) { + settings['send_logs_level'] = args.sendLogsLevel + } + if (args.maxInsertThreads !== null && args.maxInsertThreads.length > 0) { + settings['max_insert_threads'] = args.maxInsertThreads + } + for (const [k, v] of Object.entries(args.serverSettings)) { + settings[k] = v + } + return settings +} + +export async function executeWithClient(opts: BackendOptions): Promise { + const { args, queries, logPath } = opts + const proto = args.secure ? 'https' : 'http' + const url = `${proto}://${args.host}:${args.port}` + const client = createClient({ + url, + username: args.user, + password: args.password, + database: args.database, + }) + + const clickhouse_settings = buildClickHouseSettings(args) + + try { + for (const q of queries) { + appendLog(logPath, 'executing_query=' + q) + const result = await client.exec({ + query: q, + clickhouse_settings, + }) + await pipeline(result.stream, process.stdout, { end: false }) + } + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + appendLog(logPath, 'error=' + msg) + throw err + } finally { + await client.close() + } +} diff --git a/tests/clickhouse-test-runner/src/backends/http.ts b/tests/clickhouse-test-runner/src/backends/http.ts new file mode 100644 index 000000000..9db162a2e --- /dev/null +++ b/tests/clickhouse-test-runner/src/backends/http.ts @@ -0,0 +1,76 @@ +import { Buffer } from 'node:buffer' +import type { ParsedArgs } from '../args.js' +import { appendLog } from '../log.js' + +export interface BackendOptions { + args: ParsedArgs + queries: string[] + logPath: string +} + +function buildUrl(args: ParsedArgs): string { + const proto = args.secure ? 'https' : 'http' + const params = new URLSearchParams() + params.set('database', args.database) + params.set('default_format', 'TabSeparated') + if (args.logComment !== null && args.logComment.length > 0) { + params.set('log_comment', args.logComment) + } + if (args.sendLogsLevel !== null && args.sendLogsLevel.length > 0) { + params.set('send_logs_level', args.sendLogsLevel) + } + if (args.maxInsertThreads !== null && args.maxInsertThreads.length > 0) { + params.set('max_insert_threads', args.maxInsertThreads) + } + for (const [k, v] of Object.entries(args.serverSettings)) { + params.set(k, v) + } + return `${proto}://${args.host}:${args.port}/?${params.toString()}` +} + +async function writeChunk(chunk: Uint8Array): Promise { + await new Promise((resolve, reject) => { + const ok = process.stdout.write(chunk, (err) => { + if (err) reject(err) + else if (ok) resolve() + }) + if (!ok) { + process.stdout.once('drain', () => { + resolve() + }) + } + }) +} + +export async function executeWithHttp(opts: BackendOptions): Promise { + const { args, queries, logPath } = opts + const url = buildUrl(args) + const auth = Buffer.from(`${args.user}:${args.password}`).toString('base64') + const headers: Record = { + Authorization: `Basic ${auth}`, + 'Content-Type': 'text/plain; charset=utf-8', + } + + try { + for (const q of queries) { + appendLog(logPath, 'executing_query=' + q) + const response = await fetch(url, { + method: 'POST', + headers, + body: q, + }) + if (!response.ok) { + const text = await response.text() + throw new Error(`HTTP ${response.status}: ${text.trim()}`) + } + if (response.body === null) continue + for await (const chunk of response.body) { + await writeChunk(chunk) + } + } + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + appendLog(logPath, 'error=' + msg) + throw err + } +} diff --git a/tests/clickhouse-test-runner/src/extract-from-config.ts b/tests/clickhouse-test-runner/src/extract-from-config.ts new file mode 100644 index 000000000..4a1d65ce4 --- /dev/null +++ b/tests/clickhouse-test-runner/src/extract-from-config.ts @@ -0,0 +1,27 @@ +export function handleExtractFromConfig(args: string[]): void { + let key: string | null = null + + for (let i = 0; i < args.length; i++) { + const arg = args[i] + if (arg === undefined) continue + + if (arg.startsWith('--key=')) { + key = arg.substring('--key='.length) + continue + } + if (arg === '--key') { + const next = args[i + 1] + if (next !== undefined) { + key = next + i++ + } + continue + } + } + + if (key === 'listen_host') { + process.stdout.write('127.0.0.1\n') + } + + process.exitCode = 0 +} diff --git a/tests/clickhouse-test-runner/src/log.ts b/tests/clickhouse-test-runner/src/log.ts new file mode 100644 index 000000000..47fb8700c --- /dev/null +++ b/tests/clickhouse-test-runner/src/log.ts @@ -0,0 +1,42 @@ +import { appendFileSync, mkdirSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { EOL } from 'node:os' + +const DEFAULT_LOG_PATH = '/tmp/clickhouse-client-cli.log' +const FALLBACK_LOG_FILENAME = 'clickhouse-client-cli.log' + +export function resolveLogPath(): string { + const fromEnv = process.env.CLICKHOUSE_CLIENT_CLI_LOG + if (fromEnv !== undefined && fromEnv.trim().length > 0) { + return fromEnv + } + return DEFAULT_LOG_PATH +} + +function tryAppend(path: string, payload: string): boolean { + try { + const parent = dirname(path) + if (parent.length > 0) { + mkdirSync(parent, { recursive: true }) + } + appendFileSync(path, payload, 'utf8') + return true + } catch { + return false + } +} + +export function appendLog(path: string, line: string): void { + const payload = line + EOL + if (tryAppend(path, payload)) { + return + } + const fallback = resolve(process.cwd(), FALLBACK_LOG_FILENAME) + if (fallback !== path) { + tryAppend(fallback, payload) + } +} + +export function safeForLog(value: string | null | undefined): string { + return value === null || value === undefined ? '' : value +} diff --git a/tests/clickhouse-test-runner/src/main.ts b/tests/clickhouse-test-runner/src/main.ts new file mode 100644 index 000000000..bba57e57b --- /dev/null +++ b/tests/clickhouse-test-runner/src/main.ts @@ -0,0 +1,102 @@ +#!/usr/bin/env node +import { readFileSync } from 'node:fs' +import { parseArgs, printUsage } from './args.js' +import { appendLog, resolveLogPath, safeForLog } from './log.js' +import { splitQueries } from './split-queries.js' +import { handleExtractFromConfig } from './extract-from-config.js' +import { executeWithClient } from './backends/client.js' +import { executeWithHttp } from './backends/http.js' + +async function main(): Promise { + const argv = process.argv.slice(2) + + if (argv[0] === 'extract-from-config') { + handleExtractFromConfig(argv.slice(1)) + return + } + + const logPath = resolveLogPath() + appendLog(logPath, '=== clickhouse-client invocation ===') + appendLog(logPath, 'timestamp=' + new Date().toISOString()) + appendLog(logPath, 'argv=' + JSON.stringify(argv)) + + let args + try { + args = parseArgs(argv) + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + process.stderr.write('Error: ' + msg + '\n') + printUsage(process.stderr) + process.exitCode = 1 + return + } + + if (args.help) { + printUsage(process.stdout) + return + } + + let query: string | null = args.query + if (query === null) { + try { + query = readFileSync(0, 'utf8') + } catch { + query = null + } + } + + if (query === null || query.trim().length === 0) { + process.stderr.write( + 'No query provided. Use --query or pipe SQL via stdin.\n', + ) + process.exitCode = 1 + return + } + + const queries = args.multiquery ? splitQueries(query) : [query.trim()] + if (queries.length === 0) { + process.stderr.write( + 'No query provided. Use --query or pipe SQL via stdin.\n', + ) + process.exitCode = 1 + return + } + + const impl = process.env.CLICKHOUSE_CLIENT_CLI_IMPL ?? 'client' + if (impl !== 'client' && impl !== 'http') { + process.stderr.write( + `Unknown CLICKHOUSE_CLIENT_CLI_IMPL value: ${impl}. Supported: client, http\n`, + ) + process.exitCode = 1 + return + } + + appendLog(logPath, 'impl=' + impl) + appendLog(logPath, 'database=' + args.database) + appendLog(logPath, 'user=' + args.user) + appendLog(logPath, 'secure=' + String(args.secure)) + appendLog(logPath, 'multiquery=' + String(args.multiquery)) + appendLog(logPath, 'log_comment=' + safeForLog(args.logComment)) + appendLog(logPath, 'send_logs_level=' + safeForLog(args.sendLogsLevel)) + appendLog(logPath, 'max_insert_threads=' + safeForLog(args.maxInsertThreads)) + appendLog(logPath, 'server_settings=' + JSON.stringify(args.serverSettings)) + appendLog(logPath, 'queries_count=' + String(queries.length)) + for (const q of queries) { + appendLog(logPath, 'query=' + q) + } + + try { + if (impl === 'client') { + await executeWithClient({ args, queries, logPath }) + } else { + await executeWithHttp({ args, queries, logPath }) + } + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + appendLog(logPath, 'error=' + msg) + process.stderr.write('Error: ' + msg + '\n') + process.exitCode = 1 + } +} + +void main() diff --git a/tests/clickhouse-test-runner/src/settings.ts b/tests/clickhouse-test-runner/src/settings.ts new file mode 100644 index 000000000..b00ade99c --- /dev/null +++ b/tests/clickhouse-test-runner/src/settings.ts @@ -0,0 +1,162 @@ +export type SettingScope = 'server' | 'client_only' | 'unknown' + +export const SERVER_SETTINGS: ReadonlySet = new Set([ + 'max_insert_threads', + 'max_threads', + 'send_logs_level', +]) + +export const CLIENT_ONLY_SETTINGS: ReadonlySet = new Set([ + 'group_by_two_level_threshold', + 'group_by_two_level_threshold_bytes', + 'distributed_aggregation_memory_efficient', + 'fsync_metadata', + 'output_format_parallel_formatting', + 'input_format_parallel_parsing', + 'min_chunk_bytes_for_parallel_parsing', + 'max_read_buffer_size', + 'prefer_localhost_replica', + 'max_block_size', + 'max_joined_block_size_rows', + 'joined_block_split_single_row', + 'join_output_by_rowlist_perkey_rows_threshold', + 'optimize_append_index', + 'use_hedged_requests', + 'optimize_if_chain_to_multiif', + 'optimize_if_transform_strings_to_enum', + 'optimize_read_in_order', + 'optimize_or_like_chain', + 'optimize_substitute_columns', + 'enable_multiple_prewhere_read_steps', + 'read_in_order_two_level_merge_threshold', + 'optimize_aggregation_in_order', + 'aggregation_in_order_max_block_bytes', + 'use_uncompressed_cache', + 'min_bytes_to_use_direct_io', + 'min_bytes_to_use_mmap_io', + 'local_filesystem_read_method', + 'remote_filesystem_read_method', + 'local_filesystem_read_prefetch', + 'filesystem_cache_segments_batch_size', + 'read_from_filesystem_cache_if_exists_otherwise_bypass_cache', + 'throw_on_error_from_cache_on_write_operations', + 'remote_filesystem_read_prefetch', + 'distributed_cache_discard_connection_if_unread_data', + 'distributed_cache_use_clients_cache_for_write', + 'distributed_cache_use_clients_cache_for_read', + 'allow_prefetched_read_pool_for_remote_filesystem', + 'filesystem_prefetch_max_memory_usage', + 'filesystem_prefetches_limit', + 'filesystem_prefetch_min_bytes_for_single_read_task', + 'filesystem_prefetch_step_marks', + 'filesystem_prefetch_step_bytes', + 'enable_filesystem_cache', + 'enable_filesystem_cache_on_write_operations', + 'compile_expressions', + 'compile_aggregate_expressions', + 'compile_sort_description', + 'merge_tree_coarse_index_granularity', + 'optimize_distinct_in_order', + 'max_bytes_before_remerge_sort', + 'min_compress_block_size', + 'max_compress_block_size', + 'merge_tree_compact_parts_min_granules_to_multibuffer_read', + 'optimize_sorting_by_input_stream_properties', + 'http_response_buffer_size', + 'http_wait_end_of_query', + 'enable_memory_bound_merging_of_aggregation_results', + 'min_count_to_compile_expression', + 'min_count_to_compile_aggregate_expression', + 'min_count_to_compile_sort_description', + 'session_timezone', + 'use_page_cache_for_disks_without_file_cache', + 'use_page_cache_for_local_disks', + 'use_page_cache_for_object_storage', + 'page_cache_inject_eviction', + 'merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability', + 'prefer_external_sort_block_bytes', + 'cross_join_min_rows_to_compress', + 'cross_join_min_bytes_to_compress', + 'min_external_table_block_size_bytes', + 'max_parsing_threads', + 'optimize_functions_to_subcolumns', + 'parallel_replicas_local_plan', + 'query_plan_join_swap_table', + 'enable_vertical_final', + 'optimize_extract_common_expressions', + 'optimize_syntax_fuse_functions', + 'use_async_executor_for_materialized_views', + 'use_query_condition_cache', + 'secondary_indices_enable_bulk_filtering', + 'use_skip_indexes_if_final', + 'use_skip_indexes_on_data_read', + 'optimize_rewrite_like_perfect_affix', + 'input_format_parquet_use_native_reader_v3', + 'enable_lazy_columns_replication', + 'allow_special_serialization_kinds_in_output_formats', + 'short_circuit_function_evaluation_for_nulls_threshold', + 'automatic_parallel_replicas_mode', + 'temporary_files_buffer_size', + 'query_plan_optimize_join_order_algorithm', + 'max_bytes_before_external_sort', + 'max_bytes_before_external_group_by', + 'max_bytes_ratio_before_external_sort', + 'max_bytes_ratio_before_external_group_by', + 'allow_repeated_settings', + 'use_skip_indexes_if_final_exact_mode', + 'ratio_of_defaults_for_sparse_serialization', + 'prefer_fetch_merged_part_size_threshold', + 'vertical_merge_algorithm_min_rows_to_activate', + 'vertical_merge_algorithm_min_columns_to_activate', + 'allow_vertical_merges_from_compact_to_wide_parts', + 'min_merge_bytes_to_use_direct_io', + 'index_granularity_bytes', + 'merge_max_block_size', + 'index_granularity', + 'min_bytes_for_wide_part', + 'compress_marks', + 'compress_primary_key', + 'marks_compress_block_size', + 'primary_key_compress_block_size', + 'replace_long_file_name_to_hash', + 'max_file_name_length', + 'min_bytes_for_full_part_storage', + 'compact_parts_max_bytes_to_buffer', + 'compact_parts_max_granules_to_buffer', + 'compact_parts_merge_max_bytes_to_prefetch_part', + 'cache_populated_by_fetch', + 'concurrent_part_removal_threshold', + 'old_parts_lifetime', + 'prewarm_mark_cache', + 'use_const_adaptive_granularity', + 'enable_index_granularity_compression', + 'enable_block_number_column', + 'enable_block_offset_column', + 'use_primary_key_cache', + 'prewarm_primary_key_cache', + 'object_serialization_version', + 'object_shared_data_serialization_version', + 'object_shared_data_serialization_version_for_zero_level_parts', + 'object_shared_data_buckets_for_compact_part', + 'object_shared_data_buckets_for_wide_part', + 'dynamic_serialization_version', + 'auto_statistics_types', + 'serialization_info_version', + 'string_serialization_version', + 'nullable_serialization_version', + 'enable_shared_storage_snapshot_in_query', + 'min_columns_to_activate_adaptive_write_buffer', + 'reduce_blocking_parts_sleep_ms', + 'shared_merge_tree_outdated_parts_group_size', + 'shared_merge_tree_max_outdated_parts_to_process_at_once', +]) + +export function classifySetting(name: string): SettingScope { + if (SERVER_SETTINGS.has(name)) { + return 'server' + } + if (CLIENT_ONLY_SETTINGS.has(name)) { + return 'client_only' + } + return 'unknown' +} diff --git a/tests/clickhouse-test-runner/src/split-queries.ts b/tests/clickhouse-test-runner/src/split-queries.ts new file mode 100644 index 000000000..afe09b1f8 --- /dev/null +++ b/tests/clickhouse-test-runner/src/split-queries.ts @@ -0,0 +1,59 @@ +export function splitQueries(sql: string): string[] { + const queries: string[] = [] + let current = '' + + let inSingleQuote = false + let inDoubleQuote = false + let inBacktick = false + let escaping = false + + for (let i = 0; i < sql.length; i++) { + const ch = sql.charAt(i) + + if (escaping) { + current += ch + escaping = false + continue + } + + if ((inSingleQuote || inDoubleQuote) && ch === '\\') { + current += ch + escaping = true + continue + } + + if (!inDoubleQuote && !inBacktick && ch === "'") { + inSingleQuote = !inSingleQuote + current += ch + continue + } + if (!inSingleQuote && !inBacktick && ch === '"') { + inDoubleQuote = !inDoubleQuote + current += ch + continue + } + if (!inSingleQuote && !inDoubleQuote && ch === '`') { + inBacktick = !inBacktick + current += ch + continue + } + + if (!inSingleQuote && !inDoubleQuote && !inBacktick && ch === ';') { + const statement = current.trim() + if (statement.length > 0) { + queries.push(statement) + } + current = '' + continue + } + + current += ch + } + + const trailing = current.trim() + if (trailing.length > 0) { + queries.push(trailing) + } + + return queries +} diff --git a/tests/clickhouse-test-runner/tsconfig.build.json b/tests/clickhouse-test-runner/tsconfig.build.json new file mode 100644 index 000000000..7bbac0b24 --- /dev/null +++ b/tests/clickhouse-test-runner/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*.ts"] +} diff --git a/tests/clickhouse-test-runner/tsconfig.json b/tests/clickhouse-test-runner/tsconfig.json new file mode 100644 index 000000000..946ab4c87 --- /dev/null +++ b/tests/clickhouse-test-runner/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "noEmit": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "__tests__/**/*.ts", "vitest.config.ts"] +} diff --git a/tests/clickhouse-test-runner/vitest.config.ts b/tests/clickhouse-test-runner/vitest.config.ts new file mode 100644 index 000000000..422de8509 --- /dev/null +++ b/tests/clickhouse-test-runner/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['__tests__/**/*.test.ts'], + }, +}) From 4949fbd40b2549445b86f91e0ab5d8e36e94d86d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 11:36:17 +0200 Subject: [PATCH 05/23] Fix CI: drop unrecognized `--print-time` flag from upstream-sql-tests (#708) Fix CI errors from #707. - [x] Remove `--print-time` from workflow + README example (it isn't a valid `tests/clickhouse-test` flag) - [x] Remove unused `clickhouse_version` `workflow_dispatch` input from `upstream-sql-tests.yml` - [x] Remove unused `UPSTREAM_REF` env var from `upstream-sql-tests.yml` - [x] Install `jinja2` in `upstream-sql-tests.yml` - [x] Fix `MaxListenersExceededWarning` in `client` backend by replacing `pipeline(result.stream, process.stdout, { end: false })` with `for await (const chunk of result.stream)` so no error/close listeners accumulate on stdout - [x] Keep `--trace-warnings` enabled in `tests/clickhouse-test-runner/bin/clickhouse` so any future Node warnings include their stack traces in CI logs --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/upstream-sql-tests.yml | 113 ++++++++++++++++++ AGENTS.md | 7 ++ CONTRIBUTING.md | 4 + tests/clickhouse-test-runner/.gitignore | 1 + tests/clickhouse-test-runner/README.md | 67 ++++++++--- tests/clickhouse-test-runner/bin/clickhouse | 2 +- .../scripts/run-upstream-tests.sh | 66 ++++++++++ .../src/backends/client.ts | 5 +- .../upstream-allowlist.txt | 12 ++ 9 files changed, 256 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/upstream-sql-tests.yml create mode 100755 tests/clickhouse-test-runner/scripts/run-upstream-tests.sh create mode 100644 tests/clickhouse-test-runner/upstream-allowlist.txt diff --git a/.github/workflows/upstream-sql-tests.yml b/.github/workflows/upstream-sql-tests.yml new file mode 100644 index 000000000..ec7a8c16b --- /dev/null +++ b/.github/workflows/upstream-sql-tests.yml @@ -0,0 +1,113 @@ +name: 'upstream-sql-tests' + +permissions: {} + +on: + workflow_dispatch: + inputs: + upstream_ref: + description: 'ClickHouse/ClickHouse ref to check out' + required: false + default: 'master' + type: string + schedule: + - cron: '0 5 * * *' + push: + branches: + - main + paths: + - 'tests/clickhouse-test-runner/**' + - '.github/workflows/upstream-sql-tests.yml' + pull_request: + paths: + - 'tests/clickhouse-test-runner/**' + - '.github/workflows/upstream-sql-tests.yml' + +concurrency: + group: '${{ github.workflow }}-${{ github.ref }}' + cancel-in-progress: true + +env: + UPSTREAM_REPO: 'ClickHouse/ClickHouse' + +jobs: + upstream-sql-tests: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + impl: [client, http] + clickhouse: [head, latest] + steps: + - name: Checkout clickhouse-js + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Checkout ClickHouse upstream + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ env.UPSTREAM_REPO }} + ref: ${{ github.event.inputs.upstream_ref || 'master' }} + path: tests/clickhouse-test-runner/.upstream/ClickHouse + sparse-checkout: | + tests/clickhouse-test + tests/queries + tests/config + tests/ci + tests/performance + docker/test/util + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 24 + + - name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: '3.12' + + - name: Install Python dependencies for upstream clickhouse-test + run: | + python -m pip install --upgrade pip + pip install jinja2 + + - name: Start ClickHouse (version - ${{ matrix.clickhouse }}) in Docker + uses: isbang/compose-action@3846bcd61da338e9eaaf83e7ed0234a12b099b72 # v2.4.2 + env: + CLICKHOUSE_VERSION: ${{ matrix.clickhouse }} + with: + compose-file: 'docker-compose.yml' + down-flags: '--volumes' + + - name: Build test runner + working-directory: tests/clickhouse-test-runner + run: | + npm install + npm run build + + - name: Make upstream test script executable + run: | + chmod +x tests/clickhouse-test-runner/.upstream/ClickHouse/tests/clickhouse-test + + - name: Run upstream SQL tests + id: run-tests + env: + CLICKHOUSE_CLIENT_CLI_IMPL: ${{ matrix.impl }} + CLICKHOUSE_CLIENT_CLI_LOG: ${{ github.workspace }}/upstream-run.log + run: | + bash tests/clickhouse-test-runner/scripts/run-upstream-tests.sh --no-stateful + + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: upstream-sql-tests-${{ matrix.impl }}-${{ matrix.clickhouse }} + retention-days: 14 + if-no-files-found: ignore + path: | + upstream-run.log + tests/clickhouse-test-runner/.upstream/ClickHouse/tests/queries/**/*.stdout + tests/clickhouse-test-runner/.upstream/ClickHouse/tests/queries/**/*.stderr + tests/clickhouse-test-runner/.upstream/ClickHouse/tests/queries/**/*.diff diff --git a/AGENTS.md b/AGENTS.md index 0bc76ebdc..0b5c40c67 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,13 @@ The goals of the refactor are: a duplicated example, update **all** copies. The current duplicates and their primary locations are listed in [`examples/README.md`](examples/README.md#editing-duplicated-examples). +## Upstream SQL test harness + +The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) harness is a Node.js port of `clickhouse-client` that allows the official ClickHouse Python test runner (`tests/clickhouse-test`) to drive a subset of the upstream SQL test suite against `@clickhouse/client`. + +- Refer to [`tests/clickhouse-test-runner/README.md`](tests/clickhouse-test-runner/README.md) for build, usage, and environment-variable documentation. The curated list of passing tests is maintained in [`upstream-allowlist.txt`](tests/clickhouse-test-runner/upstream-allowlist.txt). +- When the harness's behavior changes—such as new wrapper flags, new short-circuited keys in `bin/clickhouse`, or new entries in the `SERVER_SETTINGS`/`CLIENT_ONLY_SETTINGS` allowlists in [`src/settings.ts`](tests/clickhouse-test-runner/src/settings.ts)—review the README and [`.github/workflows/upstream-sql-tests.yml`](.github/workflows/upstream-sql-tests.yml) to ensure they stay synchronized with the implementation. + ## When reviewing code changes For every pull request review, make sure to provide an evaluation of the following aspects: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6e78c842..781c97516 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -203,3 +203,7 @@ The average reported test coverage is above 90%. We generally aim towards this t Currently, automatic coverage reports are disabled. See [#177](https://github.com/ClickHouse/clickhouse-js/issues/177), as it should be restored in the scope of that issue. + +## Running upstream ClickHouse SQL tests + +The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) directory contains a Node.js port of `clickhouse-client` that lets `tests/clickhouse-test` from `ClickHouse/ClickHouse` exercise the JS client against the upstream SQL test suite. This harness helps validate that `@clickhouse/client` behaves correctly against real ClickHouse tests. See the [clickhouse-test-runner README](tests/clickhouse-test-runner/README.md) for setup and usage instructions. diff --git a/tests/clickhouse-test-runner/.gitignore b/tests/clickhouse-test-runner/.gitignore index 3c25e1e49..f690e4f90 100644 --- a/tests/clickhouse-test-runner/.gitignore +++ b/tests/clickhouse-test-runner/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ *.log +.upstream/ diff --git a/tests/clickhouse-test-runner/README.md b/tests/clickhouse-test-runner/README.md index 42447fd9f..d9c2ab9bf 100644 --- a/tests/clickhouse-test-runner/README.md +++ b/tests/clickhouse-test-runner/README.md @@ -46,29 +46,60 @@ export PATH="/path/to/clickhouse-js/tests/clickhouse-test-runner/bin:$PATH" ## Environment variables -| Variable | Default | Description | -| ---------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- | -| `CLICKHOUSE_CLIENT_CLI_IMPL` | `client` | Backend: `client` (uses `@clickhouse/client`) or `http` (uses Node `fetch` to talk to HTTP 8123). | -| `CLICKHOUSE_CLIENT_CLI_LOG` | `/tmp/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | +| Variable | Default | Description | +| ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| `CLICKHOUSE_CLIENT_CLI_IMPL` | `client` | Backend: `client` (uses `@clickhouse/client`) or `http` (uses Node `fetch` to talk to HTTP 8123). | +| `CLICKHOUSE_CLIENT_CLI_LOG` | `tests/clickhouse-test-runner/.upstream/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | +| `UPSTREAM_CLICKHOUSE_DIR` | `tests/clickhouse-test-runner/.upstream/ClickHouse` | Path to a checkout of `ClickHouse/ClickHouse` containing the upstream test suite. | +| `UPSTREAM_TEST_LIST` | `tests/clickhouse-test-runner/upstream-allowlist.txt` | Path to a file listing the upstream tests to run (one test name per line, `#` for comments). | -## Examples +## Running against the upstream test suite + +To avoid a full multi-GB clone of `ClickHouse/ClickHouse`, use a sparse + shallow clone: + +```bash +git clone --depth 1 --filter=blob:none --sparse https://github.com/ClickHouse/ClickHouse.git +cd ClickHouse +git sparse-checkout set tests/clickhouse-test tests/queries tests/config tests/ci +``` + +Then run the helper script from the `clickhouse-js` repository: + +```bash +cd /path/to/clickhouse-js +UPSTREAM_CLICKHOUSE_DIR=/path/to/ClickHouse \ + tests/clickhouse-test-runner/scripts/run-upstream-tests.sh +``` + +The helper script reads the tests listed in `upstream-allowlist.txt` and runs them through the wrapper. It honors the environment variables documented in the [Environment variables](#environment-variables) table above, including `UPSTREAM_CLICKHOUSE_DIR` and `UPSTREAM_TEST_LIST`. + +Extra positional arguments are forwarded to `tests/clickhouse-test`. For example, to skip the stateful tests: ```bash -# Do NOT clone the full ClickHouse repo (too large). Download a zip instead, or -# fetch only the test files you need from -# https://github.com/ClickHouse/ClickHouse/tree/master/tests/queries - -cd ClickHouse-master -CLICKHOUSE_CLIENT_CLI_LOG=./test-run.log \ - PATH="$PATH:/path/to/clickhouse-js/tests/clickhouse-test-runner/bin/" \ - tests/clickhouse-test 01428_hash_set_nan_key - -# Use the raw HTTP backend instead: -CLICKHOUSE_CLIENT_CLI_IMPL=http CLICKHOUSE_CLIENT_CLI_LOG=./test-run.log \ - PATH="$PATH:/path/to/clickhouse-js/tests/clickhouse-test-runner/bin/" \ - tests/clickhouse-test 01428_hash_set_nan_key +UPSTREAM_CLICKHOUSE_DIR=/path/to/ClickHouse \ + tests/clickhouse-test-runner/scripts/run-upstream-tests.sh --no-stateful ``` +To toggle the backend implementation, set `CLICKHOUSE_CLIENT_CLI_IMPL`: + +```bash +CLICKHOUSE_CLIENT_CLI_IMPL=http \ + tests/clickhouse-test-runner/scripts/run-upstream-tests.sh +``` + +### Extending the allowlist + +The file `upstream-allowlist.txt` contains the curated list of upstream tests known to pass through this harness. The file format is one test name per line; lines starting with `#` and blank lines are ignored. + +**Rule of thumb:** Only add tests that pass on both the `client` and `http` backends. Remove or comment out tests that begin to flake. + +### CI + +The workflow `.github/workflows/upstream-sql-tests.yml` runs this harness in CI: + +- Triggered on **workflow_dispatch**, **nightly at 05:00 UTC**, and on **pushes/PRs** that touch `tests/clickhouse-test-runner/**` or the workflow file itself. +- The `workflow_dispatch` input `upstream_ref` can be used to pin a specific upstream commit or branch (defaults to `master`). + ## Local development From this directory: diff --git a/tests/clickhouse-test-runner/bin/clickhouse b/tests/clickhouse-test-runner/bin/clickhouse index 219d9eed5..c29419f56 100755 --- a/tests/clickhouse-test-runner/bin/clickhouse +++ b/tests/clickhouse-test-runner/bin/clickhouse @@ -26,4 +26,4 @@ if [[ ! -f "${ENTRYPOINT}" ]]; then exit 1 fi -exec node "${ENTRYPOINT}" "$@" +exec node --trace-warnings "${ENTRYPOINT}" "$@" diff --git a/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh new file mode 100755 index 000000000..15f005459 --- /dev/null +++ b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Resolve the runner directory (parent of scripts/) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RUNNER_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +# Read environment variables with defaults +UPSTREAM_CLICKHOUSE_DIR="${UPSTREAM_CLICKHOUSE_DIR:-${RUNNER_DIR}/.upstream/ClickHouse}" +CLICKHOUSE_CLIENT_CLI_IMPL="${CLICKHOUSE_CLIENT_CLI_IMPL:-}" +CLICKHOUSE_CLIENT_CLI_LOG="${CLICKHOUSE_CLIENT_CLI_LOG:-${RUNNER_DIR}/.upstream/clickhouse-client-cli.log}" +UPSTREAM_TEST_LIST="${UPSTREAM_TEST_LIST:-${RUNNER_DIR}/upstream-allowlist.txt}" + +# Build the runner if needed +if [[ ! -f "${RUNNER_DIR}/dist/main.js" ]]; then + echo "Building clickhouse-test-runner..." >&2 + (cd "$RUNNER_DIR" && npm install && npm run build) +fi + +# Verify upstream ClickHouse directory +if [[ ! -x "${UPSTREAM_CLICKHOUSE_DIR}/tests/clickhouse-test" ]]; then + echo "Error: ${UPSTREAM_CLICKHOUSE_DIR}/tests/clickhouse-test not found or not executable." >&2 + echo "Set UPSTREAM_CLICKHOUSE_DIR to point to a checkout of ClickHouse/ClickHouse." >&2 + exit 1 +fi + +# Read allowlist into array, skipping comments and blank lines. +# Leading/trailing whitespace is trimmed so test names are passed cleanly +# to tests/clickhouse-test even if the allowlist file is hand-edited. +tests=() +while IFS= read -r line || [[ -n "$line" ]]; do + # Trim leading whitespace + line="${line#"${line%%[![:space:]]*}"}" + # Trim trailing whitespace + line="${line%"${line##*[![:space:]]}"}" + # Skip blank lines and comments + [[ -z "${line}" ]] && continue + [[ "${line}" == \#* ]] && continue + tests+=("${line}") +done < "${UPSTREAM_TEST_LIST}" + +echo "Selected ${#tests[@]} test(s) from ${UPSTREAM_TEST_LIST}" >&2 + +if [[ ${#tests[@]} -eq 0 ]]; then + if [[ "${ALLOW_EMPTY_UPSTREAM_ALLOWLIST:-0}" != "1" ]]; then + echo "Error: no tests were selected from ${UPSTREAM_TEST_LIST}." >&2 + echo "Refusing to run tests/clickhouse-test without explicit test names because an empty allowlist can run a large upstream suite." >&2 + echo "If this is intentional, rerun with ALLOW_EMPTY_UPSTREAM_ALLOWLIST=1." >&2 + exit 1 + fi + echo "Warning: no tests were selected from ${UPSTREAM_TEST_LIST}; continuing because ALLOW_EMPTY_UPSTREAM_ALLOWLIST=1." >&2 +fi + +# Ensure log file directory exists +mkdir -p "$(dirname "${CLICKHOUSE_CLIENT_CLI_LOG}")" + +# Export environment for the wrapper +export PATH="${RUNNER_DIR}/bin:${PATH}" +export CLICKHOUSE_CLIENT_CLI_LOG +if [[ -n "${CLICKHOUSE_CLIENT_CLI_IMPL}" ]]; then + export CLICKHOUSE_CLIENT_CLI_IMPL +fi + +# Run the upstream test runner +cd "${UPSTREAM_CLICKHOUSE_DIR}" +exec python3 tests/clickhouse-test "${tests[@]}" "$@" diff --git a/tests/clickhouse-test-runner/src/backends/client.ts b/tests/clickhouse-test-runner/src/backends/client.ts index 73a708019..82749e4a6 100644 --- a/tests/clickhouse-test-runner/src/backends/client.ts +++ b/tests/clickhouse-test-runner/src/backends/client.ts @@ -1,4 +1,3 @@ -import { pipeline } from 'node:stream/promises' import { createClient } from '@clickhouse/client' import type { ParsedArgs } from '../args.js' import { appendLog } from '../log.js' @@ -49,7 +48,9 @@ export async function executeWithClient(opts: BackendOptions): Promise { query: q, clickhouse_settings, }) - await pipeline(result.stream, process.stdout, { end: false }) + for await (const chunk of result.stream) { + process.stdout.write(chunk) + } } } catch (err) { const msg = err instanceof Error ? err.message : String(err) diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt new file mode 100644 index 000000000..a514bfc03 --- /dev/null +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -0,0 +1,12 @@ +# Upstream ClickHouse SQL tests known/expected to pass through @clickhouse/client +# via the tests/clickhouse-test-runner harness. +# +# Conventions: +# - One test name per line (matches the pattern argument of tests/clickhouse-test). +# - Lines starting with '#' and blank lines are ignored. +# - Add tests here once they reliably pass on both `client` and `http` backends. +# - Remove (or comment out with a reason) tests that begin to flake. +# +# To extend this list, see tests/clickhouse-test-runner/README.md. + +01428_hash_set_nan_key From 50d1ed23274e001bca44285af7173170d58c6205 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 12:08:48 +0200 Subject: [PATCH 06/23] Tag the latest release in the publish workflow (#711) ## Summary The `publish` workflow pushes packages to npm but doesn't create a corresponding git tag, leaving the repo's tag history out of sync with published `latest` releases. Updates `.github/workflows/publish.yml` so the manually-dispatched `latest` job tags the released commit after a successful npm publish. The `head` job (auto-publish on every merge to `main`) is intentionally untouched. - **Tagging step**: reads the version from `packages/client-common/package.json`, then creates and pushes an annotated tag in the existing `X.Y.Z` style (e.g. `1.18.2`). Skips idempotently if the tag already exists, so re-running a dispatch is safe. - **Permissions**: scopes `contents: write` to the `latest` job only, preserving the workflow-level least-privilege defaults (`contents: read`, `id-token: write`). ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/publish.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6f6c5c3be..39ff21382 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -72,6 +72,9 @@ jobs: latest: if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + permissions: + contents: write # Required to push the release git tag + id-token: write # Required for npm OIDC authentication and provenance steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -86,9 +89,11 @@ jobs: run: npm ci - name: Get the release version + id: version run: | BASE_VERSION=$(node -p "require('./packages/client-common/package.json').version") echo "Using version: $BASE_VERSION" + echo "version=$BASE_VERSION" >> "$GITHUB_OUTPUT" - name: Build packages run: npm --workspaces run build @@ -98,3 +103,16 @@ jobs: npm --workspaces publish \ --access public \ --provenance + + - name: Create and push release git tag + env: + RELEASE_VERSION: ${{ steps.version.outputs.version }} + run: | + if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_VERSION}" >/dev/null 2>&1; then + echo "Tag ${RELEASE_VERSION} already exists on origin; skipping." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "${RELEASE_VERSION}" -m "Release ${RELEASE_VERSION}" + git push origin "refs/tags/${RELEASE_VERSION}" From d4c2efe5ee83907669a124de8484b26f4ab7a519 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 12:20:15 +0200 Subject: [PATCH 07/23] Expand upstream SQL test allowlist (#710) ## Summary Continues #708 by growing `tests/clickhouse-test-runner/upstream-allowlist.txt` from 1 to 36 upstream ClickHouse stateless SQL tests. Each added test was verified to pass through the harness on both backends (`client`, `http`) and both server matrix versions (`head`, `latest`). Coverage spans: - Basic `SELECT`, parser numerics, `reinterpretAsString` - Array functions, `ARRAY JOIN`, nested columns, `arrayEnumerateUniq` - `GROUP BY` / `WITH TOTALS` / `HAVING`, `DISTINCT` in subqueries - `FixedString` conversions, higher-order function constants, `argMin`/`argMax` - `IN` / set, ORDER BY descending on (Fixed)String - `ANY`/`ALL` `LEFT`/`INNER` joins on numbers, strings, mixed types ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> --- .../upstream-allowlist.txt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index a514bfc03..46bc94a95 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -9,4 +9,39 @@ # # To extend this list, see tests/clickhouse-test-runner/README.md. +00001_select_1 +00003_reinterpret_as_string +00007_array +00008_array_join +00009_array_join_subquery +00012_array_join_alias_2 +00013_create_table_with_arrays +00014_select_from_table_with_nested +00015_totals_having_constants +00016_totals_having_constants +00018_distinct_in_subquery +00022_func_higher_order_and_constants +00023_agg_select_agg_subquery +00025_implicitly_used_subquery_column +00027_argMinMax +00031_parser_number +00032_fixed_string_to_string +00033_fixed_string_to_string +00034_fixed_string_to_number +00035_function_array_return_type +00036_array_element +00040_array_enumerate_uniq +00041_aggregation_remap +00041_big_array_join +00042_set +00044_sorting_by_string_descending +00045_sorting_by_fixed_string_descending +00049_any_left_join +00050_any_left_join +00051_any_inner_join +00052_all_left_join +00053_all_inner_join +00054_join_string +00055_join_two_numbers +00056_join_number_string 01428_hash_set_nan_key From c636f66567da65cd50d46291c3de2b517e54b288 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 12:57:34 +0200 Subject: [PATCH 08/23] Add integration test for streaming a RowBinary payload via the request body (#709) - [x] Add a Node.js integration test that streams a RowBinary payload as the request body via `client.exec` (`node_stream_row_binary.test.ts`) - [x] Add a mirror Node.js integration test that reads the same set of values via `client.exec` with `FORMAT RowBinary` and decodes them from the response stream (`node_stream_row_binary_select.test.ts`) - [x] Verify lint, typecheck, and the new test pass against a local ClickHouse --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../node_stream_row_binary.test.ts | 106 ++++++++++++++++++ .../node_stream_row_binary_select.test.ts | 102 +++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 packages/client-node/__tests__/integration/node_stream_row_binary.test.ts create mode 100644 packages/client-node/__tests__/integration/node_stream_row_binary_select.test.ts diff --git a/packages/client-node/__tests__/integration/node_stream_row_binary.test.ts b/packages/client-node/__tests__/integration/node_stream_row_binary.test.ts new file mode 100644 index 000000000..b59663d0d --- /dev/null +++ b/packages/client-node/__tests__/integration/node_stream_row_binary.test.ts @@ -0,0 +1,106 @@ +import { + ClickHouseLogLevel, + DefaultLogger, + LogWriter, + type ClickHouseClient, +} from '@clickhouse/client-common' +import { describe, it, beforeEach, afterEach, expect } from 'vitest' +import { createSimpleTable } from '@test/fixtures/simple_table' +import { createTestClient } from '@test/utils/client' +import { guid } from '@test/utils/guid' +import Stream from 'stream' +import { drainStreamInternal } from '../../src/connection/stream' + +describe('[Node.js] stream RowBinary insert', () => { + let client: ClickHouseClient + let tableName: string + let log_writer: LogWriter + + beforeEach(async () => { + client = createTestClient() + log_writer = new LogWriter( + new DefaultLogger(), + 'Connection', + ClickHouseLogLevel.OFF, + ) + tableName = `test_node_row_binary_stream_${guid()}` + await createSimpleTable(client, tableName) + }) + afterEach(async () => { + await client.close() + }) + + it('should send a RowBinary payload via a stream passed into the request body', async () => { + // Schema: id UInt64, name String, sku Array(UInt8) + // RowBinary encoding: + // UInt64 -> 8 bytes little-endian + // String -> varint length prefix + UTF-8 bytes + // Array(T) -> varint length prefix + items + const row1 = Buffer.concat([ + uint64LE(42n), + varString('foo'), + varUInt8Array([1, 2]), + ]) + const row2 = Buffer.concat([ + uint64LE(43n), + varString('bar'), + varUInt8Array([3, 4]), + ]) + + // Provide the payload via a Readable stream split across multiple chunks + // to exercise the streaming code path on the request body. + const stream = Stream.Readable.from([row1, row2], { objectMode: false }) + + const execResult = await client.exec({ + query: `INSERT INTO ${tableName} FORMAT RowBinary`, + values: stream, + }) + // The result stream contains nothing useful for an insert and should be + // immediately drained to release the socket. + await drainStreamInternal( + { + op: 'Insert', + query_id: execResult.query_id, + log_writer, + log_level: ClickHouseLogLevel.OFF, + }, + execResult.stream, + ) + + const rs = await client.query({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC`, + format: 'JSONEachRow', + }) + expect(await rs.json()).toEqual([ + { id: '42', name: 'foo', sku: [1, 2] }, + { id: '43', name: 'bar', sku: [3, 4] }, + ]) + }) +}) + +function uint64LE(value: bigint): Buffer { + const buf = Buffer.alloc(8) + buf.writeBigUInt64LE(value) + return buf +} + +// LEB128 unsigned varint, used by ClickHouse for length prefixes in RowBinary. +function varUInt(value: number): Buffer { + const bytes: number[] = [] + let v = value + while (v >= 0x80) { + bytes.push((v & 0x7f) | 0x80) + v >>>= 7 + } + bytes.push(v & 0x7f) + return Buffer.from(bytes) +} + +function varString(value: string): Buffer { + const data = Buffer.from(value, 'utf8') + return Buffer.concat([varUInt(data.length), data]) +} + +function varUInt8Array(values: number[]): Buffer { + return Buffer.concat([varUInt(values.length), Buffer.from(values)]) +} diff --git a/packages/client-node/__tests__/integration/node_stream_row_binary_select.test.ts b/packages/client-node/__tests__/integration/node_stream_row_binary_select.test.ts new file mode 100644 index 000000000..6152f4968 --- /dev/null +++ b/packages/client-node/__tests__/integration/node_stream_row_binary_select.test.ts @@ -0,0 +1,102 @@ +import type { ClickHouseClient } from '@clickhouse/client-common' +import { describe, it, beforeEach, afterEach, expect } from 'vitest' +import { createSimpleTable } from '@test/fixtures/simple_table' +import { createTestClient } from '@test/utils/client' +import { guid } from '@test/utils/guid' +import type Stream from 'stream' + +describe('[Node.js] stream RowBinary select', () => { + let client: ClickHouseClient + let tableName: string + + beforeEach(async () => { + client = createTestClient() + tableName = `test_node_row_binary_select_stream_${guid()}` + await createSimpleTable(client, tableName) + await client.insert({ + table: tableName, + values: [ + { id: '42', name: 'foo', sku: [1, 2] }, + { id: '43', name: 'bar', sku: [3, 4] }, + ], + format: 'JSONEachRow', + }) + }) + afterEach(async () => { + await client.close() + }) + + it('should read a RowBinary payload from the response stream', async () => { + // Schema: id UInt64, name String, sku Array(UInt8) + const { stream } = await client.exec({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC FORMAT RowBinary`, + }) + + const chunks: Buffer[] = [] + for await (const chunk of stream) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) + } + const payload = Buffer.concat(chunks) + + // RowBinary decoding: + // UInt64 -> 8 bytes little-endian + // String -> varint length prefix + UTF-8 bytes + // Array(T) -> varint length prefix + items + const reader = new BufferReader(payload) + const rows: Array<{ id: string; name: string; sku: number[] }> = [] + while (!reader.eof()) { + const id = reader.readUInt64LE().toString() + const name = reader.readString() + const sku = reader.readUInt8Array() + rows.push({ id, name, sku }) + } + expect(reader.eof()).toBe(true) + expect(rows).toEqual([ + { id: '42', name: 'foo', sku: [1, 2] }, + { id: '43', name: 'bar', sku: [3, 4] }, + ]) + }) +}) + +class BufferReader { + private offset = 0 + constructor(private readonly buf: Buffer) {} + + eof(): boolean { + return this.offset >= this.buf.length + } + + readUInt64LE(): bigint { + const value = this.buf.readBigUInt64LE(this.offset) + this.offset += 8 + return value + } + + // LEB128 unsigned varint, used by ClickHouse for length prefixes in RowBinary. + readVarUInt(): number { + let value = 0 + let shift = 0 + while (true) { + const byte = this.buf[this.offset++] + value |= (byte & 0x7f) << shift + if ((byte & 0x80) === 0) { + return value >>> 0 + } + shift += 7 + } + } + + readString(): string { + const length = this.readVarUInt() + const value = this.buf.toString('utf8', this.offset, this.offset + length) + this.offset += length + return value + } + + readUInt8Array(): number[] { + const length = this.readVarUInt() + const slice = this.buf.subarray(this.offset, this.offset + length) + this.offset += length + return Array.from(slice) + } +} From c10068eb4eae871e88916882ea4844a52aa41f31 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 20:18:35 +0200 Subject: [PATCH 09/23] =?UTF-8?q?Expand=20upstream=20SQL=20test=20allowlis?= =?UTF-8?q?t=20(+342=20tests,=20batches=201=E2=80=936)=20(#712)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continues #708 / #710 by growing `tests/clickhouse-test-runner/upstream-allowlist.txt` from 36 → 424 entries through the Node.js harness, validated on both `client` and `http` backends. - [x] Set up upstream ClickHouse sparse checkout, install Python deps, build the test runner, start ClickHouse via docker-compose - [x] Verify the existing 36-test allowlist passes through the harness on both `client` and `http` backends - [x] Build the per-batch driver (add 100, run both backends with `--no-stateful --no-long`, keep passers, drop failures and skips) - [x] Run batches 1-6 (600 candidates evaluated; 342 passed on both `client` and `http` backends and were kept; 257 failures and 1 skip dropped); allowlist 36 → 378 - [x] Fix CI failures from prior commit: drop 54 entries from the allowlist that either failed in the CI matrix (`{client,http} × {head,latest}`) or that act as **prefix matches** for failing tests (clickhouse-test treats positional args as substring/prefix matches, so e.g. `00396_uuid` was unintentionally pulling in the failing `00396_uuid_v7`). Net: allowlist now 424 entries (+388 vs the pre-#710 baseline of 36) - [x] Document the harness purpose and the batch-add strategy (including the prefix-match gotcha) in `AGENTS.md` so future agent runs have the context - [x] PR ready for review --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> --- AGENTS.md | 20 +- .../upstream-allowlist.txt | 388 ++++++++++++++++++ 2 files changed, 406 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0b5c40c67..7bb3ab286 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,8 +65,24 @@ The goals of the refactor are: The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) harness is a Node.js port of `clickhouse-client` that allows the official ClickHouse Python test runner (`tests/clickhouse-test`) to drive a subset of the upstream SQL test suite against `@clickhouse/client`. -- Refer to [`tests/clickhouse-test-runner/README.md`](tests/clickhouse-test-runner/README.md) for build, usage, and environment-variable documentation. The curated list of passing tests is maintained in [`upstream-allowlist.txt`](tests/clickhouse-test-runner/upstream-allowlist.txt). -- When the harness's behavior changes—such as new wrapper flags, new short-circuited keys in `bin/clickhouse`, or new entries in the `SERVER_SETTINGS`/`CLIENT_ONLY_SETTINGS` allowlists in [`src/settings.ts`](tests/clickhouse-test-runner/src/settings.ts)—review the README and [`.github/workflows/upstream-sql-tests.yml`](.github/workflows/upstream-sql-tests.yml) to ensure they stay synchronized with the implementation. +### What the harness does + +- Wraps `@clickhouse/client` in a tiny CLI (`bin/clickhouse` → `dist/main.js`) that mimics enough of the upstream `clickhouse-client` binary (same flags, `extract-from-config` shortcut, stdin/`--query` behavior) for the Python `tests/clickhouse-test` runner to drive it without modification. +- Two backend implementations selectable via `CLICKHOUSE_CLIENT_CLI_IMPL`: `client` (uses `@clickhouse/client`) and `http` (raw `fetch` to port 8123). The CI matrix runs both against ClickHouse `latest` and `head` so that we cover both code paths and detect server regressions. +- Reads the curated test list from [`upstream-allowlist.txt`](tests/clickhouse-test-runner/upstream-allowlist.txt) (one test name per line, `#` for comments) and forwards them as positional arguments to `tests/clickhouse-test`. +- The `SERVER_SETTINGS`/`CLIENT_ONLY_SETTINGS` allowlists in [`src/settings.ts`](tests/clickhouse-test-runner/src/settings.ts) are copied from the Java port and may need periodic resync as ClickHouse adds or reclassifies settings. + +See [`tests/clickhouse-test-runner/README.md`](tests/clickhouse-test-runner/README.md) for build, usage, and environment-variable documentation. When harness behavior changes (new wrapper flags, new short-circuited keys in `bin/clickhouse`, new entries in the settings allowlists), review the README and [`.github/workflows/upstream-sql-tests.yml`](.github/workflows/upstream-sql-tests.yml) to keep them in sync with the implementation. + +### Strategy for growing the allowlist + +The allowlist is grown in **batches of ~100 candidate tests at a time**, in upstream filename order, following this loop: + +1. **Pre-filter the candidate batch.** Skip non-SQL tests (`.sh`, `.py`, `.j2`) and tests tagged for unsupported infrastructure (`shard`, `distributed`, `replicated`, `zookeeper`, `kafka`, `s3`, `mysql`, `tls`, etc.). These will never pass through this harness as it stands today. +2. **Run each candidate against both backends** (`CLICKHOUSE_CLIENT_CLI_IMPL=client` and `=http`) using the harness, with `--no-stateful --no-long`. **Only keep tests that report `[ OK ]` on both backends**; drop failures and skips. +3. **Validate against the CI matrix before committing**, not just one local server version. The CI workflow runs `{client, http} × {ClickHouse latest, head}` — a test that passes locally on `head` may fail on `latest` (or vice versa) and break CI. +4. **Beware substring/prefix expansion.** `tests/clickhouse-test` treats positional arguments as **substring/prefix matches** rather than exact names, so an allowlist entry like `00396_uuid` will silently pull in `00396_uuid_v7`, `00712_prewhere_with_alias` will pull in `00712_prewhere_with_alias_bug_2`, etc. When adding an entry whose name is a prefix of any other test in `0_stateless`, prefer the longest unambiguous form, or accept that the siblings come along and verify they all pass on both backends. +5. **Prune flakes promptly.** If a previously-passing test starts to flake on the nightly run, remove it (or its prefix-expanded siblings) from the allowlist rather than retrying — the allowlist exists to be a stable green signal, not a TODO list. ## When reviewing code changes diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index 46bc94a95..58614e7ac 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -45,3 +45,391 @@ 00055_join_two_numbers 00056_join_number_string 01428_hash_set_nan_key +00136_duplicate_order_by_elems +00137_in_constants +00138_table_aliases +00140_parse_unix_timestamp_as_datetime +00142_parse_timestamp_as_datetime +00143_number_classification_functions +00144_empty_regexp +00145_empty_likes +00149_function_url_hash +00151_tuple_with_array +00152_totals_in_subquery +00156_array_map_to_constant +00157_aliases_and_lambda_formal_parameters +00159_whitespace_in_columns_list +00160_merge_and_index_in_in +00164_not_chain +00165_transform_non_const_default +00167_settings_inside_query +00169_join_constant_keys +00172_constexprs_in_set +00173_compare_date_time_with_constant_string +00174_compare_date_time_with_constant_string_in_in +00175_if_num_arrays +00175_partition_by_ignore +00176_if_string_arrays +00178_query_datetime64_index +00179_lambdas_with_common_expressions_and_filter +00180_attach_materialized_view +00185_array_literals +00187_like_regexp_prefix +00188_constants_as_arguments_of_aggregate_functions +00190_non_constant_array_of_constant_data +00192_least_greatest +00194_identity +00196_float32_formatting +00197_if_fixed_string +00198_group_by_empty_arrays +00201_array_uniq +00202_cross_join +00204_extract_url_parameter +00205_emptyscalar_subquery_type_mismatch_bug +00206_empty_array_to_single +00207_left_array_join +00208_agg_state_merge +00209_insert_select_extremes +00216_bit_test_function_family +00218_like_regexp_newline +00219_full_right_join_column_order +00222_sequence_aggregate_function_family +00227_quantiles_timing_arbitrary_order +00230_array_functions_has_count_equal_index_of_non_const_second_arg +00231_format_vertical_raw +00232_format_readable_decimal_size +00232_format_readable_size +00233_position_function_sql_comparibilty +00234_disjunctive_equality_chains_optimization +00237_group_by_arrays +00238_removal_of_temporary_columns +00239_type_conversion_in_in +00240_replace_substring_loop +00250_tuple_comparison +00251_has_types +00254_tuple_extremes +00255_array_concat_string +00256_reverse +00258_materializing_tuples +00263_merge_aggregates_and_overflow +00264_uniq_many_args +00266_read_overflow_mode +00267_tuple_array_access_operators_priority +00268_aliases_without_as_keyword +00269_database_table_whitespace +00270_views_query_processing_stage +00271_agg_state_and_totals +00272_union_all_and_in_subquery +00277_array_filter +00280_hex_escape_sequence +00283_column_cut +00287_column_const_with_nan +00288_empty_stripelog +00291_array_reduce +00292_parser_tuple_element +00296_url_parameters +00299_stripe_log_multiple_inserts +00300_csv +00306_insert_values_and_expressions +00312_position_case_insensitive_utf8 +00315_quantile_off_by_one +00316_rounding_functions_and_empty_block +00317_in_tuples_and_out_of_range_values +00320_between +00323_quantiles_timing_bug +00330_view_subqueries +00331_final_and_prewhere_condition_ver_column +00333_parser_number_bug +00334_column_aggregate_function_limit +00342_escape_sequences +00343_array_element_generic +00346_if_tuple +00347_has_tuple +00348_tuples +00349_visible_width +00350_count_distinct +00351_select_distinct_arrays_tuples +00353_join_by_tuple +00355_array_of_non_const_convertible_types +00356_analyze_aggregations_and_union_all +00357_to_string_complex_types +00358_from_string_complex_types +00359_convert_or_zero_functions +00360_to_date_from_string_with_datetime +00362_great_circle_distance +00364_java_style_denormals +00367_visible_width_of_array_tuple_enum +00369_int_div_of_float +00371_union_all +00373_group_by_tuple +00374_any_last_if_merge +00381_first_significant_subdomain +00388_enum_with_totals +00389_concat_operator +00393_if_with_constant_condition +00397_tsv_format_synonym +00399_group_uniq_array_date_datetime +00401_merge_and_stripelog +00402_nan_and_extremes +00403_to_start_of_day +00404_null_literal +00406_tuples_with_nulls +00413_least_greatest_new_behavior +00414_time_zones_direct_conversion +00420_null_in_scalar_subqueries +00422_hash_function_constexpr +00423_storage_log_single_thread +00425_count_nullable +00426_nulls_sorting +00429_point_in_ellipses +00431_if_nulls +00433_ifnull +00434_tonullable +00435_coalesce +00436_fixed_string_16_comparisons +00437_nulls_first_last +00438_bit_rotate +00439_fixed_string_filter +00441_nulls_in +00442_filter_by_nullable +00445_join_nullable_keys +00447_foreach_modifier +00448_to_string_cut_to_zero +00449_filter_array_nullable_tuple +00450_higher_order_and_nullable +00451_left_array_join_and_constants +00452_left_array_join_and_nullable +00453_top_k +00457_log_tinylog_stripelog_nullable +00459_group_array_insert_at +00461_default_value_of_argument_type +00464_array_element_out_of_range +00464_sort_all_constant_columns +00465_nullable_default +00466_comments_in_keyword +00468_array_join_multiple_arrays_and_use_original_column +00469_comparison_of_strings_containing_null_char +00470_identifiers_in_double_quotes +00471_sql_style_quoting +00472_compare_uuid_with_constant_string +00472_create_view_if_not_exists +00475_in_join_db_table +00477_parsing_data_types +00479_date_and_datetime_to_number +00480_mac_addresses +00481_create_view_for_null +00482_subqueries_and_aliases +00483_cast_syntax +00486_if_fixed_string +00487_if_array_fixed_string +00488_column_name_primary +00488_non_ascii_column_names +00490_with_select +00498_array_functions_concat_slice_push_pop +00498_bitwise_aggregate_functions +00500_point_in_polygon_2d_const +00500_point_in_polygon_3d_const +00500_point_in_polygon_bug_2 +00500_point_in_polygon_nan +00500_point_in_polygon_non_const_poly +00502_custom_partitioning_local +00502_string_concat_with_array +00503_cast_const_nullable +00507_sumwithoverflow +00511_get_size_of_enum +00513_fractional_time_zones +00516_is_inf_nan +00516_modulo +00517_date_parsing +00518_extract_all_and_empty_matches +00520_tuple_values_interpreter +00521_multidimensional +00522_multidimensional +00523_aggregate_functions_in_group_array +00524_time_intervals_months_underflow +00525_aggregate_functions_of_nullable_that_return_non_nullable +00526_array_join_with_arrays_of_nullable +00527_totals_having_nullable +00528_const_of_nullable +00529_orantius +00530_arrays_of_nothing +00532_topk_generic +00533_uniq_array +00534_exp10 +00535_parse_float_scientific +00537_quarters +00538_datediff +00538_datediff_plural_units +00539_functions_for_working_with_json +00541_kahan_sum +00541_to_start_of_fifteen_minutes +00544_agg_foreach_of_two_arg +00544_insert_with_select +00545_weird_aggregate_functions +00547_named_tuples +00548_slice_of_nested +00551_parse_or_null +00552_logical_functions_simple +00552_logical_functions_ternary +00552_logical_functions_uint8_as_bool +00552_or_nullable +00553_buff_exists_materlized_column +00553_invalid_nested_name +00554_nested_and_table_engines +00555_right_join_excessive_rows +00556_array_intersect +00556_remove_columns_from_subquery +00557_alter_null_storage_tables +00558_parse_floats +00559_filter_array_generic +00562_in_subquery_merge_tree +00562_rewrite_select_expression_with_union +00566_enum_min_max +00568_empty_function_with_fixed_string +00570_empty_array_is_const +00571_alter_nullable +00576_nested_and_prewhere +00578_merge_table_and_table_virtual_column +00578_merge_trees_without_primary_key +00579_merge_tree_partition_and_primary_keys_using_same_expression +00580_cast_nullable_to_non_nullable +00582_not_aliasing_functions +00583_limit_by_expressions +00585_union_all_subquery_aggregation_column_removal +00587_union_all_type_conversions +00589_removal_unused_columns_aggregation +00590_limit_by_column_removal +00591_columns_removal_union_all +00592_union_all_different_aliases +00593_union_all_assert_columns_removed +00597_with_totals_on_empty_set +00599_create_view_with_subquery +00603_system_parts_nonexistent_database +00605_intersections_aggregate_functions +00606_quantiles_and_nans +00607_index_in_in +00608_uniq_array +00609_prewhere_and_default +00612_count +00612_union_query_with_subquery +00617_array_in +00618_nullable_in +00619_union_highlite +00622_select_in_parens +00624_length_utf8 +00625_arrays_in_nested +00626_in_syntax +00627_recursive_alias +00628_in_lambda_on_merge_table_bug +00633_func_or_in +00634_rename_view +00639_startsWith +00642_cast +00644_different_expressions_with_same_alias +00647_histogram +00647_histogram_negative +00647_select_numbers_with_offset +00649_quantile_tdigest_negative +00650_array_enumerate_uniq_with_tuples +00653_monotonic_integer_cast +00661_array_has_silviucpp +00662_array_has_nullable +00662_has_nullable +00663_tiny_log_empty_insert +00664_cast_from_string_to_nullable +00665_alter_nullable_string_to_nullable_uint8 +00666_uniq_complex_types +00667_compare_arrays_of_different_types +00668_compare_arrays_silviucpp +00671_max_intersections +00672_arrayDistinct +00673_subquery_prepared_set_performance +00674_has_array_enum +00676_group_by_in +00678_murmurhash +00679_uuid_in_key +00680_duplicate_columns_inside_union_all +00681_duplicate_columns_inside_union_all_stas_sviridov +00687_insert_into_mv +00688_aggregation_retention +00688_case_without_else +00688_low_cardinality_alter_add_column +00688_low_cardinality_defaults +00688_low_cardinality_dictionary_deserialization +00688_low_cardinality_prewhere +00688_low_cardinality_serialization +00689_join_table_function +00691_array_distinct +00696_system_columns_limit +00700_decimal_array_functions +00700_decimal_defaults +00700_decimal_gathers +00700_decimal_in_keys +00700_decimal_math +00700_decimal_null +00700_decimal_round +00700_decimal_with_default_precision_and_scale +00701_context_use_after_free +00702_join_with_using_dups +00702_where_with_quailified_names +00703_join_crash +00704_arrayCumSumLimited_arrayDifference +00710_array_enumerate_dense +00711_array_enumerate_variants +00712_prewhere_with_alias_and_virtual_column +00712_prewhere_with_missing_columns +00712_prewhere_with_missing_columns_2 +00713_collapsing_merge_tree +00715_bounding_ratio +00715_bounding_ratio_merge_empty +00717_default_join_type +00717_low_cardinaliry_group_by +00718_format_datetime_1 +00719_format_datetime_f_varsize_bug +00719_format_datetime_rand +00720_combinations_of_aggregate_combinators +00720_with_cube +00722_inner_join +00723_remerge_sort +00725_join_on_bug_1 +00725_join_on_bug_3 +00725_join_on_bug_4 +00726_length_aliases +00726_materialized_view_concurrent +00726_modulo_for_date +00733_if_datetime +00735_or_expr_optimize_bug +00737_decimal_group_by +00738_nested_merge_multidimensional_array +00740_optimize_predicate_expression +00745_compile_scalar_subquery +00746_compile_non_deterministic_function +00747_contributors +00750_merge_tree_merge_with_o_direct +00752_low_cardinality_array_result +00752_low_cardinality_mv_1 +00752_low_cardinality_permute +00753_alter_destination_for_storage_buffer +00753_quantile_format +00753_with_with_single_alias +00754_alter_modify_column_partitions +00754_first_significant_subdomain_more +00755_avg_value_size_hint_passing +00756_power_alias +00757_enum_defaults_const +00757_enum_defaults_const_analyzer +00759_kodieg +00760_url_functions_overflow +00765_sql_compatibility_aliases +00780_unaligned_array_join +00799_function_dry_run +00800_low_cardinality_array_group_by_arg +00800_low_cardinality_empty_array +00801_daylight_saving_time_hour_underflow +00802_daylight_saving_time_shift_backwards_at_midnight +00802_system_parts_with_datetime_partition +00803_xxhash +00804_rollup_with_having +00807_regexp_quote_meta +00810_in_operators_segfault +00812_prewhere_alias_array From 0044d164b229b1fd404481015184e35484ca624b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 20:49:11 +0200 Subject: [PATCH 10/23] Document 1.18.4 and 1.18.5 in CHANGELOG.md (#713) - [x] Document 1.18.4 and 1.18.5 in CHANGELOG.md - [x] Extend `.github/workflows/publish.yml` `latest` job to create the matching GitHub Release from the CHANGELOG section after pushing the tag (idempotent; aborts if the section is missing/empty) --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 235f57d50..540381aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 1.18.5 + +## Improvements + +- The `@clickhouse/client` npm package now ships an embedded AI-agent skill, `clickhouse-js-node-troubleshooting`, under `node_modules/@clickhouse/client/skills/`. The skill is also declared in the `agents.skills` field of the package manifest for discovery tools that scan `node_modules`. This allows agentic coding tools to load focused, Node-client-specific troubleshooting guidance without any additional setup. ([#682]) + +[#682]: https://github.com/ClickHouse/clickhouse-js/pull/682 + +# 1.18.4 + +A release-infrastructure-only version bump (no user-facing changes). See 1.18.5 for the next release with user-facing improvements. + # 1.18.3 ## Improvements From b92f73a156ac42c34a9909a48860ebe8116a2b6f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 22:01:43 +0200 Subject: [PATCH 11/23] tests: grow upstream-allowlist with verified passing SQL tests (#715) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - [x] Merge `origin/main` into the branch (resolves with `tests/clickhouse-test-runner/upstream-allowlist.txt` auto-merge) - [x] Dedup and sort the allowlist entries (661 entry lines → 481 unique entries, sorted lexicographically; header preserved) - [x] Final allowlist: 481 unique tests, alphabetically sorted --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> --- .../upstream-allowlist.txt | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index 58614e7ac..3bfedb1dc 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -6,6 +6,9 @@ # - Lines starting with '#' and blank lines are ignored. # - Add tests here once they reliably pass on both `client` and `http` backends. # - Remove (or comment out with a reason) tests that begin to flake. +# - Avoid bare prefixes that overlap with failing tests: `tests/clickhouse-test` +# treats positional arguments as substring matches, so e.g. `00396_uuid` would +# also pull in `00396_uuid_v7`. # # To extend this list, see tests/clickhouse-test-runner/README.md. @@ -44,7 +47,49 @@ 00054_join_string 00055_join_two_numbers 00056_join_number_string -01428_hash_set_nan_key +00067_replicate_segfault +00068_empty_tiny_log +00069_date_arithmetic +00077_set_keys_fit_128_bits_many_blocks +00078_string_concat +00087_distinct_of_empty_arrays +00087_math_functions +00088_distinct_of_arrays_of_strings +00089_group_by_arrays_of_fixed +00098_1_union_all +00098_2_union_all +00098_3_union_all +00098_4_union_all +00098_5_union_all +00098_6_union_all +00098_7_union_all +00098_8_union_all +00098_9_union_all +00098_a_union_all +00098_b_union_all +00098_c_union_all +00098_d_union_all +00098_e_union_all +00098_f_union_all +00098_g_union_all +00098_h_union_all +00098_j_union_all +00098_l_union_all +00099_join_many_blocks_segfault +00103_ipv4_num_to_string_class_c +00114_float_type_result_of_division +00116_storage_set +00117_parsing_arrays +00119_storage_join +00120_join_and_group_by +00122_join_with_subquery_with_subquery +00125_array_element_of_array_of_tuple +00127_group_by_concat +00128_group_by_number_and_fixed_string +00129_quantile_timing_weighted +00131_set_hashed +00132_sets +00134_aggregation_by_fixed_string_of_size_1_2_4_8 00136_duplicate_order_by_elems 00137_in_constants 00138_table_aliases @@ -64,12 +109,15 @@ 00165_transform_non_const_default 00167_settings_inside_query 00169_join_constant_keys +00170_lower_upper_utf8 +00170_lower_upper_utf8_memleak 00172_constexprs_in_set 00173_compare_date_time_with_constant_string 00174_compare_date_time_with_constant_string_in_in 00175_if_num_arrays 00175_partition_by_ignore 00176_if_string_arrays +00178_function_replicate 00178_query_datetime64_index 00179_lambdas_with_common_expressions_and_filter 00180_attach_materialized_view @@ -99,6 +147,7 @@ 00231_format_vertical_raw 00232_format_readable_decimal_size 00232_format_readable_size +00233_position_function_family 00233_position_function_sql_comparibilty 00234_disjunctive_equality_chains_optimization 00237_group_by_arrays @@ -111,6 +160,8 @@ 00255_array_concat_string 00256_reverse 00258_materializing_tuples +00259_hashing_tuples +00260_like_and_curly_braces 00263_merge_aggregates_and_overflow 00264_uniq_many_args 00266_read_overflow_mode @@ -137,10 +188,13 @@ 00317_in_tuples_and_out_of_range_values 00320_between 00323_quantiles_timing_bug +00324_hashing_enums 00330_view_subqueries 00331_final_and_prewhere_condition_ver_column +00332_quantile_timing_memory_leak 00333_parser_number_bug 00334_column_aggregate_function_limit +00338_replicate_array_of_strings 00342_escape_sequences 00343_array_element_generic 00346_if_tuple @@ -186,6 +240,7 @@ 00433_ifnull 00434_tonullable 00435_coalesce +00436_convert_charset 00436_fixed_string_16_comparisons 00437_nulls_first_last 00438_bit_rotate @@ -194,6 +249,7 @@ 00442_filter_by_nullable 00445_join_nullable_keys 00447_foreach_modifier +00448_replicate_nullable_tuple_generic 00448_to_string_cut_to_zero 00449_filter_array_nullable_tuple 00450_higher_order_and_nullable @@ -203,6 +259,7 @@ 00457_log_tinylog_stripelog_nullable 00459_group_array_insert_at 00461_default_value_of_argument_type +00462_json_true_false_literals 00464_array_element_out_of_range 00464_sort_all_constant_columns 00465_nullable_default @@ -224,9 +281,11 @@ 00487_if_array_fixed_string 00488_column_name_primary 00488_non_ascii_column_names +00490_special_line_separators_and_characters_outside_of_bmp 00490_with_select 00498_array_functions_concat_slice_push_pop 00498_bitwise_aggregate_functions +00499_json_enum_insert 00500_point_in_polygon_2d_const 00500_point_in_polygon_3d_const 00500_point_in_polygon_bug_2 @@ -433,3 +492,4 @@ 00807_regexp_quote_meta 00810_in_operators_segfault 00812_prewhere_alias_array +01428_hash_set_nan_key From 40c7da55b429bfc189bfc4fcb2b65fb2d9512122 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 22:18:48 +0200 Subject: [PATCH 12/23] docs(AGENTS.md): refresh based on last 4 weeks of PRs (#714) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `AGENTS.md` had drifted from reality after PRs #662–#712: it claimed `examples/web` has no `performance/` folder, pointed at a placeholder docs path, and said nothing about the `skills/` directory or the `.claude/skills/setup/` skill that replaced `copilot-setup-steps.yml`. ## Summary - **Examples › `performance/`** — drop the "no `performance/` folder under `examples/web`" claim; #701 added one for browser-safe streaming. - **Log-message docs guidance** — point at the real `docs/` layout from #677 (`docs/socket_hang_up_econnreset.md`, `docs/howto/`) instead of a fictional `docs/example-log-message.md`. The example URL in the snippet now also resolves to a real page (`docs/socket_hang_up_econnreset.md`). - **New `Skills` section** — documents the repo-root `skills/` directory (#681, #682, #702): `client-node`'s `prepack` ships it via `@clickhouse/client`, the `agents.skills` field of `packages/client-node/package.json` declares which skills are discoverable, and the `Skills E2E` workflow (`.github/workflows/e2e-skills.yml`, backed by `tests/e2e/skills/check.js`) asserts the packaged tarball contains the declared skills. Routes contributors to `.claude/skills/setup/SKILL.md` (#705) instead of re-introducing `copilot-setup-steps.yml`. - **`agents.skills` manifest** — adds `clickhouse-js-node-coding` alongside `clickhouse-js-node-troubleshooting` so both shipped skills are discoverable to downstream tooling. `tests/e2e/skills/check.js` is extended to verify both skills land in the packaged `@clickhouse/client` tarball. - **New `Embedded docs` section** — surfaces `docs/` as the preferred home for pages linked from log messages. - **CHANGELOG guidance** — aligns with the PR template: entry goes in the PR description and is folded into `CHANGELOG.md` at release time. Documentation refresh plus a small manifest/test change to make the second shipped skill discoverable. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov --- AGENTS.md | 23 +++++++++++++++++++---- packages/client-node/package.json | 4 ++++ tests/e2e/skills/check.js | 29 ++++++++++++++++------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7bb3ab286..b6d52ec92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,13 +12,13 @@ } ``` -2. When adding new log messages with suggestions for users, make sure to create a unique documentation page in the `docs` directory with a detailed explanation of the issue and how to resolve it. Then, include a link to that documentation page in the log message. For example: +2. When adding new log messages with suggestions for users, make sure to create a unique documentation page under the `docs/` directory (use `docs/howto/` for task-style guides; see `docs/socket_hang_up_econnreset.md` as a reference) with a detailed explanation of the issue and how to resolve it. Then, include a link to that documentation page in the log message. For example: ```ts if (some_condition) { log_writer.warn({ message: - 'Example log message with suggestions for users. For more information, see https://github.com/ClickHouse/clickhouse-js/blob/main/docs/example-log-message.md', + 'Example log message with suggestions for users. For more information, see https://github.com/ClickHouse/clickhouse-js/blob/main/docs/socket_hang_up_econnreset.md', }) } ``` @@ -49,7 +49,8 @@ The goals of the refactor are: - `coding/` — day-to-day client API usage (configure, ping, basic insert/select, parameter binding, sessions, data types, custom JSON). - `performance/` — async inserts, streaming with backpressure, file/Parquet streams, progress - streaming, server-side bulk moves. Node-only (no `performance/` folder under `examples/web`). + streaming, server-side bulk moves. Mostly Node-only; `examples/web/performance/` exists for the + few perf scenarios that work in the browser (e.g. streaming `JSONEachRow`). - `troubleshooting/` — cancellation, timeouts, long-running query progress, server error surfaces, number-precision pitfalls. - `security/` — TLS, RBAC, SQL-injection-safe parameter binding. @@ -61,6 +62,20 @@ The goals of the refactor are: a duplicated example, update **all** copies. The current duplicates and their primary locations are listed in [`examples/README.md`](examples/README.md#editing-duplicated-examples). +## Skills + + +- Each shipped skill must also be listed in the `agents.skills` array of + [`packages/client-node/package.json`](packages/client-node/package.json) so downstream tooling can + discover it. The [`Skills E2E`](.github/workflows/e2e-skills.yml) workflow + (`tests/e2e/skills/check.js`) asserts that the packaged tarball contains the declared skills. + +## Embedded docs + +The [`docs/`](docs) directory holds long-form troubleshooting / how-to pages that log messages and +skill references can link to (e.g. `docs/socket_hang_up_econnreset.md`, `docs/howto/`). Prefer +adding new pages here over linking out to external docs from log messages. + ## Upstream SQL test harness The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) harness is a Node.js port of `clickhouse-client` that allows the official ClickHouse Python test runner (`tests/clickhouse-test`) to drive a subset of the upstream SQL test suite against `@clickhouse/client`. @@ -98,6 +113,6 @@ For every pull request review, make sure to provide an evaluation of the followi 1. When reviewing code changes, it is important to consider the impact on the API quality and stability. For example, if the code changes involve modifying the library's public API surface (such as exported functions, classes, or types) or adding new public APIs, it is important to ensure that the changes are well-documented and do not break existing functionality for users of the library. -2. When introducing new features or making changes to the API make sure to update the CHANGELOG.md file with a concise description of the changes followed with an example usage if applicable. +2. When introducing new features or making changes to the API, make sure the PR description includes a concise, human-readable CHANGELOG entry (followed by an example usage if applicable) so it can be folded into `CHANGELOG.md` at release time. This matches the PR template checklist item ("A human-readable description of the changes was provided to include in CHANGELOG"). 3. Additionally, make sure that the official documentation is in sync with the changes. diff --git a/packages/client-node/package.json b/packages/client-node/package.json index 8e5ad68ac..1cdb78988 100644 --- a/packages/client-node/package.json +++ b/packages/client-node/package.json @@ -25,6 +25,10 @@ ], "agents": { "skills": [ + { + "name": "clickhouse-js-node-coding", + "path": "./skills/clickhouse-js-node-coding" + }, { "name": "clickhouse-js-node-troubleshooting", "path": "./skills/clickhouse-js-node-troubleshooting" diff --git a/tests/e2e/skills/check.js b/tests/e2e/skills/check.js index b0531d142..a6e079add 100644 --- a/tests/e2e/skills/check.js +++ b/tests/e2e/skills/check.js @@ -19,18 +19,21 @@ const nm = path.join(__dirname, 'node_modules') // @clickhouse/client (Node.js) const nodeRoot = path.join(nm, '@clickhouse', 'client') -const nodeSkillRoot = path.join( - nodeRoot, - 'skills', +const nodeSkills = [ + 'clickhouse-js-node-coding', 'clickhouse-js-node-troubleshooting', -) +] check('@clickhouse/client skills dir exists', () => assert.ok(fs.existsSync(path.join(nodeRoot, 'skills'))), ) -check('@clickhouse/client SKILL.md exists', () => - assert.ok(fs.existsSync(path.join(nodeSkillRoot, 'SKILL.md'))), -) +for (const skill of nodeSkills) { + check(`@clickhouse/client ${skill} SKILL.md exists`, () => + assert.ok( + fs.existsSync(path.join(nodeRoot, 'skills', skill, 'SKILL.md')), + ), + ) +} check('@clickhouse/client package.json declares agents.skills', () => { const pkg = JSON.parse( fs.readFileSync(path.join(nodeRoot, 'package.json'), 'utf8'), @@ -39,12 +42,12 @@ check('@clickhouse/client package.json declares agents.skills', () => { Array.isArray(pkg.agents?.skills), 'agents.skills should be an array', ) - assert.ok( - pkg.agents.skills.some( - (s) => s.name === 'clickhouse-js-node-troubleshooting', - ), - 'agents.skills should include clickhouse-js-node-troubleshooting', - ) + for (const skill of nodeSkills) { + assert.ok( + pkg.agents.skills.some((s) => s.name === skill), + `agents.skills should include ${skill}`, + ) + } }) // @clickhouse/client-web — no skills yet; verify the package installed cleanly and does not ship skills From b561a989b17d1ff29d5a498a86ace17e8b319819 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 22:22:56 +0200 Subject: [PATCH 13/23] Add max_response_headers_size client option (Node.js only) (#716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a new Node.js–only client option, `max_response_headers_size`, that is forwarded as the `maxHeaderSize` request option to `http.request` / `https.request`. Lifts the per-request response header cap (Node default ~16 KB) without `--max-http-header-size` / `NODE_OPTIONS`. - [x] Add `max_response_headers_size` to `NodeClickHouseClientConfigOptions` and forward it in `NodeConfigImpl.make_connection` - [x] Thread it through `CreateConnectionParams` and `NodeConnectionParams` - [x] Pass it as `maxHeaderSize` in `Http.request` / `Https.request` / custom-agent `*.request` calls - [x] Unit tests covering the new option (config-layer, custom-agent, HTTP and HTTPS connections — set + unset) - [x] Update docs and skills (long-running queries, socket-hangup) to point users at the new option - [x] CHANGELOG entry filed under `1.18.5` - [x] Merge `main` and resolve conflicts in `CHANGELOG.md` and `skills/.../socket-hangup.md` - [x] All change descriptions (CHANGELOG, `docs/howto/long_running_queries.md`, `socket-hangup.md` reference) pin the new option to `>= 1.18.5` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot --- CHANGELOG.md | 13 ++ docs/howto/long_running_queries.md | 15 +++ .../__tests__/unit/node_config.test.ts | 23 ++++ .../unit/node_custom_agent_connection.test.ts | 79 ++++++++++++ .../unit/node_http_connection.test.ts | 120 ++++++++++++++++++ .../unit/node_https_connection.test.ts | 79 ++++++++++++ packages/client-node/src/config.ts | 18 +++ .../src/connection/create_connection.ts | 5 + .../src/connection/node_base_connection.ts | 9 ++ .../node_custom_agent_connection.ts | 3 + .../src/connection/node_http_connection.ts | 3 + .../src/connection/node_https_connection.ts | 3 + .../reference/socket-hangup.md | 18 ++- 13 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 packages/client-node/__tests__/unit/node_http_connection.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 540381aae..b35be0ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## Improvements +- (Node.js only) Added `max_response_headers_size` client option that forwards the [`maxHeaderSize`](https://nodejs.org/api/http.html#httprequesturl-options-callback) option to the underlying `http(s).request` call. This raises the per-request limit on the total size of HTTP response headers received from the server (Node.js default is ~16 KB). It is most useful when running long-running queries with `send_progress_in_http_headers` enabled — the `X-ClickHouse-Progress` headers accumulate over the lifetime of the request and can exceed the default limit, causing the request to fail with `HPE_HEADER_OVERFLOW`. Setting this option avoids the need to use the global `--max-http-header-size` Node.js CLI flag or the `NODE_OPTIONS` environment variable. Has no effect for the Web client (which uses `fetch`) and no effect when a custom `http_agent` is configured with a request implementation that does not honor the option. + +```ts +const client = createClient({ + request_timeout: 400_000, + max_response_headers_size: 1024 * 1024, // accept up to 1 MiB of response headers + clickhouse_settings: { + send_progress_in_http_headers: 1, + http_headers_progress_interval_ms: '110000', + }, +}) +``` + - The `@clickhouse/client` npm package now ships an embedded AI-agent skill, `clickhouse-js-node-troubleshooting`, under `node_modules/@clickhouse/client/skills/`. The skill is also declared in the `agents.skills` field of the package manifest for discovery tools that scan `node_modules`. This allows agentic coding tools to load focused, Node-client-specific troubleshooting guidance without any additional setup. ([#682]) [#682]: https://github.com/ClickHouse/clickhouse-js/pull/682 diff --git a/docs/howto/long_running_queries.md b/docs/howto/long_running_queries.md index 070b2c16a..7351dbfdc 100644 --- a/docs/howto/long_running_queries.md +++ b/docs/howto/long_running_queries.md @@ -40,6 +40,21 @@ curl -v "http://localhost:8123/?wait_end_of_query=1&send_progress_in_http_header The default value for `http_headers_progress_interval_ms` is defined by how often ClickHouse sends progress updates for the query type. For some queries, this may be too frequent (causing unnecessary overhead and/or HTTP client headers buffer overflow) or too infrequent (failing to keep the LB connection alive). Therefore, it's recommended to set it explicitly when using `send_progress_in_http_headers`. +> **Note (Node.js):** Node.js caps the total size of received HTTP headers at ~16 KB by default. Each `X-ClickHouse-Progress` header is roughly 200 bytes, so after ~75 progress headers accumulate the request fails with `HPE_HEADER_OVERFLOW`. Since `>= 1.18.5`, you can raise this limit per client (without resorting to the global `--max-http-header-size` CLI flag or `NODE_OPTIONS`) by passing `max_response_headers_size` (in bytes) to `createClient`: +> +> ```ts +> const client = createClient({ +> request_timeout: 400_000, +> max_response_headers_size: 1024 * 1024, // 1 MiB +> clickhouse_settings: { +> send_progress_in_http_headers: 1, +> http_headers_progress_interval_ms: '110000', +> }, +> }) +> ``` +> +> The Web client uses `fetch` and is not subject to this limit. + **Step 1.** Estimate the maximum query execution time. Set `request_timeout` to a value safely above that estimate. **Step 2.** Find out your LB's idle connection timeout (e.g. 120s). Set `http_headers_progress_interval_ms` to a value a few seconds below it (e.g. `'110000'`). diff --git a/packages/client-node/__tests__/unit/node_config.test.ts b/packages/client-node/__tests__/unit/node_config.test.ts index da84b9815..d4c23e56a 100644 --- a/packages/client-node/__tests__/unit/node_config.test.ts +++ b/packages/client-node/__tests__/unit/node_config.test.ts @@ -282,5 +282,28 @@ describe('[Node.js] Config implementation details', () => { expect(createConnectionStub).toHaveBeenCalledTimes(1) expect(res).toEqual(fakeConnection) }) + + it('should forward max_response_headers_size to the connection factory', async () => { + const nodeConfig: NodeClickHouseClientConfigOptions = { + url: new URL('http://localhost:8123'), + max_response_headers_size: 64 * 1024, + } + const res = NodeConfigImpl.make_connection(nodeConfig as any, params) + expect(createConnectionStub).toHaveBeenCalledWith({ + connection_params: params, + tls: undefined, + keep_alive: { + enabled: true, + idle_socket_ttl: 2500, + }, + http_agent: undefined, + set_basic_auth_header: true, + capture_enhanced_stack_trace: false, + eagerly_destroy_stale_sockets: false, + max_response_headers_size: 64 * 1024, + } satisfies CreateConnectionParams) + expect(createConnectionStub).toHaveBeenCalledTimes(1) + expect(res).toEqual(fakeConnection) + }) }) }) diff --git a/packages/client-node/__tests__/unit/node_custom_agent_connection.test.ts b/packages/client-node/__tests__/unit/node_custom_agent_connection.test.ts index c5bf6775e..62c71a783 100644 --- a/packages/client-node/__tests__/unit/node_custom_agent_connection.test.ts +++ b/packages/client-node/__tests__/unit/node_custom_agent_connection.test.ts @@ -273,5 +273,84 @@ describe('[Node.js] NodeCustomAgentConnection', () => { httpRequestSpy.mockRestore() }) + + it('should forward max_response_headers_size as maxHeaderSize when set', () => { + const httpAgent = new Http.Agent() + const mockRequest = {} as Http.ClientRequest + const httpRequestSpy = vi + .spyOn(Http, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableCustomAgentConnection( + buildCustomAgentConnectionParams({ + url: new URL('http://localhost:8123'), + http_agent: httpAgent, + max_response_headers_size: 64 * 1024, + }), + ) + + const url = new URL('http://localhost:8123/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'CustomAgentConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions.maxHeaderSize).toBe(64 * 1024) + + httpRequestSpy.mockRestore() + }) + + it('should not include maxHeaderSize when max_response_headers_size is undefined', () => { + const httpAgent = new Http.Agent() + const mockRequest = {} as Http.ClientRequest + const httpRequestSpy = vi + .spyOn(Http, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableCustomAgentConnection( + buildCustomAgentConnectionParams({ + url: new URL('http://localhost:8123'), + http_agent: httpAgent, + }), + ) + + const url = new URL('http://localhost:8123/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'CustomAgentConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions).not.toHaveProperty('maxHeaderSize') + + httpRequestSpy.mockRestore() + }) }) }) diff --git a/packages/client-node/__tests__/unit/node_http_connection.test.ts b/packages/client-node/__tests__/unit/node_http_connection.test.ts new file mode 100644 index 000000000..f0f1e4e64 --- /dev/null +++ b/packages/client-node/__tests__/unit/node_http_connection.test.ts @@ -0,0 +1,120 @@ +import { describe, it, expect, vi } from 'vitest' +import Http from 'http' +import { ClickHouseLogLevel, LogWriter } from '@clickhouse/client-common' +import { TestLogger } from '../../../client-common/__tests__/utils/test_logger' +import type { NodeConnectionParams } from '../../src/connection' +import { NodeHttpConnection } from '../../src/connection' + +/** Extends NodeHttpConnection to expose protected methods for testing. */ +class TestableHttpConnection extends NodeHttpConnection { + public testCreateClientRequest( + ...args: Parameters + ): Http.ClientRequest { + return this.createClientRequest(...args) + } +} + +function buildHttpConnectionParams( + overrides?: Partial, +): NodeConnectionParams { + return { + url: new URL('http://localhost:8123'), + request_timeout: 30_000, + compression: { + decompress_response: false, + compress_request: false, + }, + max_open_connections: 10, + auth: { username: 'default', password: '', type: 'Credentials' }, + database: 'default', + clickhouse_settings: {}, + log_writer: new LogWriter( + new TestLogger(), + 'HttpConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + keep_alive: { + enabled: true, + idle_socket_ttl: 2500, + }, + set_basic_auth_header: true, + capture_enhanced_stack_trace: false, + eagerly_destroy_stale_sockets: false, + ...overrides, + } +} + +describe('[Node.js] NodeHttpConnection', () => { + describe('createClientRequest', () => { + it('should forward max_response_headers_size as maxHeaderSize when set', () => { + const mockRequest = {} as Http.ClientRequest + const httpRequestSpy = vi + .spyOn(Http, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableHttpConnection( + buildHttpConnectionParams({ + max_response_headers_size: 64 * 1024, + }), + ) + + const url = new URL('http://localhost:8123/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'HttpConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions.maxHeaderSize).toBe(64 * 1024) + + httpRequestSpy.mockRestore() + }) + + it('should not include maxHeaderSize when max_response_headers_size is undefined', () => { + const mockRequest = {} as Http.ClientRequest + const httpRequestSpy = vi + .spyOn(Http, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableHttpConnection(buildHttpConnectionParams()) + + const url = new URL('http://localhost:8123/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'HttpConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions).not.toHaveProperty('maxHeaderSize') + + httpRequestSpy.mockRestore() + }) + }) +}) diff --git a/packages/client-node/__tests__/unit/node_https_connection.test.ts b/packages/client-node/__tests__/unit/node_https_connection.test.ts index 314b69f92..ae71aac68 100644 --- a/packages/client-node/__tests__/unit/node_https_connection.test.ts +++ b/packages/client-node/__tests__/unit/node_https_connection.test.ts @@ -13,6 +13,11 @@ class TestableHttpsConnection extends NodeHttpsConnection { ): Http.OutgoingHttpHeaders { return this.buildRequestHeaders(params) } + public testCreateClientRequest( + ...args: Parameters + ): Http.ClientRequest { + return this.createClientRequest(...args) + } } function buildHttpsConnectionParams( @@ -167,4 +172,78 @@ describe('[Node.js] NodeHttpsConnection', () => { agentSpy.mockRestore() }) }) + + describe('createClientRequest', () => { + it('should forward max_response_headers_size as maxHeaderSize when set', () => { + const mockRequest = {} as Http.ClientRequest + const httpsRequestSpy = vi + .spyOn(Https, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableHttpsConnection( + buildHttpsConnectionParams({ + max_response_headers_size: 64 * 1024, + }), + ) + + const url = new URL('https://localhost:8443/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'HttpsConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpsRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpsRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions.maxHeaderSize).toBe(64 * 1024) + + httpsRequestSpy.mockRestore() + }) + + it('should not include maxHeaderSize when max_response_headers_size is undefined', () => { + const mockRequest = {} as Http.ClientRequest + const httpsRequestSpy = vi + .spyOn(Https, 'request') + .mockReturnValue(mockRequest) + + const connection = new TestableHttpsConnection( + buildHttpsConnectionParams(), + ) + + const url = new URL('https://localhost:8443/?query_id=test') + const abortController = new AbortController() + connection.testCreateClientRequest({ + method: 'GET', + url, + headers: {}, + abort_signal: abortController.signal, + query: 'SELECT 1', + query_id: 'test-query-id', + log_writer: new LogWriter( + new TestLogger(), + 'HttpsConnectionTest', + ClickHouseLogLevel.OFF, + ), + log_level: ClickHouseLogLevel.OFF, + }) + + expect(httpsRequestSpy).toHaveBeenCalledTimes(1) + const calledOptions = httpsRequestSpy.mock + .calls[0][1] as Http.RequestOptions + expect(calledOptions).not.toHaveProperty('maxHeaderSize') + + httpsRequestSpy.mockRestore() + }) + }) }) diff --git a/packages/client-node/src/config.ts b/packages/client-node/src/config.ts index 5369e4d56..19e223504 100644 --- a/packages/client-node/src/config.ts +++ b/packages/client-node/src/config.ts @@ -59,6 +59,23 @@ export type NodeClickHouseClientConfigOptions = * please provide your feedback in the repository. * @default false (disabled) */ capture_enhanced_stack_trace?: boolean + /** Override the maximum length (in bytes) of HTTP response headers accepted from the server. + * Forwarded as the `maxHeaderSize` option to {@link http.request} / {@link https.request}. + * + * This is primarily useful for long-running queries that rely on + * `send_progress_in_http_headers`: ClickHouse keeps appending an `X-ClickHouse-Progress` + * header on every progress interval, and once the cumulative size exceeds the Node.js + * default (~16 KB), the request fails with `HPE_HEADER_OVERFLOW`. Setting a higher value + * here (e.g. `64 * 1024` or `1024 * 1024`) lifts that limit per client without requiring + * the global `--max-http-header-size` Node.js CLI flag or `NODE_OPTIONS` environment variable. + * + * When `undefined`, the Node.js default (or the value of `--max-http-header-size`) applies. + * + * Has no effect when a custom {@link http_agent} is provided that uses a different + * request implementation; for the bundled HTTP/HTTPS connections it is passed straight + * through to the request options. + * @default undefined */ + max_response_headers_size?: number } interface BasicTLSOptions { @@ -138,6 +155,7 @@ export const NodeConfigImpl: Required< http_agent: nodeConfig.http_agent, keep_alive, tls, + max_response_headers_size: nodeConfig.max_response_headers_size, }) }, values_encoder: (jsonHandling: JSONHandling) => diff --git a/packages/client-node/src/connection/create_connection.ts b/packages/client-node/src/connection/create_connection.ts index 5527b7232..2f6760742 100644 --- a/packages/client-node/src/connection/create_connection.ts +++ b/packages/client-node/src/connection/create_connection.ts @@ -17,6 +17,7 @@ export interface CreateConnectionParams { set_basic_auth_header: boolean capture_enhanced_stack_trace: boolean eagerly_destroy_stale_sockets?: boolean + max_response_headers_size?: number } /** A factory for easier mocking after Node.js 22.18 */ @@ -30,6 +31,7 @@ export class NodeConnectionFactory { set_basic_auth_header, capture_enhanced_stack_trace, eagerly_destroy_stale_sockets = false, + max_response_headers_size, }: CreateConnectionParams): NodeBaseConnection { if (http_agent !== undefined) { return new NodeCustomAgentConnection({ @@ -39,6 +41,7 @@ export class NodeConnectionFactory { keep_alive, // only used to enforce proper KeepAlive headers http_agent, eagerly_destroy_stale_sockets, + max_response_headers_size, }) } switch (connection_params.url.protocol) { @@ -49,6 +52,7 @@ export class NodeConnectionFactory { capture_enhanced_stack_trace, keep_alive, eagerly_destroy_stale_sockets, + max_response_headers_size, }) case 'https:': return new NodeHttpsConnection({ @@ -58,6 +62,7 @@ export class NodeConnectionFactory { keep_alive, tls, eagerly_destroy_stale_sockets, + max_response_headers_size, }) default: throw new Error('Only HTTP and HTTPS protocols are supported') diff --git a/packages/client-node/src/connection/node_base_connection.ts b/packages/client-node/src/connection/node_base_connection.ts index 3d271a288..fd0a143eb 100644 --- a/packages/client-node/src/connection/node_base_connection.ts +++ b/packages/client-node/src/connection/node_base_connection.ts @@ -44,6 +44,15 @@ export type NodeConnectionParams = ConnectionParams & { * Eagerly destroy the sockets that are considered stale (idle for more than `idle_socket_ttl`), without waiting for the timeout to trigger. This allows to free up the stale sockets in case of longer event loop delays. */ eagerly_destroy_stale_sockets: boolean + /** + * Optional override for {@link Http.RequestOptions.maxHeaderSize} forwarded to + * `http(s).request`. Useful for long-running queries that accumulate many + * `X-ClickHouse-Progress` headers and would otherwise hit the Node.js default + * (~16 KB) total response header limit. + * + * When `undefined`, the Node.js default applies. + */ + max_response_headers_size?: number } export type TLSParams = diff --git a/packages/client-node/src/connection/node_custom_agent_connection.ts b/packages/client-node/src/connection/node_custom_agent_connection.ts index 6d4eebf95..b6a898e94 100644 --- a/packages/client-node/src/connection/node_custom_agent_connection.ts +++ b/packages/client-node/src/connection/node_custom_agent_connection.ts @@ -35,6 +35,9 @@ export class NodeCustomAgentConnection extends NodeBaseConnection { timeout: this.params.request_timeout, signal: params.abort_signal, headers, + ...(this.params.max_response_headers_size !== undefined && { + maxHeaderSize: this.params.max_response_headers_size, + }), }) } } diff --git a/packages/client-node/src/connection/node_http_connection.ts b/packages/client-node/src/connection/node_http_connection.ts index 3e4c2efb7..eb25493cf 100644 --- a/packages/client-node/src/connection/node_http_connection.ts +++ b/packages/client-node/src/connection/node_http_connection.ts @@ -25,6 +25,9 @@ export class NodeHttpConnection extends NodeBaseConnection { timeout: this.params.request_timeout, signal: params.abort_signal, headers, + ...(this.params.max_response_headers_size !== undefined && { + maxHeaderSize: this.params.max_response_headers_size, + }), }) } } diff --git a/packages/client-node/src/connection/node_https_connection.ts b/packages/client-node/src/connection/node_https_connection.ts index 5ea79bfb4..2a0d693a7 100644 --- a/packages/client-node/src/connection/node_https_connection.ts +++ b/packages/client-node/src/connection/node_https_connection.ts @@ -72,6 +72,9 @@ export class NodeHttpsConnection extends NodeBaseConnection { timeout: this.params.request_timeout, signal: params.abort_signal, headers, + ...(this.params.max_response_headers_size !== undefined && { + maxHeaderSize: this.params.max_response_headers_size, + }), }) } } diff --git a/skills/clickhouse-js-node-troubleshooting/reference/socket-hangup.md b/skills/clickhouse-js-node-troubleshooting/reference/socket-hangup.md index 91c14764d..39ffc7ee4 100644 --- a/skills/clickhouse-js-node-troubleshooting/reference/socket-hangup.md +++ b/skills/clickhouse-js-node-troubleshooting/reference/socket-hangup.md @@ -113,7 +113,7 @@ const client = createClient({ **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. -[^max-header-size]: Node.js also exposes a `maxHeaderSize` option on `http(s).request`, but the ClickHouse JS client currently does not forward it through `createClient`. For now, the practical workaround in clickhouse-js is to either use the `--max-http-header-size` CLI flag / `NODE_OPTIONS` (process-wide) or supply a custom `http.Agent` configured with `maxHeaderSize`. A dedicated client option is coming soon. +[^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`. **Maximum safe query duration formula:** @@ -133,11 +133,23 @@ Max duration (seconds) ≈ http_headers_progress_interval_ms × 75 ÷ 1000 If you need a longer max safe duration without lengthening the progress interval, raise Node's HTTP header limit. For example, increasing it from the default 16 KB to **64 KB** quadruples the max safe duration (≈300 progress headers instead of ≈75). +```ts +// Option 1 (recommended, since `>= 1.18.5`) — per-client, no process-wide flag needed +const client = createClient({ + request_timeout: 400_000, + max_response_headers_size: 65536, // 64 KB; lifts the per-request header cap + clickhouse_settings: { + send_progress_in_http_headers: 1, + http_headers_progress_interval_ms: '110000', + }, +}) +``` + ```bash -# Option 1 — CLI flag when launching your app +# Option 2 — CLI flag when launching your app (process-wide; older client versions) node --max-http-header-size=65536 app.js -# Option 2 — environment variable (works with any Node entry point, including npm/ts-node) +# Option 3 — environment variable (works with any Node entry point, including npm/ts-node) NODE_OPTIONS="--max-http-header-size=65536" node app.js ``` From e9f3604da197a21a6adc7da76651c61967aa37c7 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 23:40:47 +0200 Subject: [PATCH 14/23] tests(skills E2E): drive checks from `skills/` as the source of truth (#717) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Address unresolved review comments from #714 on `tests/e2e/skills/check.js`: the hardcoded skill list and single-skill symlink assertion let a missing/misdeclared skill (or wrong `path`) slip through. Make the repo-root `skills/` directory the source of truth and verify the full `agents.skills` manifest. - **`packages/client-node/package.json`** — add `clickhouse-js-node-coding` to `agents.skills` so both shipped skills are discoverable. (Also landed independently on `main` via #714 and reconciled during the merge.) - **`tests/e2e/skills/check.js`** — discover skills from `skills/` (subdirs containing `SKILL.md`) and assert, for the installed `@clickhouse/client` tarball: - `agents.skills` declares **exactly** that set (sorted-name `deepStrictEqual` — catches both missing and stale entries). - Each declared entry has `path === ./skills/`, the path resolves inside the installed package, and contains `SKILL.md`. - Each declared skill is symlinked under `.claude/skills/` by `skills-npm` and the symlink target contains `SKILL.md` (no longer hardcoded to `clickhouse-js-node-troubleshooting`). - `@clickhouse/client-web` still ships no `skills/` dir. - Descriptive error if `skills/` is missing; `npm-*` link list is re-read inside each check to avoid stale snapshots. - Merged latest `main` and resolved a conflict in `tests/e2e/skills/check.js` by keeping this PR's source-of-truth-driven implementation over the simpler hardcoded `nodeSkills` array introduced by #714. ## Checklist - [x] Unit and integration tests covering the common scenarios were added --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: copilot Co-authored-by: Peter Leonov --- tests/e2e/skills/check.js | 161 ++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 49 deletions(-) diff --git a/tests/e2e/skills/check.js b/tests/e2e/skills/check.js index a6e079add..7da08b67c 100644 --- a/tests/e2e/skills/check.js +++ b/tests/e2e/skills/check.js @@ -1,5 +1,22 @@ 'use strict' +// E2E packaging check for shipped AI-agent skills. +// +// Source of truth: the repo-root `skills/` directory. Every skill that lives +// there is shipped via `@clickhouse/client` (its `prepack` copies the entire +// `skills/` tree into the package), so this script discovers skills from the +// source directory and asserts that each one is: +// +// 1. declared in `agents.skills` of the installed @clickhouse/client +// package.json (with matching `path`), +// 2. present at the declared path inside the installed package and contains +// a `SKILL.md`, +// 3. symlinked into `.claude/skills/` by skills-npm. +// +// It also asserts that `agents.skills` does not declare any skill that is +// missing from the source `skills/` directory, and that `@clickhouse/client-web` +// ships no skills. + const assert = require('assert') const fs = require('fs') const path = require('path') @@ -16,39 +33,77 @@ function check(description, fn) { } const nm = path.join(__dirname, 'node_modules') +const repoRoot = path.resolve(__dirname, '..', '..', '..') +const skillsSrcDir = path.join(repoRoot, 'skills') + +// Discover skills from the source-of-truth `skills/` directory. +assert.ok( + fs.existsSync(skillsSrcDir), + `source-of-truth skills directory not found at ${skillsSrcDir}; this script is meant to run from tests/e2e/skills inside the clickhouse-js repo`, +) +const expectedSkills = fs + .readdirSync(skillsSrcDir, { withFileTypes: true }) + .filter((d) => d.isDirectory() && fs.existsSync(path.join(skillsSrcDir, d.name, 'SKILL.md'))) + .map((d) => d.name) + .sort() + +check('repo skills/ directory contains at least one skill', () => { + assert.ok( + expectedSkills.length > 0, + `expected at least one skill under ${skillsSrcDir}`, + ) +}) -// @clickhouse/client (Node.js) +// @clickhouse/client (Node.js) — ships every skill from the repo `skills/` tree. const nodeRoot = path.join(nm, '@clickhouse', 'client') -const nodeSkills = [ - 'clickhouse-js-node-coding', - 'clickhouse-js-node-troubleshooting', -] +const nodePkg = JSON.parse( + fs.readFileSync(path.join(nodeRoot, 'package.json'), 'utf8'), +) +const declaredSkills = Array.isArray(nodePkg.agents?.skills) + ? nodePkg.agents.skills + : [] check('@clickhouse/client skills dir exists', () => assert.ok(fs.existsSync(path.join(nodeRoot, 'skills'))), ) -for (const skill of nodeSkills) { - check(`@clickhouse/client ${skill} SKILL.md exists`, () => - assert.ok( - fs.existsSync(path.join(nodeRoot, 'skills', skill, 'SKILL.md')), - ), +check( + '@clickhouse/client agents.skills declares every skill from skills/', + () => { + const declaredNames = declaredSkills.map((s) => s.name).sort() + assert.deepStrictEqual( + declaredNames, + expectedSkills, + `agents.skills (${JSON.stringify(declaredNames)}) must match the skills found under skills/ (${JSON.stringify(expectedSkills)})`, + ) + }, +) + +for (const skill of declaredSkills) { + check( + `@clickhouse/client agents.skills entry "${skill.name}" has a valid path`, + () => { + assert.ok( + typeof skill.path === 'string' && skill.path.length > 0, + `agents.skills entry for ${skill.name} must declare a "path"`, + ) + const expectedPath = `./skills/${skill.name}` + assert.strictEqual( + skill.path, + expectedPath, + `agents.skills entry for ${skill.name} should declare path "${expectedPath}"`, + ) + const resolved = path.join(nodeRoot, skill.path) + assert.ok( + fs.existsSync(resolved), + `declared skill path does not exist in installed package: ${skill.path}`, + ) + assert.ok( + fs.existsSync(path.join(resolved, 'SKILL.md')), + `declared skill at ${skill.path} should contain SKILL.md`, + ) + }, ) } -check('@clickhouse/client package.json declares agents.skills', () => { - const pkg = JSON.parse( - fs.readFileSync(path.join(nodeRoot, 'package.json'), 'utf8'), - ) - assert.ok( - Array.isArray(pkg.agents?.skills), - 'agents.skills should be an array', - ) - for (const skill of nodeSkills) { - assert.ok( - pkg.agents.skills.some((s) => s.name === skill), - `agents.skills should include ${skill}`, - ) - } -}) // @clickhouse/client-web — no skills yet; verify the package installed cleanly and does not ship skills check('@clickhouse/client-web installs without skills dir', () => { @@ -63,39 +118,47 @@ check('@clickhouse/client-web installs without skills dir', () => { ) }) +// skills-npm — symlinks each declared skill under `.claude/skills/`. +const skillsLinkDir = path.join(__dirname, '.claude', 'skills') + check('skills-npm creates a skills/ directory', () => { assert.ok( - fs.existsSync(path.join(__dirname, '.claude', 'skills')), + fs.existsSync(skillsLinkDir), 'skills/ directory should be created by skills-npm', ) }) -check('skills-npm symlinks clickhouse-js-node-troubleshooting', () => { - const skillsDir = path.join(__dirname, '.claude', 'skills') - const entries = fs.readdirSync(skillsDir) - const npmLinks = entries.filter((e) => e.startsWith('npm-')) - assert.ok( - npmLinks.length > 0, - 'skills/ should contain at least one npm-* symlink', - ) - - const troubleshootingLink = npmLinks.find((e) => - e.includes('clickhouse-js-node-troubleshooting'), - ) - assert.ok( - troubleshootingLink, - `skills/ should contain a symlink for clickhouse-js-node-troubleshooting, found: [${npmLinks.join(', ')}]`, - ) +const npmLinks = () => + fs.existsSync(skillsLinkDir) + ? fs.readdirSync(skillsLinkDir).filter((e) => e.startsWith('npm-')) + : [] - const linkPath = path.join(skillsDir, troubleshootingLink) - assert.ok( - fs.lstatSync(linkPath).isSymbolicLink(), - `${troubleshootingLink} should be a symlink`, - ) +check('skills-npm creates at least one npm-* symlink', () => { + const links = npmLinks() assert.ok( - fs.existsSync(path.join(linkPath, 'SKILL.md')), - 'symlinked skill should contain SKILL.md', + links.length > 0, + 'skills/ should contain at least one npm-* symlink', ) }) +for (const skill of declaredSkills) { + check(`skills-npm symlinks ${skill.name}`, () => { + const links = npmLinks() + const link = links.find((e) => e.includes(skill.name)) + assert.ok( + link, + `skills/ should contain a symlink for ${skill.name}, found: [${links.join(', ')}]`, + ) + const linkPath = path.join(skillsLinkDir, link) + assert.ok( + fs.lstatSync(linkPath).isSymbolicLink(), + `${link} should be a symlink`, + ) + assert.ok( + fs.existsSync(path.join(linkPath, 'SKILL.md')), + `symlinked skill ${skill.name} should contain SKILL.md`, + ) + }) +} + console.log('\nAll checks passed.') From 09b78f08bf6da33d2e84681d6cd76dc563a46910 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 23:52:42 +0200 Subject: [PATCH 15/23] Add client-based integration test for max_response_headers_size (#719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary The existing `node_response_headers_cap.test.ts` only verifies the underlying Node.js `http.request` `maxHeaderSize` knob, not that the client actually wires `max_response_headers_size` through to it. This adds a sibling integration test that drives the same scenarios via `createClient` so the public option is covered end-to-end. - **New test** `packages/client-node/__tests__/integration/node_response_headers_cap_client.test.ts`: copy of the original, but the request goes through `createClient({ url, max_response_headers_size, keep_alive: { enabled: false } })` + `client.ping()` against the same raw TCP server that emits a hand-crafted HTTP/1.1 response with a large header block. - **Assertions**: oversize cases resolve to `{ success: false, error: { code: 'HPE_HEADER_OVERFLOW' } }`; raised-limit cases resolve to `{ success: true }`. Same six A–F scenarios across the 16K / 32K / 64K buckets as the original. - **Original test** `node_response_headers_cap.test.ts` is left unchanged — it still serves as the pure-Node behavior baseline. ```ts client = createClient({ url: `http://127.0.0.1:${port}`, keep_alive: { enabled: false }, max_response_headers_size: 64 * 1024, }) const result = await client.ping() ``` ## Checklist - [x] Unit and integration tests covering the common scenarios were added Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Peter Leonov --- .../node_response_headers_cap_client.test.ts | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 packages/client-node/__tests__/integration/node_response_headers_cap_client.test.ts diff --git a/packages/client-node/__tests__/integration/node_response_headers_cap_client.test.ts b/packages/client-node/__tests__/integration/node_response_headers_cap_client.test.ts new file mode 100644 index 000000000..8dcfe964c --- /dev/null +++ b/packages/client-node/__tests__/integration/node_response_headers_cap_client.test.ts @@ -0,0 +1,164 @@ +import net, { type AddressInfo } from 'net' +import { afterEach, describe, it } from 'vitest' +import { createClient } from '../../src' +import type { ClickHouseClient } from '@clickhouse/client-common' + +// Verifies that the Node.js client honors the `max_response_headers_size` +// configuration option, which is forwarded to `http(s).request` as the +// `maxHeaderSize` option. +// +// Mirrors the scenarios from `node_response_headers_cap.test.ts`, but instead +// of using the raw Node `http` module the request is issued through +// `createClient` + `client.ping()`. A raw TCP server is still used to emit a +// hand-crafted HTTP/1.1 response with a large block of headers, bypassing the +// real ClickHouse server (and its own header-size limits). +describe('[Node.js] client max_response_headers_size behavior', () => { + let client: ClickHouseClient | undefined + + afterEach(async () => { + if (client !== undefined) { + await client.close() + client = undefined + } + }) + + // Build enough X-H-NNNN headers to roughly reach `targetBytes`. + function makeHeaders( + targetBytes: number, + ): Array<{ name: string; value: string }> { + const headers: Array<{ name: string; value: string }> = [] + let total = 0 + let i = 0 + while (total < targetBytes) { + const name = `X-H-${String(i).padStart(4, '0')}` + const value = 'a'.repeat(90) + headers.push({ name, value }) + total += name.length + 2 /* ": " */ + value.length + 2 /* CRLF */ + i++ + } + return headers + } + + // Raw TCP server that replies with a fixed HTTP/1.1 response containing + // the supplied headers. Bypasses Node's own server header limit entirely. + async function startServer( + headers: Array<{ name: string; value: string }>, + ): Promise<[net.Server, number]> { + const server = net.createServer((socket) => { + socket.once('data', () => { + const body = 'Ok.\n' + const headerBlob = headers + .map((h) => `${h.name}: ${h.value}\r\n`) + .join('') + const response = + 'HTTP/1.1 200 OK\r\n' + + `Content-Length: ${body.length}\r\n` + + 'Connection: close\r\n' + + headerBlob + + '\r\n' + + body + socket.write(response) + socket.end() + }) + }) + await new Promise((resolve) => { + server.listen(0, '127.0.0.1', () => resolve()) + }) + return [server, (server.address() as AddressInfo).port] + } + + type ClientResult = + | { ok: true } + | { ok: false; code?: string; message: string } + + async function tryClient( + port: number, + maxHeaderSize?: number, + ): Promise { + client = createClient({ + url: `http://127.0.0.1:${port}`, + // Force `Connection: close` so the client does not attempt to reuse + // sockets across the single response from our raw TCP server. + keep_alive: { enabled: false }, + max_response_headers_size: maxHeaderSize, + }) + const result = await client.ping() + if (result.success) { + return { ok: true } + } + const err = result.error as NodeJS.ErrnoException + return { ok: false, code: err.code, message: err.message } + } + + async function runScenario(params: { + payloadKB: number + maxHeaderSize?: number + }): Promise<{ result: ClientResult }> { + const headers = makeHeaders(params.payloadKB * 1024) + const [server, port] = await startServer(headers) + try { + const result = await tryClient(port, params.maxHeaderSize) + return { result } + } finally { + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) reject(err) + else resolve() + }) + }) + } + } + + // ── 16K bucket ──────────────────────────────────────────────── + it('A. ~15K payload at default 16K limit fits', async ({ expect }) => { + const { result } = await runScenario({ payloadKB: 15 }) + expect(result.ok).toBe(true) + }) + + it('B. ~20K payload at default 16K limit overflows (HPE_HEADER_OVERFLOW)', async ({ + expect, + }) => { + const { result } = await runScenario({ payloadKB: 20 }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.code).toBe('HPE_HEADER_OVERFLOW') + }) + + // ── 32K bucket ──────────────────────────────────────────────── + it('C. ~20K payload raised to 32K limit fits', async ({ expect }) => { + const { result } = await runScenario({ + payloadKB: 20, + maxHeaderSize: 32 * 1024, + }) + expect(result.ok).toBe(true) + }) + + it('D. ~40K payload at 32K limit overflows (HPE_HEADER_OVERFLOW)', async ({ + expect, + }) => { + const { result } = await runScenario({ + payloadKB: 40, + maxHeaderSize: 32 * 1024, + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.code).toBe('HPE_HEADER_OVERFLOW') + }) + + // ── 64K bucket ──────────────────────────────────────────────── + it('E. ~40K payload raised to 64K limit fits', async ({ expect }) => { + const { result } = await runScenario({ + payloadKB: 40, + maxHeaderSize: 64 * 1024, + }) + expect(result.ok).toBe(true) + }) + + it('F. ~60K payload at 64K limit fits', async ({ expect }) => { + const { result } = await runScenario({ + payloadKB: 60, + maxHeaderSize: 64 * 1024, + }) + expect(result.ok).toBe(true) + }) +}) From df37f5eec9b56e20475d63add925aa6f998374ce Mon Sep 17 00:00:00 2001 From: eryue0220 Date: Sun, 10 May 2026 06:15:23 +0800 Subject: [PATCH 16/23] feat(infra): update to eslint v10 (#643) ## Summary Update eslint to v10 ## Checklist --------- Co-authored-by: Peter Leonov --- .github/dependabot.yml | 4 - package-lock.json | 369 +++++------------------ package.json | 4 +- packages/client-node/src/utils/stream.ts | 1 + 4 files changed, 81 insertions(+), 297 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 636ac6f6e..c272e9631 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,10 +18,6 @@ updates: dev-dependencies: dependency-type: 'development' ignore: - - dependency-name: 'eslint' - versions: ['>= 10'] - - dependency-name: '@eslint/js' - versions: ['>= 10'] - dependency-name: '@opentelemetry/auto-instrumentations-node' versions: ['0.70.0'] - dependency-name: '@types/node' diff --git a/package-lock.json b/package-lock.json index 549cbed80..395e06c5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "./packages/*" ], "devDependencies": { - "@eslint/js": "^9.39.4", + "@eslint/js": "^10.0.1", "@faker-js/faker": "^10.3.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.71.0", @@ -29,7 +29,7 @@ "@vitest/coverage-istanbul": "^4.1.0", "@vitest/coverage-v8": "^4.1.0", "apache-arrow": "^21.0.0", - "eslint": "^9.39.4", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-expect-type": "^0.6.2", "eslint-plugin-prettier": "^5.5.5", @@ -822,153 +822,89 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@eslint/object-schema": "^3.0.4", "debug": "^4.3.1", - "minimatch": "^3.1.5" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", + "@eslint/core": "^1.2.0", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@faker-js/faker": { @@ -3784,6 +3720,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -4146,19 +4089,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@vitest/browser": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.0.tgz", @@ -4376,9 +4306,9 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -4511,13 +4441,6 @@ "undici-types": "~7.16.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/array-back": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", @@ -4557,13 +4480,6 @@ "dev": true, "license": "MIT" }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { "version": "2.10.8", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", @@ -4651,16 +4567,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001779", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", @@ -4897,13 +4803,6 @@ "node": ">=20" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5071,33 +4970,30 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", - "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -5107,8 +5003,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -5116,7 +5011,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -5198,72 +5093,50 @@ } }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5609,19 +5482,6 @@ "node": ">=10.13.0" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/google-logging-utils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", @@ -5696,23 +5556,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5815,19 +5658,6 @@ "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -6147,13 +5977,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -6502,19 +6325,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parquet-wasm": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/parquet-wasm/-/parquet-wasm-0.7.1.tgz", @@ -6833,16 +6643,6 @@ "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -7174,19 +6974,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index 2bc9057c3..170857615 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "prepare": "husky" }, "devDependencies": { - "@eslint/js": "^9.39.4", + "@eslint/js": "^10.0.1", "@faker-js/faker": "^10.3.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.71.0", @@ -63,7 +63,7 @@ "@vitest/coverage-istanbul": "^4.1.0", "@vitest/coverage-v8": "^4.1.0", "apache-arrow": "^21.0.0", - "eslint": "^9.39.4", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-expect-type": "^0.6.2", "eslint-plugin-prettier": "^5.5.5", diff --git a/packages/client-node/src/utils/stream.ts b/packages/client-node/src/utils/stream.ts index 0af193d93..1be3151fd 100644 --- a/packages/client-node/src/utils/stream.ts +++ b/packages/client-node/src/utils/stream.ts @@ -34,6 +34,7 @@ export async function getAsText(stream: Stream.Readable): Promise { ) { throw new Error( `The response length exceeds the maximum allowed size of V8 String: ${MAX_STRING_LENGTH} characters.`, + { cause: err }, ) } throw err From f013a5b128e182ce945b1c8753eb71ccbbc71e72 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 May 2026 00:38:34 +0200 Subject: [PATCH 17/23] ci(upstream-sql-tests): shard the allowlist into ~1-minute jobs (#720) ## Summary The `upstream-sql-tests` workflow had grown to dominate PR CI runtime (~170 s for the `Run upstream SQL tests` step in each matrix cell). Since the allowlist is trivially parallelizable, this splits it across a third matrix dimension so each shard finishes in roughly a minute. - **`run-upstream-tests.sh`**: new `SHARD_INDEX` / `SHARD_TOTAL` env vars. When `SHARD_TOTAL > 1`, the allowlist is filtered round-robin (`i % SHARD_TOTAL == SHARD_INDEX - 1`). Both inputs are validated as positive integers with `SHARD_INDEX <= SHARD_TOTAL`. Defaults are `1/1`, so local invocations are unchanged. - **`upstream-sql-tests.yml`**: added `shard: [1, 2, 3]` to the matrix and threaded `SHARD_INDEX: ${{ matrix.shard }}` / `SHARD_TOTAL: 3` into the run step. Artifact names now include `-shard-N` to avoid upload collisions. - **Docs** (`README.md`, `AGENTS.md`): documented the new env vars, the updated matrix shape, and the rule of thumb to bump `shard` and `SHARD_TOTAL` together if per-shard runtime climbs back above ~1 minute. Round-robin (rather than contiguous chunks) keeps per-shard runtimes even regardless of allowlist ordering. With the current 481 entries the split is 161 / 160 / 160. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov --- .github/workflows/upstream-sql-tests.yml | 9 +++++- AGENTS.md | 4 +-- tests/clickhouse-test-runner/README.md | 3 ++ .../scripts/run-upstream-tests.sh | 30 +++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/.github/workflows/upstream-sql-tests.yml b/.github/workflows/upstream-sql-tests.yml index ec7a8c16b..de66e1cf3 100644 --- a/.github/workflows/upstream-sql-tests.yml +++ b/.github/workflows/upstream-sql-tests.yml @@ -39,6 +39,11 @@ jobs: matrix: impl: [client, http] clickhouse: [head, latest] + # Round-robin shards keep each job at roughly one minute so the + # upstream SQL tests no longer dominate PR CI runtime. Bump + # `shard` and `SHARD_TOTAL` together if the allowlist grows enough + # that per-shard runtime climbs back above ~1 minute. + shard: [1, 2, 3] steps: - name: Checkout clickhouse-js uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -96,6 +101,8 @@ jobs: env: CLICKHOUSE_CLIENT_CLI_IMPL: ${{ matrix.impl }} CLICKHOUSE_CLIENT_CLI_LOG: ${{ github.workspace }}/upstream-run.log + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 3 run: | bash tests/clickhouse-test-runner/scripts/run-upstream-tests.sh --no-stateful @@ -103,7 +110,7 @@ jobs: if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: upstream-sql-tests-${{ matrix.impl }}-${{ matrix.clickhouse }} + name: upstream-sql-tests-${{ matrix.impl }}-${{ matrix.clickhouse }}-shard-${{ matrix.shard }} retention-days: 14 if-no-files-found: ignore path: | diff --git a/AGENTS.md b/AGENTS.md index b6d52ec92..8432285e7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,7 +83,7 @@ The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) harness is a ### What the harness does - Wraps `@clickhouse/client` in a tiny CLI (`bin/clickhouse` → `dist/main.js`) that mimics enough of the upstream `clickhouse-client` binary (same flags, `extract-from-config` shortcut, stdin/`--query` behavior) for the Python `tests/clickhouse-test` runner to drive it without modification. -- Two backend implementations selectable via `CLICKHOUSE_CLIENT_CLI_IMPL`: `client` (uses `@clickhouse/client`) and `http` (raw `fetch` to port 8123). The CI matrix runs both against ClickHouse `latest` and `head` so that we cover both code paths and detect server regressions. +- Two backend implementations selectable via `CLICKHOUSE_CLIENT_CLI_IMPL`: `client` (uses `@clickhouse/client`) and `http` (raw `fetch` to port 8123). The CI matrix runs both against ClickHouse `latest` and `head` so that we cover both code paths and detect server regressions. The allowlist is also split into round-robin shards (`SHARD_INDEX` / `SHARD_TOTAL`) so each matrix job stays at roughly one minute; bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together if per-shard runtime climbs back above ~1 minute. - Reads the curated test list from [`upstream-allowlist.txt`](tests/clickhouse-test-runner/upstream-allowlist.txt) (one test name per line, `#` for comments) and forwards them as positional arguments to `tests/clickhouse-test`. - The `SERVER_SETTINGS`/`CLIENT_ONLY_SETTINGS` allowlists in [`src/settings.ts`](tests/clickhouse-test-runner/src/settings.ts) are copied from the Java port and may need periodic resync as ClickHouse adds or reclassifies settings. @@ -95,7 +95,7 @@ The allowlist is grown in **batches of ~100 candidate tests at a time**, in upst 1. **Pre-filter the candidate batch.** Skip non-SQL tests (`.sh`, `.py`, `.j2`) and tests tagged for unsupported infrastructure (`shard`, `distributed`, `replicated`, `zookeeper`, `kafka`, `s3`, `mysql`, `tls`, etc.). These will never pass through this harness as it stands today. 2. **Run each candidate against both backends** (`CLICKHOUSE_CLIENT_CLI_IMPL=client` and `=http`) using the harness, with `--no-stateful --no-long`. **Only keep tests that report `[ OK ]` on both backends**; drop failures and skips. -3. **Validate against the CI matrix before committing**, not just one local server version. The CI workflow runs `{client, http} × {ClickHouse latest, head}` — a test that passes locally on `head` may fail on `latest` (or vice versa) and break CI. +3. **Validate against the CI matrix before committing**, not just one local server version. The CI workflow runs `{client, http} × {ClickHouse latest, head} × {shard 1..N}` — a test that passes locally on `head` may fail on `latest` (or vice versa) and break CI. 4. **Beware substring/prefix expansion.** `tests/clickhouse-test` treats positional arguments as **substring/prefix matches** rather than exact names, so an allowlist entry like `00396_uuid` will silently pull in `00396_uuid_v7`, `00712_prewhere_with_alias` will pull in `00712_prewhere_with_alias_bug_2`, etc. When adding an entry whose name is a prefix of any other test in `0_stateless`, prefer the longest unambiguous form, or accept that the siblings come along and verify they all pass on both backends. 5. **Prune flakes promptly.** If a previously-passing test starts to flake on the nightly run, remove it (or its prefix-expanded siblings) from the allowlist rather than retrying — the allowlist exists to be a stable green signal, not a TODO list. diff --git a/tests/clickhouse-test-runner/README.md b/tests/clickhouse-test-runner/README.md index d9c2ab9bf..06f603b90 100644 --- a/tests/clickhouse-test-runner/README.md +++ b/tests/clickhouse-test-runner/README.md @@ -52,6 +52,8 @@ export PATH="/path/to/clickhouse-js/tests/clickhouse-test-runner/bin:$PATH" | `CLICKHOUSE_CLIENT_CLI_LOG` | `tests/clickhouse-test-runner/.upstream/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | | `UPSTREAM_CLICKHOUSE_DIR` | `tests/clickhouse-test-runner/.upstream/ClickHouse` | Path to a checkout of `ClickHouse/ClickHouse` containing the upstream test suite. | | `UPSTREAM_TEST_LIST` | `tests/clickhouse-test-runner/upstream-allowlist.txt` | Path to a file listing the upstream tests to run (one test name per line, `#` for comments). | +| `SHARD_INDEX` | `1` | 1-based index of the shard to run when sharding the allowlist (must be `<= SHARD_TOTAL`). | +| `SHARD_TOTAL` | `1` | Total number of shards. When `> 1`, only tests at positions where `i % SHARD_TOTAL == SHARD_INDEX - 1` are run (round-robin selection). | ## Running against the upstream test suite @@ -99,6 +101,7 @@ The workflow `.github/workflows/upstream-sql-tests.yml` runs this harness in CI: - Triggered on **workflow_dispatch**, **nightly at 05:00 UTC**, and on **pushes/PRs** that touch `tests/clickhouse-test-runner/**` or the workflow file itself. - The `workflow_dispatch` input `upstream_ref` can be used to pin a specific upstream commit or branch (defaults to `master`). +- The matrix runs every combination of `{impl: client | http} × {clickhouse: head | latest} × {shard: 1..N}`. Sharding is round-robin across the allowlist (see `SHARD_INDEX` / `SHARD_TOTAL` above) so each shard takes roughly one minute. If the allowlist grows enough that per-shard runtime climbs back above ~1 minute, bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together. ## Local development diff --git a/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh index 15f005459..d01842399 100755 --- a/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh +++ b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh @@ -41,6 +41,36 @@ done < "${UPSTREAM_TEST_LIST}" echo "Selected ${#tests[@]} test(s) from ${UPSTREAM_TEST_LIST}" >&2 +# Optional sharding: pick a round-robin subset of the allowlist when +# SHARD_TOTAL > 1. Tests at positions where (index % SHARD_TOTAL) == +# (SHARD_INDEX - 1) are kept (1-based SHARD_INDEX). Round-robin selection +# keeps each shard a representative sample of the full allowlist regardless +# of how the allowlist is ordered, so per-shard runtimes stay roughly even. +SHARD_INDEX="${SHARD_INDEX:-1}" +SHARD_TOTAL="${SHARD_TOTAL:-1}" +if ! [[ "${SHARD_TOTAL}" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: SHARD_TOTAL must be a positive integer (got: '${SHARD_TOTAL}')." >&2 + exit 1 +fi +if ! [[ "${SHARD_INDEX}" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: SHARD_INDEX must be a positive integer (got: '${SHARD_INDEX}')." >&2 + exit 1 +fi +if (( SHARD_INDEX > SHARD_TOTAL )); then + echo "Error: SHARD_INDEX (${SHARD_INDEX}) must be <= SHARD_TOTAL (${SHARD_TOTAL})." >&2 + exit 1 +fi +if (( SHARD_TOTAL > 1 )); then + sharded=() + for i in "${!tests[@]}"; do + if (( i % SHARD_TOTAL == SHARD_INDEX - 1 )); then + sharded+=("${tests[$i]}") + fi + done + echo "Sharding: keeping ${#sharded[@]} test(s) for shard ${SHARD_INDEX}/${SHARD_TOTAL}" >&2 + tests=("${sharded[@]}") +fi + if [[ ${#tests[@]} -eq 0 ]]; then if [[ "${ALLOW_EMPTY_UPSTREAM_ALLOWLIST:-0}" != "1" ]]; then echo "Error: no tests were selected from ${UPSTREAM_TEST_LIST}." >&2 From 088b3cb6be7104d24268d866628738f44ae724e4 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 May 2026 00:42:33 +0200 Subject: [PATCH 18/23] chore: bump root engines.node to >=20.19.0 to match ESLint v10 (#723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Applies review feedback from [#643 (discussion_r3213961603)](https://github.com/ClickHouse/clickhouse-js/pull/643#discussion_r3213961603): ESLint v10 requires Node `^20.19.0`, but the root `engines.node` still advertises `>=20`, allowing 20.0–20.18 where ESLint v10 may not run reliably. - Bump root `engines.node` from `>=20` to `>=20.19.0` in `package.json`. Other `package.json` files in the repo are unaffected — `examples/{node,web}` and `tests/clickhouse-test-runner` pin ESLint v9, and `tests/clickhouse-test-runner` already declares `>=20.19.0`. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 170857615..230624f61 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "private": true, "engines": { - "node": ">=20" + "node": ">=20.19.0" }, "scripts": { "typecheck": "npm --workspaces run typecheck", From 79a1dc129166fe44463ea5786016f0f679283c29 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 12:10:38 +0000 Subject: [PATCH 19/23] Remove unused `http` mode from upstream SQL test harness (#727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `tests/clickhouse-test-runner` exposed a `CLICKHOUSE_CLIENT_CLI_IMPL` switch with two backends — `client` (uses `@clickhouse/client`) and `http` (raw `fetch` to port 8123). The `http` backend doesn't exercise any code shipped by this repo; it was carried over from the [`clickhouse-java` harness](https://github.com/ClickHouse/clickhouse-java/tree/main/tests/clickhouse-client), where the same switch toggles between two real product surfaces (`client-v2` vs JDBC). `clickhouse-js` has no such second surface, so the `http` mode is removed entirely along with its CI matrix axis. - **Harness**: deleted `src/backends/http.ts`; `src/main.ts` always dispatches to `executeWithClient`; removed the `CLICKHOUSE_CLIENT_CLI_IMPL` env var, validation, and usage line from `src/main.ts` / `src/args.ts`. - **Runner script**: dropped `CLICKHOUSE_CLIENT_CLI_IMPL` pickup/export from `scripts/run-upstream-tests.sh`. - **CI** (`.github/workflows/upstream-sql-tests.yml`): removed the `impl` matrix axis and corresponding step env; matrix is now `{clickhouse: head | latest} × {shard: 1..3}`, halving the job count. Artifact name no longer includes `${{ matrix.impl }}`. - **Docs**: updated `tests/clickhouse-test-runner/README.md`, `AGENTS.md`, and the `upstream-allowlist.txt` header to drop the dual-backend env var, toggle example, and "must pass on both backends" allowlist rule. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .github/workflows/upstream-sql-tests.yml | 4 +- AGENTS.md | 9 +-- tests/clickhouse-test-runner/README.md | 26 +++---- .../scripts/run-upstream-tests.sh | 4 - tests/clickhouse-test-runner/src/args.ts | 1 - .../src/backends/http.ts | 76 ------------------- tests/clickhouse-test-runner/src/main.ts | 17 +---- .../upstream-allowlist.txt | 2 +- 8 files changed, 16 insertions(+), 123 deletions(-) delete mode 100644 tests/clickhouse-test-runner/src/backends/http.ts diff --git a/.github/workflows/upstream-sql-tests.yml b/.github/workflows/upstream-sql-tests.yml index de66e1cf3..416902d33 100644 --- a/.github/workflows/upstream-sql-tests.yml +++ b/.github/workflows/upstream-sql-tests.yml @@ -37,7 +37,6 @@ jobs: strategy: fail-fast: false matrix: - impl: [client, http] clickhouse: [head, latest] # Round-robin shards keep each job at roughly one minute so the # upstream SQL tests no longer dominate PR CI runtime. Bump @@ -99,7 +98,6 @@ jobs: - name: Run upstream SQL tests id: run-tests env: - CLICKHOUSE_CLIENT_CLI_IMPL: ${{ matrix.impl }} CLICKHOUSE_CLIENT_CLI_LOG: ${{ github.workspace }}/upstream-run.log SHARD_INDEX: ${{ matrix.shard }} SHARD_TOTAL: 3 @@ -110,7 +108,7 @@ jobs: if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: upstream-sql-tests-${{ matrix.impl }}-${{ matrix.clickhouse }}-shard-${{ matrix.shard }} + name: upstream-sql-tests-${{ matrix.clickhouse }}-shard-${{ matrix.shard }} retention-days: 14 if-no-files-found: ignore path: | diff --git a/AGENTS.md b/AGENTS.md index 8432285e7..12b10388a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,7 +64,6 @@ The goals of the refactor are: ## Skills - - Each shipped skill must also be listed in the `agents.skills` array of [`packages/client-node/package.json`](packages/client-node/package.json) so downstream tooling can discover it. The [`Skills E2E`](.github/workflows/e2e-skills.yml) workflow @@ -83,7 +82,7 @@ The [`tests/clickhouse-test-runner`](tests/clickhouse-test-runner) harness is a ### What the harness does - Wraps `@clickhouse/client` in a tiny CLI (`bin/clickhouse` → `dist/main.js`) that mimics enough of the upstream `clickhouse-client` binary (same flags, `extract-from-config` shortcut, stdin/`--query` behavior) for the Python `tests/clickhouse-test` runner to drive it without modification. -- Two backend implementations selectable via `CLICKHOUSE_CLIENT_CLI_IMPL`: `client` (uses `@clickhouse/client`) and `http` (raw `fetch` to port 8123). The CI matrix runs both against ClickHouse `latest` and `head` so that we cover both code paths and detect server regressions. The allowlist is also split into round-robin shards (`SHARD_INDEX` / `SHARD_TOTAL`) so each matrix job stays at roughly one minute; bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together if per-shard runtime climbs back above ~1 minute. +- The CI matrix runs the harness against ClickHouse `latest` and `head` so that we exercise `@clickhouse/client` against both server versions and detect server regressions. The allowlist is also split into round-robin shards (`SHARD_INDEX` / `SHARD_TOTAL`) so each matrix job stays at roughly one minute; bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together if per-shard runtime climbs back above ~1 minute. - Reads the curated test list from [`upstream-allowlist.txt`](tests/clickhouse-test-runner/upstream-allowlist.txt) (one test name per line, `#` for comments) and forwards them as positional arguments to `tests/clickhouse-test`. - The `SERVER_SETTINGS`/`CLIENT_ONLY_SETTINGS` allowlists in [`src/settings.ts`](tests/clickhouse-test-runner/src/settings.ts) are copied from the Java port and may need periodic resync as ClickHouse adds or reclassifies settings. @@ -94,9 +93,9 @@ See [`tests/clickhouse-test-runner/README.md`](tests/clickhouse-test-runner/READ The allowlist is grown in **batches of ~100 candidate tests at a time**, in upstream filename order, following this loop: 1. **Pre-filter the candidate batch.** Skip non-SQL tests (`.sh`, `.py`, `.j2`) and tests tagged for unsupported infrastructure (`shard`, `distributed`, `replicated`, `zookeeper`, `kafka`, `s3`, `mysql`, `tls`, etc.). These will never pass through this harness as it stands today. -2. **Run each candidate against both backends** (`CLICKHOUSE_CLIENT_CLI_IMPL=client` and `=http`) using the harness, with `--no-stateful --no-long`. **Only keep tests that report `[ OK ]` on both backends**; drop failures and skips. -3. **Validate against the CI matrix before committing**, not just one local server version. The CI workflow runs `{client, http} × {ClickHouse latest, head} × {shard 1..N}` — a test that passes locally on `head` may fail on `latest` (or vice versa) and break CI. -4. **Beware substring/prefix expansion.** `tests/clickhouse-test` treats positional arguments as **substring/prefix matches** rather than exact names, so an allowlist entry like `00396_uuid` will silently pull in `00396_uuid_v7`, `00712_prewhere_with_alias` will pull in `00712_prewhere_with_alias_bug_2`, etc. When adding an entry whose name is a prefix of any other test in `0_stateless`, prefer the longest unambiguous form, or accept that the siblings come along and verify they all pass on both backends. +2. **Run each candidate through the harness** with `--no-stateful --no-long`. **Only keep tests that report `[ OK ]`**; drop failures and skips. +3. **Validate against the CI matrix before committing**, not just one local server version. The CI workflow runs `{ClickHouse latest, head} × {shard 1..N}` — a test that passes locally on `head` may fail on `latest` (or vice versa) and break CI. +4. **Beware substring/prefix expansion.** `tests/clickhouse-test` treats positional arguments as **substring/prefix matches** rather than exact names, so an allowlist entry like `00396_uuid` will silently pull in `00396_uuid_v7`, `00712_prewhere_with_alias` will pull in `00712_prewhere_with_alias_bug_2`, etc. When adding an entry whose name is a prefix of any other test in `0_stateless`, prefer the longest unambiguous form, or accept that the siblings come along and verify they all pass. 5. **Prune flakes promptly.** If a previously-passing test starts to flake on the nightly run, remove it (or its prefix-expanded siblings) from the allowlist rather than retrying — the allowlist exists to be a stable green signal, not a TODO list. ## When reviewing code changes diff --git a/tests/clickhouse-test-runner/README.md b/tests/clickhouse-test-runner/README.md index 06f603b90..bdd06271f 100644 --- a/tests/clickhouse-test-runner/README.md +++ b/tests/clickhouse-test-runner/README.md @@ -46,14 +46,13 @@ export PATH="/path/to/clickhouse-js/tests/clickhouse-test-runner/bin:$PATH" ## Environment variables -| Variable | Default | Description | -| ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | -| `CLICKHOUSE_CLIENT_CLI_IMPL` | `client` | Backend: `client` (uses `@clickhouse/client`) or `http` (uses Node `fetch` to talk to HTTP 8123). | -| `CLICKHOUSE_CLIENT_CLI_LOG` | `tests/clickhouse-test-runner/.upstream/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | -| `UPSTREAM_CLICKHOUSE_DIR` | `tests/clickhouse-test-runner/.upstream/ClickHouse` | Path to a checkout of `ClickHouse/ClickHouse` containing the upstream test suite. | -| `UPSTREAM_TEST_LIST` | `tests/clickhouse-test-runner/upstream-allowlist.txt` | Path to a file listing the upstream tests to run (one test name per line, `#` for comments). | -| `SHARD_INDEX` | `1` | 1-based index of the shard to run when sharding the allowlist (must be `<= SHARD_TOTAL`). | -| `SHARD_TOTAL` | `1` | Total number of shards. When `> 1`, only tests at positions where `i % SHARD_TOTAL == SHARD_INDEX - 1` are run (round-robin selection). | +| Variable | Default | Description | +| --------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| `CLICKHOUSE_CLIENT_CLI_LOG` | `tests/clickhouse-test-runner/.upstream/clickhouse-client-cli.log` | Path to a log file used to record every shim invocation. Useful for troubleshooting. | +| `UPSTREAM_CLICKHOUSE_DIR` | `tests/clickhouse-test-runner/.upstream/ClickHouse` | Path to a checkout of `ClickHouse/ClickHouse` containing the upstream test suite. | +| `UPSTREAM_TEST_LIST` | `tests/clickhouse-test-runner/upstream-allowlist.txt` | Path to a file listing the upstream tests to run (one test name per line, `#` for comments). | +| `SHARD_INDEX` | `1` | 1-based index of the shard to run when sharding the allowlist (must be `<= SHARD_TOTAL`). | +| `SHARD_TOTAL` | `1` | Total number of shards. When `> 1`, only tests at positions where `i % SHARD_TOTAL == SHARD_INDEX - 1` are run (round-robin selection). | ## Running against the upstream test suite @@ -82,18 +81,11 @@ UPSTREAM_CLICKHOUSE_DIR=/path/to/ClickHouse \ tests/clickhouse-test-runner/scripts/run-upstream-tests.sh --no-stateful ``` -To toggle the backend implementation, set `CLICKHOUSE_CLIENT_CLI_IMPL`: - -```bash -CLICKHOUSE_CLIENT_CLI_IMPL=http \ - tests/clickhouse-test-runner/scripts/run-upstream-tests.sh -``` - ### Extending the allowlist The file `upstream-allowlist.txt` contains the curated list of upstream tests known to pass through this harness. The file format is one test name per line; lines starting with `#` and blank lines are ignored. -**Rule of thumb:** Only add tests that pass on both the `client` and `http` backends. Remove or comment out tests that begin to flake. +**Rule of thumb:** Only add tests that pass reliably. Remove or comment out tests that begin to flake. ### CI @@ -101,7 +93,7 @@ The workflow `.github/workflows/upstream-sql-tests.yml` runs this harness in CI: - Triggered on **workflow_dispatch**, **nightly at 05:00 UTC**, and on **pushes/PRs** that touch `tests/clickhouse-test-runner/**` or the workflow file itself. - The `workflow_dispatch` input `upstream_ref` can be used to pin a specific upstream commit or branch (defaults to `master`). -- The matrix runs every combination of `{impl: client | http} × {clickhouse: head | latest} × {shard: 1..N}`. Sharding is round-robin across the allowlist (see `SHARD_INDEX` / `SHARD_TOTAL` above) so each shard takes roughly one minute. If the allowlist grows enough that per-shard runtime climbs back above ~1 minute, bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together. +- The matrix runs every combination of `{clickhouse: head | latest} × {shard: 1..N}`. Sharding is round-robin across the allowlist (see `SHARD_INDEX` / `SHARD_TOTAL` above) so each shard takes roughly one minute. If the allowlist grows enough that per-shard runtime climbs back above ~1 minute, bump both the `shard` matrix values and the `SHARD_TOTAL` env value in the workflow together. ## Local development diff --git a/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh index d01842399..7f71491e3 100755 --- a/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh +++ b/tests/clickhouse-test-runner/scripts/run-upstream-tests.sh @@ -7,7 +7,6 @@ RUNNER_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" # Read environment variables with defaults UPSTREAM_CLICKHOUSE_DIR="${UPSTREAM_CLICKHOUSE_DIR:-${RUNNER_DIR}/.upstream/ClickHouse}" -CLICKHOUSE_CLIENT_CLI_IMPL="${CLICKHOUSE_CLIENT_CLI_IMPL:-}" CLICKHOUSE_CLIENT_CLI_LOG="${CLICKHOUSE_CLIENT_CLI_LOG:-${RUNNER_DIR}/.upstream/clickhouse-client-cli.log}" UPSTREAM_TEST_LIST="${UPSTREAM_TEST_LIST:-${RUNNER_DIR}/upstream-allowlist.txt}" @@ -87,9 +86,6 @@ mkdir -p "$(dirname "${CLICKHOUSE_CLIENT_CLI_LOG}")" # Export environment for the wrapper export PATH="${RUNNER_DIR}/bin:${PATH}" export CLICKHOUSE_CLIENT_CLI_LOG -if [[ -n "${CLICKHOUSE_CLIENT_CLI_IMPL}" ]]; then - export CLICKHOUSE_CLIENT_CLI_IMPL -fi # Run the upstream test runner cd "${UPSTREAM_CLICKHOUSE_DIR}" diff --git a/tests/clickhouse-test-runner/src/args.ts b/tests/clickhouse-test-runner/src/args.ts index 72b1e5b29..3d8e77287 100644 --- a/tests/clickhouse-test-runner/src/args.ts +++ b/tests/clickhouse-test-runner/src/args.ts @@ -82,7 +82,6 @@ const USAGE_TEXT = [ ' --help Print this help', '', 'Environment variables:', - " CLICKHOUSE_CLIENT_CLI_IMPL Backend implementation: 'client' (default) or 'http'", ' CLICKHOUSE_CLIENT_CLI_LOG Path to log file for troubleshooting', '', ].join('\n') diff --git a/tests/clickhouse-test-runner/src/backends/http.ts b/tests/clickhouse-test-runner/src/backends/http.ts deleted file mode 100644 index 9db162a2e..000000000 --- a/tests/clickhouse-test-runner/src/backends/http.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Buffer } from 'node:buffer' -import type { ParsedArgs } from '../args.js' -import { appendLog } from '../log.js' - -export interface BackendOptions { - args: ParsedArgs - queries: string[] - logPath: string -} - -function buildUrl(args: ParsedArgs): string { - const proto = args.secure ? 'https' : 'http' - const params = new URLSearchParams() - params.set('database', args.database) - params.set('default_format', 'TabSeparated') - if (args.logComment !== null && args.logComment.length > 0) { - params.set('log_comment', args.logComment) - } - if (args.sendLogsLevel !== null && args.sendLogsLevel.length > 0) { - params.set('send_logs_level', args.sendLogsLevel) - } - if (args.maxInsertThreads !== null && args.maxInsertThreads.length > 0) { - params.set('max_insert_threads', args.maxInsertThreads) - } - for (const [k, v] of Object.entries(args.serverSettings)) { - params.set(k, v) - } - return `${proto}://${args.host}:${args.port}/?${params.toString()}` -} - -async function writeChunk(chunk: Uint8Array): Promise { - await new Promise((resolve, reject) => { - const ok = process.stdout.write(chunk, (err) => { - if (err) reject(err) - else if (ok) resolve() - }) - if (!ok) { - process.stdout.once('drain', () => { - resolve() - }) - } - }) -} - -export async function executeWithHttp(opts: BackendOptions): Promise { - const { args, queries, logPath } = opts - const url = buildUrl(args) - const auth = Buffer.from(`${args.user}:${args.password}`).toString('base64') - const headers: Record = { - Authorization: `Basic ${auth}`, - 'Content-Type': 'text/plain; charset=utf-8', - } - - try { - for (const q of queries) { - appendLog(logPath, 'executing_query=' + q) - const response = await fetch(url, { - method: 'POST', - headers, - body: q, - }) - if (!response.ok) { - const text = await response.text() - throw new Error(`HTTP ${response.status}: ${text.trim()}`) - } - if (response.body === null) continue - for await (const chunk of response.body) { - await writeChunk(chunk) - } - } - } catch (err) { - const msg = err instanceof Error ? err.message : String(err) - appendLog(logPath, 'error=' + msg) - throw err - } -} diff --git a/tests/clickhouse-test-runner/src/main.ts b/tests/clickhouse-test-runner/src/main.ts index bba57e57b..a204043ac 100644 --- a/tests/clickhouse-test-runner/src/main.ts +++ b/tests/clickhouse-test-runner/src/main.ts @@ -5,7 +5,6 @@ import { appendLog, resolveLogPath, safeForLog } from './log.js' import { splitQueries } from './split-queries.js' import { handleExtractFromConfig } from './extract-from-config.js' import { executeWithClient } from './backends/client.js' -import { executeWithHttp } from './backends/http.js' async function main(): Promise { const argv = process.argv.slice(2) @@ -62,16 +61,6 @@ async function main(): Promise { return } - const impl = process.env.CLICKHOUSE_CLIENT_CLI_IMPL ?? 'client' - if (impl !== 'client' && impl !== 'http') { - process.stderr.write( - `Unknown CLICKHOUSE_CLIENT_CLI_IMPL value: ${impl}. Supported: client, http\n`, - ) - process.exitCode = 1 - return - } - - appendLog(logPath, 'impl=' + impl) appendLog(logPath, 'database=' + args.database) appendLog(logPath, 'user=' + args.user) appendLog(logPath, 'secure=' + String(args.secure)) @@ -86,11 +75,7 @@ async function main(): Promise { } try { - if (impl === 'client') { - await executeWithClient({ args, queries, logPath }) - } else { - await executeWithHttp({ args, queries, logPath }) - } + await executeWithClient({ args, queries, logPath }) } catch (err) { const msg = err instanceof Error ? err.message : String(err) appendLog(logPath, 'error=' + msg) diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index 3bfedb1dc..d8b5dd99e 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -4,7 +4,7 @@ # Conventions: # - One test name per line (matches the pattern argument of tests/clickhouse-test). # - Lines starting with '#' and blank lines are ignored. -# - Add tests here once they reliably pass on both `client` and `http` backends. +# - Add tests here once they reliably pass through the harness. # - Remove (or comment out with a reason) tests that begin to flake. # - Avoid bare prefixes that overlap with failing tests: `tests/clickhouse-test` # treats positional arguments as substring matches, so e.g. `00396_uuid` would From 965ff6765da37c6bbf2549e1ed8e9f928f445874 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 21:38:02 +0000 Subject: [PATCH 20/23] Expand upstream SQL allowlist with 2,157 client-backend tests (#730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Expand upstream SQL allowlist with 2,157 client-backend tests - [x] Append 2,157 harvested `0_stateless` tests passing on `CLICKHOUSE_CLIENT_CLI_IMPL=client` against ClickHouse `latest` to `tests/clickhouse-test-runner/upstream-allowlist.txt` - [x] Bump the upstream SQL tests matrix from 3 shards to 10 (per review feedback) — `shard` matrix and `SHARD_TOTAL` env updated together in `.github/workflows/upstream-sql-tests.yml` - [x] Remove flaky entries `01660_join_or_inner` and `03653_updating_minmax_idx_after_mutation` from the allowlist - [ ] Follow-up: re-validate against the `http` backend and the `head` ClickHouse image; prune any further entries that don't reproduce on the full CI matrix --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> Co-authored-by: Peter Leonov --- .github/workflows/upstream-sql-tests.yml | 4 +- .../upstream-allowlist.txt | 2162 +++++++++++++++++ 2 files changed, 2164 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upstream-sql-tests.yml b/.github/workflows/upstream-sql-tests.yml index 416902d33..0d7446a04 100644 --- a/.github/workflows/upstream-sql-tests.yml +++ b/.github/workflows/upstream-sql-tests.yml @@ -42,7 +42,7 @@ jobs: # upstream SQL tests no longer dominate PR CI runtime. Bump # `shard` and `SHARD_TOTAL` together if the allowlist grows enough # that per-shard runtime climbs back above ~1 minute. - shard: [1, 2, 3] + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] steps: - name: Checkout clickhouse-js uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -100,7 +100,7 @@ jobs: env: CLICKHOUSE_CLIENT_CLI_LOG: ${{ github.workspace }}/upstream-run.log SHARD_INDEX: ${{ matrix.shard }} - SHARD_TOTAL: 3 + SHARD_TOTAL: 10 run: | bash tests/clickhouse-test-runner/scripts/run-upstream-tests.sh --no-stateful diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index d8b5dd99e..5aacdcc9c 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -493,3 +493,2165 @@ 00810_in_operators_segfault 00812_prewhere_alias_array 01428_hash_set_nan_key + +# --- Added in batch: tests harvested from tests/queries/0_stateless that +# pass through this harness against ClickHouse `latest` on the +# CLICKHOUSE_CLIENT_CLI_IMPL=client backend (see AGENTS.md "Strategy for +# growing the allowlist"). Each entry was filtered to be a unique-prefix +# 0_stateless test stem to avoid the substring/prefix expansion behavior +# of tests/clickhouse-test. +00746_hashing_tuples +00751_hashing_ints +00760_insert_json_with_defaults +00761_lower_utf8_bug +00803_odbc_driver_2_format +00813_parse_date_time_best_effort_more +00814_parsing_ub +00817_with_simple +00822_array_insert_default +00824_filesystem +00834_date_datetime_cmp +00834_not_between +00836_numbers_table_function_zero +00839_bitmask_negative +00840_top_k_weighted +00844_join_lightee2 +00847_multiple_join_same_column +00853_join_with_nulls_crash +00854_multiple_join_asterisks +00876_wrong_arraj_join_column +00880_decimal_in_key +00881_unknown_identifier_in_in +00897_flatten +00898_quantile_timing_parameter_check +00901_joint_entropy +00903_array_with_constant_function +00904_array_with_constant_2 +00905_field_with_aggregate_function_state +00906_low_cardinality_const_argument +00906_low_cardinality_rollup +00907_set_index_with_nullable_and_low_cardinality_bug +00908_analyze_query +00910_decimal_group_array_crash_3783 +00912_string_comparison +00914_join_bgranvea +00915_tuple_orantius +00916_add_materialized_column_after +00916_create_or_replace_view +00917_least_sqr +00917_multiple_joins_denny_crane +00919_histogram_merge +00919_sum_aggregate_states_constants +00920_multiply_aggregate_states_constants +00927_asof_join_noninclusive +00927_asof_joins +00928_multi_match_constant_constant +00930_arrayIntersect +00930_max_partitions_per_insert_block +00931_low_cardinality_nullable_aggregate_function_type +00931_low_cardinality_read_with_empty_array +00931_low_cardinality_set_index_in_key_condition +00932_array_intersect_bug +00935_to_iso_week_first_year +00938_basename +00939_limit_by_offset +00939_test_null_in +00944_minmax_null +00950_default_prewhere +00950_test_gorilla_codec +00952_part_frozen_info +00954_resample_combinator +00956_join_use_nulls_with_array_column +00957_coalesce_const_nullable_crash +00960_eval_ml_method_const +00961_checksums_in_system_parts_columns_table +00961_visit_param_buffer_underflow +00962_enumNotExect +00962_visit_param_various +00963_startsWith_force_primary_key +00964_os_thread_priority +00966_invalid_json_must_not_parse +00968_roundAge +00969_roundDuration +00973_create_table_as_table_function +00974_bitmapContains_with_primary_key +00974_full_outer_join +00975_json_hang +00977_join_use_nulls_denny_crane +00978_ml_math +00978_sum_map_bugfix +00978_table_function_values_alias +00979_quantileExcatExclusive_and_Inclusive +00979_set_index_not +00979_toFloat_monotonicity +00980_crash_nullable_decimal +00980_full_join_crash_fancyqlx +00981_no_virtual_columns +00982_array_enumerate_uniq_ranked +00990_function_current_user +00994_table_function_numbers_mt +00995_optimize_read_in_order_with_aggregation +00996_limit_with_ties +00997_extract_all_crash_6627 +00997_trim +00999_settings_no_extra_quotes +01001_enums_in_in_section +01009_insert_select_data_loss +01009_insert_select_nicelulu +01010_partial_merge_join_const_and_lc +01011_group_uniq_array_memsan +01012_select_limit_x_0 +01013_hex_decimal +01013_hex_float +01015_array_split +01015_attach_part +01015_random_constant +01016_index_tuple_field_type +01016_null_part_minmax +01016_uniqCombined64 +01018_empty_aggregation_filling +01018_optimize_read_in_order_with_in_subquery +01019_array_fill +01020_function_array_compact +01020_function_char +01020_having_without_group_by +01025_array_compact_generic +01026_char_utf8 +01029_early_constant_folding +01030_concatenate_equal_fixed_strings +01030_final_mark_empty_primary_key +01031_pmj_new_any_semi_join +01031_semi_anti_join +01032_cityHash64_for_UUID +01032_cityHash64_for_decimal +01034_order_by_pk_prefix +01034_with_fill_and_push_down_predicate +01035_prewhere_with_alias +01040_h3_get_resolution +01041_h3_is_valid +01042_check_query_and_last_granule_size +01043_categorical_iv +01043_h3_edge_length_m +01044_great_circle_angle +01044_h3_edge_angle +01045_bloom_filter_null_array +01047_no_alias_columns_with_table_aliases +01047_nullable_rand +01047_simple_aggregate_sizes_of_columns_bug +01050_group_array_sample +01051_random_printable_ascii +01051_scalar_optimization +01053_if_chain_check +01055_prewhere_bugs +01056_negative_with_bloom_filter +01061_alter_codec_with_type +01062_pm_multiple_all_join_same_value +01063_create_column_set +01064_pm_all_join_const_and_nullable +01065_array_zip_mixed_const +01066_bit_count +01067_join_null +01068_parens +01069_set_in_group_by +01070_h3_get_base_cell +01070_h3_hex_area_m2 +01070_h3_indexes_are_neighbors +01070_h3_to_parent +01070_h3_to_string +01070_string_to_h3 +01071_in_array +01072_nullable_jit +01072_select_constant_limit +01073_blockSerializedSize +01073_crlf_end_of_line +01075_in_arrays_enmk +01076_array_join_prewhere_const_folding +01076_range_reader_segfault +01078_bloom_filter_operator_not_has +01079_bit_operations_using_bitset +01079_new_range_reader_segfault +01079_order_by_pk +01079_reinterpret_as_fixed_string +01081_keywords_formatting +01083_functional_index_in_mergetree +01083_log_first_column_alias +01085_datetime_arithmetic_preserve_timezone +01085_extract_all_empty +01085_simdjson_uint64 +01086_modulo_or_zero +01087_index_set_ubsan +01087_storage_generate +01088_array_slice_of_aggregate_functions +01090_fixed_string_bit_ops +01091_query_profiler_does_not_hang +01096_array_reduce_in_ranges +01096_block_serialized_state +01096_zeros +01097_one_more_range_reader_test_wide_part +01097_pre_limit +01100_split_by_string +01104_fixed_string_like +01105_string_like +01106_const_fixed_string_like +01107_join_right_table_totals +01109_sc0rp10_string_hash_map_zero_bytes +01112_check_table_with_index +01114_alter_modify_compact_parts +01114_clear_column_compact_parts +01115_prewhere_array_join +01116_cross_count_asterisks +01117_chain_finalize_bug +01117_greatest_least_case +01119_optimize_trivial_insert_select +01120_join_constants +01123_parse_date_time_best_effort_even_more +01124_view_bad_types +01136_multiple_sets +01137_order_by_func_final +01137_sample_final +01143_trivial_count_with_join +01144_join_rewrite_with_ambiguous_column_and_view +01144_multiple_joins_rewriter_v2_and_lambdas +01145_with_fill_const +01163_search_case_insensetive_utf8 +01186_conversion_to_nullable +01189_create_as_table_as_table_function +01197_summing_enum +01198_plus_inf +01199_url_functions_path_without_schema_yiurule +01212_empty_join_and_totals +01213_alter_rename_compact_part +01220_scalar_optimization_in_alter +01234_to_string_monotonic +01247_least_greatest_filimonov +01248_least_greatest_mixed_const +01250_fixed_string_comparison +01251_string_comparison +01252_weird_time_zone +01255_geo_types_livace +01260_ubsan_decimal_parse +01262_fractional_timezone_near_start_of_epoch +01262_low_cardinality_remove +01264_nested_baloo_bear +01266_default_prewhere_reqq +01268_mergine_sorted_limit +01269_alias_type_differs +01272_offset_without_limit +01272_totals_and_filter_bug +01273_lc_fixed_string_field +01275_alter_rename_column_default_expr +01276_alter_rename_column_materialized_expr +01276_random_string +01276_system_licenses +01277_large_tuples +01277_unixTimestamp64_compatibility +01278_alter_rename_combination +01278_variance_nonnegative +01279_dist_group_by +01280_min_map_max_map +01280_null_in +01280_unicode_whitespaces_lexer +01281_join_with_prewhere_fix +01281_sum_nullable +01283_max_threads_simple_query_optimization +01283_strict_resize_bug +01284_view_and_extremes_bug +01285_date_datetime_key_condition +01289_min_execution_speed_not_too_early +01290_empty_array_index_analysis +01291_aggregation_in_order +01292_quantile_array_bug +01293_external_sorting_limit_bug +01296_pipeline_stuck +01300_polygon_convex_hull +01303_polygons_equals +01305_array_join_prewhere_in_subquery +01307_polygon_perimeter +01308_row_policy_and_trivial_count_query +01312_case_insensitive_regexp +01314_position_in_system_columns +01315_count_distinct_return_not_nullable +01318_parallel_final_stuck +01321_aggregate_functions_of_group_by_keys +01321_monotonous_functions_in_order_by_bug +01323_too_many_threads_bug +01324_if_transform_strings_to_enum +01324_settings_documentation +01326_build_id +01326_fixed_string_comparison_denny_crane +01326_hostname_alias +01328_bad_peephole_optimization +01338_uuid_without_separator +01341_datetime64_wrong_supertype +01345_array_join_LittleMaverick +01345_index_date_vs_datetime +01346_array_join_mrxotey +01347_partition_date_vs_datetime +01351_geohash_assert +01351_parse_date_time_best_effort_us +01352_add_datetime_bad_get +01353_nullable_tuple +01353_topk_enum +01354_order_by_tuple_collate_const +01354_tuple_low_cardinality_array_mapped_bug +01355_defaultValueOfArgumentType_bug +01356_initialize_aggregation +01357_result_rows +01359_codeql +01359_geodistance_loop +01360_division_overflow +01362_year_of_ISO8601_week_modificators_for_formatDateTime +01372_wrong_order_by_removal +01374_if_nullable_filimonov +01376_array_fill_empty +01376_null_logical +01379_with_fill_several_columns +01380_nullable_state +01381_for_each_with_states +01385_not_function +01389_filter_by_virtual_columns +01390_check_table_codec +01390_remove_injective_in_uniq +01392_column_resolve +01396_negative_datetime_saturate_to_zero +01398_in_tuple_func +01403_datetime64_constant_arg +01407_lambda_arrayJoin +01410_full_join_and_null_predicates +01410_nullable_key_and_index_negate_cond +01411_from_unixtime +01411_xor_itai_shirav +01412_mod_float +01412_optimize_deduplicate_bug +01413_if_array_uuid +01413_truncate_without_table_keyword +01414_bloom_filter_index_with_const_column +01414_push_predicate_when_contains_with_clause +01415_table_function_view +01416_join_totals_header_bug +01417_update_permutation_crash +01418_index_analysis_bug +01419_materialize_null +01420_logical_functions_materialized_null +01421_array_nullable_element_nullable_index +01422_array_nullable_element_nullable_index +01423_if_nullable_cond +01425_default_value_of_type_name +01426_geohash_constants +01427_pk_and_expression_with_different_type +01430_fix_any_rewrite_aliases +01431_finish_sorting_with_consts +01431_utf8_ubsan +01432_parse_date_time_best_effort_timestamp +01434_netloc_fuzz +01440_big_int_shift +01440_to_date_monotonicity +01441_array_combinator +01450_set_null_const +01451_normalize_query +01452_normalized_query_hash +01453_normalize_query_alias_uuid +01455_nullable_type_with_if_agg_combinator +01455_time_zones +01456_low_cardinality_sorting_bugfix +01456_min_negative_decimal_formatting +01457_compile_expressions_fuzzer +01457_order_by_limit +01457_order_by_nulls_first +01458_count_digits +01458_is_decimal_overflow +01459_decimal_casts +01460_allow_dollar_and_number_in_identifier +01470_columns_transformers2 +01471_limit_by_format +01471_with_format +01474_decimal_scale_bug +01475_fix_bigint_shift +01475_mutation_with_if +01479_cross_join_9855 +01480_binary_operator_monotonicity +01481_join_with_materialized +01485_256_bit_multiply +01490_nullable_string_to_enum +01491_nested_multiline_comments +01492_array_join_crash_13829 +01492_format_readable_quantity +01493_table_function_null +01495_subqueries_in_with_statement_2 +01495_subqueries_in_with_statement_4 +01496_signedness_conversion_monotonicity +01497_alias_on_default_array +01497_extract_all_groups_empty_match +01497_now_support_timezone +01502_bar_overflow +01503_fixed_string_primary_key +01503_if_const_optimization +01504_view_type_conversion +01508_explain_header +01511_different_expression_with_same_alias +01511_format_readable_timedelta +01511_prewhere_with_virtuals +01513_ilike_like_cache +01514_tid_function +01515_with_global_and_with_propagation +01518_cast_nullable_virtual_system_column +01518_filtering_aliased_materialized_column +01518_nullable_aggregate_states1 +01518_select_in_null +01521_alter_enum_and_reverse_read +01521_max_length_alias +01523_date_time_compare_with_date_literal +01523_interval_operator_support_string_literal +01532_min_max_with_modifiers +01532_tuple_with_name_type +01533_distinct_depends_on_max_threads +01533_distinct_nullable_uuid +01534_lambda_array_join +01537_fuzz_count_equal +01540_verbatim_partition_pruning +01543_parse_datetime_besteffort_or_null_empty_string +01544_errorCodeToName +01548_lzy305 +01550_mutation_subquery +01552_impl_aggfunc_cloneresize +01554_bloom_filter_index_big_integer_uuid +01554_interpreter_integer_float +01555_or_fill +01556_accurate_cast_or_null +01556_if_null +01560_DateTime_and_DateTime64_comparision +01560_cancel_agg_func_combinator_native_name_constraint +01560_monotonicity_check_multiple_args_bug +01561_Date_and_DateTime64_comparision +01561_aggregate_functions_of_key_with_join +01567_system_processes_current_database +01576_if_null_external_aggregation +01580_column_const_comparision +01582_any_join_supertype +01582_deterministic_function_with_predicate +01583_const_column_in_set_index +01585_fuzz_bits_with_bugfix +01592_length_map +01592_toUnixTimestamp_Date +01593_functions_in_order_by +01596_full_join_chertus +01600_encode_XML +01600_min_max_compress_block_size +01600_multiple_left_join_with_aliases +01600_remerge_sort_lowered_memory_bytes_ratio +01602_array_aggregation +01603_decimal_mult_float +01605_dictinct_two_level +01605_key_condition_enum_int +01614_with_fill_with_limit +01615_two_args_function_index_fix +01616_untuple_access_field +01620_fix_simple_state_arg_type +01621_decode_XML +01623_byte_size_const +01626_cnf_test +01631_date_overflow_as_partition_key +01632_nullable_string_type_convert_to_decimal_type +01632_select_all_syntax +01633_limit_fuzz +01634_summap_nullable +01635_nullable_fuzz +01637_nullable_fuzz3 +01638_div_mod_ambiguities +01646_fix_window_funnel_inconistency +01648_normalize_query_keep_names +01649_with_alias_key_condition +01650_expressions_merge_bug +01651_group_uniq_array_enum +01654_bar_nan +01655_quarter_modificator_for_formatDateTime +01655_test_isnull_mysql_dialect +01655_window_functions_bug +01655_window_functions_null +01656_ipv4_bad_formatting +01656_join_defaul_enum +01656_test_hex_mysql_dialect +01657_array_element_ubsan +01657_test_toHour_mysql_compatibility +01658_test_base64Encode_mysql_compatibility +01659_array_aggregation_ubsan +01659_test_base64Decode_mysql_compatibility +01660_join_or_all +01660_test_toDayOfYear_mysql_compatibility +01661_join_complex +01661_test_toDayOfWeek_mysql_compatibility +01662_join_mixed +01662_test_toDayOfMonth_mysql_compatibility +01663_aes_msan +01663_quantile_weighted_overflow +01663_test_toDate_mysql_compatibility +01664_array_slice_ubsan +01664_ntoa_aton_mysql_compatibility +01665_merge_tree_min_for_concurrent_read +01665_substring_ubsan +01666_date_lut_buffer_overflow +01666_great_circle_distance_ubsan +01668_avg_weighted_ubsan +01668_test_toMonth_mysql_dialect +01669_join_or_duplicates +01669_test_toYear_mysql_dialect +01670_test_repeat_mysql_dialect +01671_test_toQuarter_mysql_dialect +01672_actions_dag_merge_crash +01672_test_toSecond_mysql_dialect +01673_test_toMinute_mysql_dialect +01674_filter_by_uint8 +01674_unicode_asan +01676_round_int_ubsan +01677_array_enumerate_bug +01678_great_circle_angle +01679_format_readable_time_delta_inf +01680_predicate_pushdown_union_distinct_subquery +01681_arg_min_max_if_fix +01684_geohash_ubsan +01685_json_extract_double_as_float +01690_quantilesTiming_ubsan +01692_DateTime64_from_DateTime +01698_fix_toMinute +01699_timezoneOffset +01700_deltasum +01700_mod_negative_type_promotion +01702_bitmap_native_integers +01702_rewrite_avg_for_algebraic_optimization +01702_toDateTime_from_string_clamping +01703_rewrite_aggregate_function_case_insensitive +01704_transform_with_float_key +01706_optimize_normalize_count_variants +01710_aggregate_projection_with_grouping_set +01710_aggregate_projection_with_hashing +01710_aggregate_projection_with_monotonic_key_expr +01710_minmax_count_projection_constant_query +01710_minmax_count_projection_count_nullable +01710_minmax_count_projection_modify_partition_key +01710_normal_projection_fix1 +01710_normal_projection_format +01710_normal_projection_join_plan_fix +01710_projection_aggregate_functions_null_for_empty +01710_projection_aggregation_in_order +01710_projection_array_join +01710_projection_detach_part +01710_projection_external_aggregate +01710_projection_in_set +01710_projection_materialize_with_missing_columns +01710_projection_mutation +01710_projection_optimize_aggregators_of_group_by_keys +01710_projection_optimize_group_by_function_keys +01710_projection_part_check +01710_projection_query_plan_optimization_misc +01710_projection_row_policy +01710_projection_with_ast_rewrite_settings +01710_projection_with_joins +01710_projection_with_nullable_keys +01710_projections_group_by_no_key +01711_decimal_multiplication +01716_array_difference_overflow +01718_subtract_seconds_date +01719_join_timezone +01720_union_distinct_with_limit +01732_explain_syntax_union_query +01733_transform_ubsan +01735_to_datetime64 +01736_null_as_default +01744_tuple_cast_to_map_bugfix +01746_convert_type_with_default +01746_lc_values_format_bug +01746_test_for_tupleElement_must_be_constant_issue +01747_transform_empty_arrays +01748_partition_id_pruning +01753_mutate_table_predicated_with_table +01761_cast_to_enum_nullable +01761_round_year_bounds +01762_datetime64_extended_parsing +01763_filter_push_down_bugs +01765_tehran_dst +01766_todatetime64_no_timezone_arg +01768_array_product +01769_extended_range_2 +01770_add_months_ubsan +01770_extended_range_3 +01771_datetime64_no_time_part +01772_intdiv_minus_one_ubsan +01773_case_sensitive_revision +01773_case_sensitive_version +01773_min_max_time_system_parts_datetime64 +01774_case_sensitive_connection_id +01774_tuple_null_in +01778_where_with_column_name +01779_quantile_deterministic_msan +01780_column_sparse_pk +01781_map_op_ubsan +01781_token_extractor_buffer_overflow +01786_group_by_pk_many_streams +01787_arena_assert_column_nothing +01795_TinyLog_rwlock_ub +01796_Log_rwlock_ub +01797_StripeLog_rwlock_ub +01798_uniq_theta_union_intersect_not +01800_log_nested +01803_const_nullable_map +01803_untuple_subquery +01811_datename +01811_filter_by_null +01812_has_generic +01813_quantileBfloat16_nans +01818_case_float_value_fangyc +01821_to_date_time_ubsan +01822_union_and_constans_error +01825_json_type_18 +01825_json_type_order_by +01825_json_type_partitions +01833_test_collation_alvarotuso +01835_alias_to_primary_key_cyfdecyf +01837_cast_to_array_from_empty_array +01839_join_to_subqueries_rewriter_columns_matcher +01840_tupleElement_formatting_fuzzer +01845_add_testcase_for_arrayElement +01846_alter_column_without_type_bugfix +01851_fix_row_policy_empty_result +01851_s2_to_geo +01852_cast_operator_2 +01852_cast_operator_3 +01852_cast_operator_4 +01852_jit_if +01852_s2_get_neighbours +01855_jit_comparison_constant_result +01865_aggregator_overflow_row +01866_aggregate_function_interval_length_sum +01866_bit_positions_to_array +01866_datetime64_cmp_with_constant +01868_order_by_fill_with_datetime64 +01869_function_modulo_legacy +01869_reinterpret_as_fixed_string_uuid +01871_merge_tree_compile_expressions +01881_to_week_monotonic_fix +01881_union_header_mismatch_bug +01889_tokenize +01891_not_in_partition_prune +01891_not_like_partition_prune +01891_partition_by_uuid +01906_lc_in_bug +01906_partition_by_multiply_by_zero +01908_with_unknown_column +01911_logical_error_minus +01912_bad_cast_join_fuzz +01913_fix_column_transformer_replace_format +01913_if_int_decimal +01913_join_push_down_bug +01913_summing_mt_and_simple_agg_function_with_lc +01914_index_bgranvea +01914_ubsan_quantile_timing +01915_json_extract_raw_string +01916_low_cardinality_interval +01917_prewhere_column_type +01922_array_join_with_index +01925_date_date_time_comparison +01925_test_group_by_const_consistency +01926_bin_unbin +01926_date_date_time_supertype +01926_union_all_schmak +01936_quantiles_cannot_return_null +01938_joins_identifiers +01940_pad_string +01940_totimezone_operator_monotonicity +01942_untuple_transformers_msan +01943_log_column_sizes +01946_profile_sleep +01948_heredoc +01960_lambda_precedence +02001_join_on_const +02001_select_with_filter +02002_sampling_and_unknown_column_bug +02003_bug_from_23515 +02006_todatetime64_from_string +02006_use_constants_in_with_and_select +02013_emptystring_cast +02015_division_by_nullable +02015_order_by_with_fill_misoptimization +02016_order_by_with_fill_monotonic_functions_removal +02017_columns_with_dot_2 +02017_order_by_with_fill_redundant_functions +02019_multiple_weird_with_fill +02020_cast_integer_overflow +02020_exponential_smoothing_cross_block +02021_map_bloom_filter_index +02021_map_has +02021_prewhere_always_true_where +02022_array_full_text_bloom_filter_index +02023_parser_number_binary_literal +02025_having_filter_column +02025_subcolumns_compact_parts +02026_accurate_cast_or_default +02026_arrayDifference_const +02027_arrayCumSumNonNegative_const +02028_system_data_skipping_indices_size +02028_tokens +02030_function_mapContainsKeyLike +02030_quantiles_underflow +02032_short_circuit_least_greatest_bug +02036_jit_short_circuit +02041_conversion_between_date32_and_datetime64 +02041_openssl_hash_functions_test +02042_map_get_non_const_key +02045_like_function +02047_alias_for_table_and_database_name +02053_INSERT_SELECT_MATERIALIZED +02095_function_get_os_kernel_version +02096_date_time_1970_saturation2 +02097_initializeAggregationNullable +02098_date32_comparison +02100_limit_push_down_bug +02100_now64_types_bug +02100_replaceRegexpAll_bug +02111_function_mapExtractKeyLike +02111_with_fill_no_rows +02112_skip_index_set_and_or +02113_base64encode_trailing_bytes_1 +02113_untuple_func_alias +02118_show_create_table_rocksdb +02124_empty_uuid +02124_uncompressed_cache +02125_low_cardinality_int256 +02125_transform_decimal_bug +02126_lc_window_functions +02129_window_functions_disable_optimizations +02131_materialize_column_cast +02131_remove_columns_in_subquery +02131_row_policies_combination +02131_skip_index_not_materialized +02132_empty_mutation_livelock +02133_final_prewhere_where_lowcardinality_replacing +02148_cast_type_parsing +02148_issue_32737 +02149_issue_32487 +02150_replace_regexp_all_empty_match +02151_lc_prefetch +02151_replace_regexp_all_empty_match_alternative +02152_count_distinct_optimization +02152_short_circuit_throw_if +02154_bitmap_contains +02155_nested_lc_defalut_bug +02155_parse_date_lowcard_default_throw +02156_storage_merge_prewhere_not_ready_set_bug +02157_line_as_string_output_format +02158_interval_length_sum +02160_h3_hex_area_Km2 +02160_monthname +02160_special_functions +02161_array_first_last +02162_array_first_last_index +02163_operators +02165_h3_edge_length_km +02167_columns_with_dots_default_values +02169_fix_view_offset_limit_setting +02176_toStartOfWeek_overflow_pruning +02178_column_function_insert_from +02179_bool_type +02179_key_condition_no_common_type +02180_group_by_lowcardinality +02187_test_final_and_limit_modifier +02188_table_function_format +02189_join_type_conversion +02190_current_metrics_query +02191_parse_date_time_best_effort_more_cases +02205_map_populate_series_non_const +02205_postgresql_functions +02206_array_starts_ends_with +02207_key_condition_floats +02207_ttl_move_if_exists +02209_short_circuit_node_without_parents +02210_append_to_dev_dull +02210_toColumnTypeName_toLowCardinality_const +02212_cte_and_table_alias +02220_array_join_format +02224_s2_test_const_columns +02226_low_cardinality_text_bloom_filter_index +02232_partition_pruner_mixed_constant_type +02232_partition_pruner_single_point +02233_optimize_aggregation_in_order_prefix_with_merge +02234_position_case_insensitive_utf8 +02240_asof_join_biginteger +02240_get_type_serialization_streams +02241_array_first_last_or_null +02241_short_circuit_short_column +02242_if_then_else_null_bug +02242_negative_datetime64 +02242_optimize_to_subcolumns_no_storage +02242_throw_if_constant_argument +02243_in_ip_address +02243_ipv6_long_parsing +02245_format_string_stack_overflow +02245_join_with_nullable_lowcardinality_crash +02247_fix_extract_parser +02248_nullable_custom_types_to_string +02251_last_day_of_month +02265_cross_join_empty_list +02265_limit_push_down_over_window_functions_bug +02267_output_format_prometheus +02267_special_operator_parse_alias_check +02267_type_inference_for_insert_into_function_null +02277_full_sort_join_misc +02285_hex_bin_support_more_types +02286_convert_decimal_type +02286_function_wyhash +02292_hash_array_tuples +02293_ilike_on_fixed_strings +02293_optimize_aggregation_in_order_Array_functions +02294_optimize_aggregation_in_order_prefix_Array_functions +02294_system_certificates +02295_global_with_in_subquery +02296_nullable_arguments_in_array_filter +02302_clash_const_aggegate_join +02304_grouping_set_order_by +02306_window_move_row_number_fix +02307_join_get_array_null +02310_generate_multi_columns_with_uuid +02310_uuid_v7 +02312_is_not_null_prewhere +02313_cross_join_dup_col_names +02313_dump_column_structure_low_cardinality +02313_multiple_limits +02313_negative_datetime64 +02313_test_fpc_codec +02315_pmj_union_ubsan_35857 +02315_replace_multiif_to_if +02316_const_string_intersact +02316_literal_no_octal +02316_values_table_func_bug +02320_alter_columns_with_dots +02320_mapped_array_witn_const_nullable +02321_nested_short_circuit_functions +02325_dates_schema_inference +02326_settings_changes_system_table +02336_sort_optimization_with_fill +02337_multiple_joins_original_names +02338_analyzer_constants_basic +02340_analyzer_functions +02343_analyzer_lambdas_issue_28083 +02343_analyzer_lambdas_issue_36677 +02345_analyzer_subqueries +02345_create_table_allow_trailing_comma +02345_partial_sort_transform_optimization +02346_position_countsubstrings_zero_byte +02346_system_tokenizers +02346_text_index_bug47393 +02346_text_index_bug54541 +02346_text_index_bug84805 +02346_text_index_bug87887 +02346_text_index_bug93432 +02346_text_index_byte_size_reporting +02346_text_index_collapsingmergetree +02346_text_index_default_granularity +02346_text_index_detach_attach +02346_text_index_dictionary_frontcoding +02346_text_index_direct_read_crash +02346_text_index_direct_read_nullable +02346_text_index_direct_read_with_query_condition_cache +02346_text_index_functions_with_empty_needle +02346_text_index_materialize_subcolumns +02346_to_hour_monotonicity_fix_2 +02347_rank_corr_nan +02347_rank_corr_size_overflow +02351_Map_combinator_dist +02353_ascii +02353_isnullable +02353_partition_prune_nullable_key +02354_array_lowcardinality +02354_numeric_literals_with_underscores +02354_tuple_element_with_default +02354_tuple_lowcardinality +02354_vector_search_and_other_skipping_indexes +02354_vector_search_default_granularity +02354_vector_search_multiple_indexes +02354_vector_search_subquery +02354_vector_search_unquoted_index_parameters +02355_column_type_name_lc +02355_control_block_size_in_aggregator +02360_small_notation_h_for_hour_interval +02363_mapupdate_improve +02364_window_case +02366_asof_optimize_predicate_bug_37813 +02366_explain_query_tree +02366_normalize_aggregate_function_types_and_states +02367_analyzer_table_alias_columns +02367_optimize_trivial_count_with_array_join +02368_analyzer_table_functions +02370_extractAll_regress +02371_select_projection_normal_agg +02374_combine_multi_if_and_count_if_opt +02374_in_tuple_index +02374_regexp_replace +02375_double_escaping_json +02375_scalar_lc_cte +02380_analyzer_join_sample +02381_parseDateTime64BestEffortUS +02383_schema_inference_hints +02386_analyzer_in_function_nested_subqueries +02387_analyzer_cte +02392_every_setting_must_have_documentation +02393_every_metric_must_have_documentation +02394_every_profile_event_must_have_documentation +02395_every_merge_tree_setting_must_have_documentation +02398_subquery_where_pushdown_and_limit_offset +02401_merge_tree_old_tmp_dirs_cleanup +02405_pmj_issue_40335 +02406_try_read_datetime64_bug +02408_to_fixed_string_short_circuit +02409_url_format_detection +02410_csv_empty_fields_inference +02410_to_decimal_or_default +02414_all_new_table_functions_must_be_documented +02415_all_new_functions_must_be_documented +02416_in_set_same_ast_diff_columns +02416_row_policy_always_false_index +02417_from_select_syntax +02418_tautological_if_index +02420_key_condition_actions_dag_bug_40599 +02421_decimal_in_precision_issue_41125 +02423_json_quote_float64 +02426_to_string_nullable_fixedstring +02427_column_nullable_ubsan +02427_msan_group_array_resample +02428_batch_nullable_assert +02428_delete_with_settings +02428_index_analysis_with_null_literal +02429_combinators_in_array_reduce +02429_offset_pipeline_stuck_bug +02430_initialize_aggregation_with_combinators +02431_single_value_or_null_empty +02452_check_low_cardinality +02454_compressed_marks_in_compact_part +02455_extract_fixed_string_from_nested_json +02456_BLAKE3_hash_function_test +02456_aggregate_state_conversion +02457_datediff_via_unix_epoch +02457_key_condition_with_types_that_cannot_be_nullable +02457_morton_coding_with_mask +02460_prewhere_row_level_policy +02462_distributions +02467_cross_join_three_table_functions +02467_set_with_lowcardinality_type +02468_has_any_tuple +02471_wrong_date_monotonicity +02473_extract_low_cardinality_from_json +02473_map_element_nullable +02474_extract_fixedstring_from_json +02474_timeDiff_UTCTimestamp +02474_unhex_in_fix_string +02475_analyzer_join_tree_subquery +02475_analyzer_subquery_compound_expression +02475_bad_cast_low_cardinality_to_string_bug +02475_date_time_schema_inference_bug +02475_or_function_alias_and_const_where +02475_positive_modulo +02476_analyzer_join_with_unused_columns +02477_analyzer_ast_key_condition_crash +02477_exists_fuzz_43478 +02477_fuse_quantiles +02477_is_null_parser +02477_logical_expressions_optimizer_issue_89803 +02478_analyzer_table_expression_aliases +02479_analyzer_aggregation_crash +02479_analyzer_aggregation_totals_rollup_crash_fix +02479_if_with_null_and_cullable_const +02479_nullable_primary_key_non_first_column +02480_every_asynchronous_metric_must_have_documentation +02480_interval_casting_and_subquery +02480_max_map_null_totals +02480_parse_date_time_best_effort_math_overflow +02481_aggregation_in_order_plan +02481_analyzer_optimize_grouping_sets_keys +02481_low_cardinality_with_short_circuit_functins_mutations +02481_xxh3_hash_function +02482_if_with_nothing_argument +02489_analyzer_indexes +02493_analyzer_sum_if_to_count_if +02493_analyzer_table_functions_untuple +02494_analyzer_cte_resolution_in_subquery_fix +02494_array_function_range +02494_parser_string_binary_literal +02494_query_cache_empty_tuple +02495_sum_if_to_count_if_bug +02497_analyzer_sum_if_count_if_pass_crash_fix +02498_analyzer_aggregate_functions_arithmetic_operations_pass_fix +02499_analyzer_set_index +02499_escaped_quote_schema_inference +02499_quantile_nan_ubsan_msan +02500_analyzer_storage_view_crash_fix +02501_analyzer_expired_context_crash_fix +02502_analyzer_insert_select_crash_fix +02503_in_lc_const_args_bug +02503_join_switch_alias_fuzz +02504_bar_fractions +02504_explain_ast_insert +02506_date_time64_floating_point_negative_value +02508_index_analysis_to_date_timezone +02510_group_by_prewhere_null +02513_analyzer_duplicate_alias_crash_fix +02513_broken_datetime64_init_on_mac +02514_tsv_zero_started_number +02515_analyzer_null_for_empty +02515_and_or_if_multiif_not_return_lc +02515_distinct_zero_size_key_bug_44831 +02515_generate_ulid +02515_projections_with_totals +02516_projections_with_rollup +02517_union_columns_order +02517_uuid_parsing +02518_qualified_asterisks_alias_table_name +02519_monotonicity_fuzz +02521_analyzer_aggregation_without_column +02521_cannot_find_column_in_projection +02523_range_const_start +02525_analyzer_function_in_crash_fix +02525_jit_logical_functions_nan +02530_ip_part_id +02531_semi_join_null_const_bug +02532_analyzer_aggregation_with_rollup +02532_profileevents_server_startup_time +02535_analyzer_limit_offset +02535_ip_parser_not_whole +02536_replace_with_nonconst_needle_and_replacement +02537_system_formats +02538_analyzer_create_table_as_select +02538_ngram_bf_index_with_null +02538_nullable_array_tuple_timeseries +02539_generate_random_ip +02539_generate_random_low_cardinality +02539_generate_random_map +02541_analyzer_grouping_sets_crash_fix +02541_empty_function_support_ip +02541_multiple_ignore_with_nested_select +02541_tuple_element_with_null +02542_case_no_else +02542_table_function_format +02553_json_type_attach_partition +02554_format_json_columns_for_empty +02559_add_parts +02559_ip_types_bloom +02559_multiple_read_steps_in_prewhere_fuzz +02559_multiple_read_steps_in_prewhere_missing_columns_2 +02559_nested_multiple_levels_default +02560_count_digits +02564_date_format +02564_read_in_order_final_desc +02565_update_empty_nested +02567_and_consistency +02568_and_consistency +02568_array_map_const_low_cardinality +02569_order_by_aggregation_result +02572_max_intersections +02576_predicate_push_down_sorting_fix +02577_analyzer_array_join_calc_twice +02578_ipv4_codec_t64 +02580_like_substring_search_bug +02584_range_ipv4 +02587_csv_big_numbers_inference +02591_bson_long_tuple +02662_sparse_columns_mutations_4 +02662_sparse_columns_mutations_5 +02674_and_consistency +02674_date_int_string_json_inference +02674_null_default_structure +02675_is_ipv6_function_fix +02676_distinct_reading_in_order_analyzer +02677_decode_url_component +02677_get_subcolumn_array_of_tuples +02679_explain_merge_tree_prewhere_row_policy +02680_instr_alias_for_position_case_insensitive +02681_aggregation_by_partitions_bug +02690_subquery_identifiers +02692_multiple_joins_unicode +02698_marked_dropped_tables +02699_polygons_sym_difference_rollup +02699_polygons_sym_difference_total_analyzer +02700_regexp_operator +02705_grouping_keys_equal_keys +02705_projection_and_ast_optimizations_bug +02707_analyzer_nested_lambdas_types +02709_generate_random_valid_decimals_and_bools +02710_date_diff_aliases +02710_topk_with_empty_array +02711_trim_aliases +02713_array_low_cardinality_string +02713_ip4_uint_compare +02714_date_date32_in +02715_or_null +02716_int256_arrayfunc +02717_pretty_json +02720_row_policy_column_with_dots +02724_function_in_left_table_clause_asof_join +02724_jit_logical_functions +02725_agg_projection_respect_PK +02725_alias_with_restricted_keywords +02731_in_operator_with_one_size_tuple +02733_distinct +02733_fix_distinct_in_order_bug_49622 +02734_big_int_from_float_ubsan +02734_optimize_group_by +02734_sparse_columns_short_circuit +02735_array_map_array_of_tuples +02736_bit_count_big_int +02746_index_analysis_binary_operator_with_null +02751_match_constant_needle +02751_multiif_to_if_crash +02751_parallel_replicas_bug_chunkinfo_not_set +02752_custom_separated_ignore_spaces_bug +02752_is_null_priority +02763_jit_compare_functions_nan +02763_mutate_compact_part_with_skip_indices_and_projections +02764_index_analysis_fix +02766_bitshift_with_const_arguments +02769_compare_functions_nan +02770_jit_aggregation_nullable_key_fix +02771_if_constant_folding +02771_jit_functions_comparison_crash +02771_log_faminy_truncate_count +02782_inconsistent_formatting_and_constant_folding +02782_values_null_to_lc_nullable +02784_move_all_conditions_to_prewhere_analyzer_asan +02784_projections_read_in_order_bug +02784_schema_inference_null_as_default +02785_date_predicate_optimizations_ast_query_tree_rewrite +02785_summing_merge_tree_datetime64 +02786_transform_float +02787_transform_null +02789_jit_cannot_convert_column +02789_set_index_nullable_condition_bug +02790_keyed_hash_bug +02790_url_multiple_tsv_files +02795_full_join_assert_cast +02796_projection_date_filter_on_view +02797_aggregator_huge_mem_usage_bug +02797_transform_narrow_types +02798_generic_transform +02799_transform_empty_arrays +02801_transform_nullable +02802_with_cube_with_totals +02804_intersect_bad_cast +02806_cte_block_cannot_be_empty +02807_lower_utf8_msan +02807_math_unary_crash +02808_aliases_inside_case +02809_has_subsequence +02810_initcap +02810_row_binary_with_defaults +02810_system_jemalloc_bins +02811_insert_schema_inference +02811_parallel_replicas_prewhere_count +02811_read_in_order_and_array_join_bug +02812_bug_with_unused_join_columns +02812_csv_date_time_with_comma +02812_from_to_utc_timestamp +02812_large_varints +02812_subquery_operators +02813_any_value +02813_array_agg +02813_float_parsing +02813_func_today_and_alias +02813_system_licenses_base +02814_order_by_tuple_window_function +02815_alias_to_length +02815_empty_subquery_nullable_bug +02815_first_line +02815_fix_not_found_constants_col_in_block +02816_has_token_empty +02818_parameterized_view_with_cte_multiple_usage +02828_create_as_table_function_rename +02831_ast_fuzz_asan_join +02831_trash +02832_integer_type_inference +02832_transform_fixed_string_no_default +02833_array_join_columns +02833_sparse_columns_tuple_function +02834_array_exists_segfault +02834_sparse_columns_sort_with_limit +02835_nested_array_lowcardinality +02841_group_array_sorted +02841_join_filter_set_sparse +02841_not_ready_set_join_on +02841_tuple_modulo +02841_with_clause_resolve +02843_date_predicate_optimizations_bugs +02845_domain_rfc_support_ipv6 +02845_join_on_cond_sparse +02845_prewhere_preserve_column +02861_filter_pushdown_const_bug +02861_interpolate_alias_precedence +02861_uuid_format_serialization +02862_uuid_reinterpret_as_numeric +02863_decode_html_component +02863_mutation_where_in_set_result_cache_pipeline_stuck_bug +02864_filtered_url_with_globs +02864_profile_event_part_lock +02864_statistics_bug_69589 +02864_test_ipv4_type_mismatch +02866_size_of_marks_skip_idx_explain +02867_null_lc_in_bug +02867_nullable_primary_key_final +02868_distinct_to_count_optimization +02869_unicode_minus +02871_join_on_system_errors +02874_infer_objects_as_named_tuples +02874_parse_json_as_json_each_row_on_no_metadata +02875_final_invalid_read_ranges_bug +02875_fix_column_decimal_serialization +02875_json_array_as_string +02876_json_incomplete_types_as_strings_inference +02876_sort_union_of_sorted +02882_primary_key_index_in_function_different_types +02883_read_in_reverse_order_virtual_column +02884_interval_operator_support_plural_literal +02884_parallel_window_functions_bug +02884_virtual_column_order_by +02886_binary_like +02888_integer_type_inference_in_if_function +02888_obsolete_settings +02888_single_state_nullable_type +02889_system_drop_format_schema +02890_partition_prune_in_extra_columns +02891_alter_update_adaptive_granularity +02891_functions_over_sparse_columns +02893_bad_sample_view +02893_trash_optimization +02895_cast_operator_bug +02896_optimize_array_exists_to_has_with_date +02900_decimal_sort_with_multiple_columns +02900_window_function_with_sparse_column +02902_show_databases_limit +02903_bug_43644 +02903_parameterized_view_explain_ast +02907_filter_pushdown_crash +02908_alter_column_alias +02910_nullable_enum_cast +02911_analyzer_remove_unused_projection_columns +02911_cte_invalid_query_analysis +02911_support_alias_column_in_indices +02911_system_symbols +02912_group_array_sample +02913_sum_map_state +02915_analyzer_fuzz_1 +02916_analyzer_set_in_join +02916_set_formatting +02917_transform_tsan +02919_ddsketch_quantile +02919_segfault_nullable_materialized_update +02920_fix_json_merge_patch +02920_unary_operators_functions +02921_bit_hamming_distance_big_int +02921_fuzzbits_with_array_join +02923_cte_equality_disjunction +02923_explain_expired_context +02923_join_use_nulls_modulo +02931_ubsan_error_arena_aligned_alloc +02932_non_ready_set_stuck +02933_compare_with_bool_as_string +02935_ipv6_bit_operations +02935_ipv6_from_uint128_equality +02935_ipv6_from_uint128_one +02935_ipv6_from_uint128_two +02935_ipv6_from_uint128_with_bit_and +02935_ipv6_to_and_from_uint128 +02941_any_RESPECT_NULL_sparse_column +02943_exprs_order_in_group_by_with_rollup +02943_positional_arguments_bugs +02943_tokenbf_and_ngrambf_indexes_support_match_function +02943_use_full_text_skip_index_with_has_any +02943_variant_element +02944_variant_as_common_type_analyzer +02945_blake3_msan +02946_literal_alias_misclassification +02947_dropped_tables_parts +02950_reading_array_tuple_subcolumns +02953_slow_create_view +02955_sparkBar_alias_sparkbar +02956_fix_to_start_of_milli_microsecond +02956_format_constexpr +02959_system_database_engines +02961_sumMapFiltered_keepKey +02962_analyzer_const_in_count_distinct +02962_analyzer_constant_set +02962_indexHint_rpn_construction +02963_single_value_destructor +02965_projection_with_partition_pruning +02967_index_hint_crash +02968_full_sorting_join_fuzz +02968_sumMap_with_nan +02969_analyzer_eliminate_injective_functions +02969_functions_to_subcolumns_if_null +02970_generate_series +02971_functions_to_subcolumns_map +02971_functions_to_subcolumns_variant +02974_analyzer_array_join_subcolumn +02974_if_with_map +02975_intdiv_with_decimal +02981_translate_fixedstring +02982_dont_infer_exponent_floats +02985_if_over_big_int_decimal +02986_leftpad_fixedstring +02987_group_array_intersect +02989_group_by_tuple +02991_count_rewrite_analyzer +02992_all_columns_should_have_comment +02994_cosineDistanceNullable +02995_preliminary_filters_duplicated_columns_SimpleAggregateFunction +02995_ulid_short_circuit +02998_analyzer_prewhere_report +02998_ipv6_hashing +02998_pretty_format_print_readable_number_if_last_column +02998_system_dns_cache_table +02999_ulid_short_circuit +03000_minmax_index_first +03000_virtual_columns_in_prewhere +03001_analyzer_nullable_nothing +03001_consider_lwd_when_merge +03001_data_version_column +03002_map_array_functions_with_low_cardinality +03002_sample_factor_where +03003_enum_and_string_compatible +03003_sql_json_nonsense +03006_mv_deduplication_throw_if_async_insert +03006_parallel_replicas_cte_explain_syntax_crash +03007_column_nullable_uninitialzed_value +03008_groupSortedArray_field +03010_read_system_parts_table_test +03010_sum_to_to_count_if_nullable +03010_view_prewhere_in +03011_adaptative_timeout_compatibility +03013_addDays_with_timezone +03013_fuzz_arrayPartialReverseSort +03013_repeat_with_nonnative_integers +03014_analyzer_group_by_use_nulls +03014_analyzer_groupby_fuzz_60317 +03015_analyzer_groupby_fuzz_60772 +03016_analyzer_groupby_fuzz_59796 +03017_analyzer_groupby_fuzz_61600 +03018_analyzer_greater_null +03023_analyzer_optimize_group_by_function_keys_with_nulls +03023_remove_unused_column_distinct +03031_input_format_allow_errors_num_bad_escape_sequence +03031_low_cardinality_logical_error +03031_table_function_fuzzquery +03032_multi_search_const_low_cardinality +03032_numbers_zeros +03032_redundant_equals +03032_scalars_create_as_select +03032_variant_bool_number_not_suspicious +03033_analyzer_parametrized_view_alias +03033_create_as_copies_comment +03033_cte_numbers_memory +03033_distinct_transform_const_columns +03033_from_unixtimestamp_joda_by_int64 +03033_virtual_column_override +03033_with_fill_interpolate +03034_json_extract_variant +03035_argMinMax_numeric_non_extreme_bug +03035_morton_encode_no_rows +03036_prewhere_lambda_function +03036_with_numbers +03037_dot_product_overflow +03037_union_view +03038_ambiguous_column +03039_unknown_identifier_window_function +03040_alias_column_join +03040_array_sum_and_join +03040_dynamic_type_alters_1_compact_merge_tree +03040_dynamic_type_alters_1_wide_merge_tree +03040_dynamic_type_alters_2_compact_merge_tree +03041_analyzer_gigachad_join +03041_select_with_query_result +03042_analyzer_alias_join +03043_group_array_result_is_expected +03044_array_join_columns_in_nested_table +03045_analyzer_alias_join_with_if +03046_column_in_block_array_join +03047_analyzer_alias_join +03047_group_by_field_identified_aggregation +03048_not_found_column_xxx_in_block +03050_select_one_one_one +03051_many_ctes +03052_query_hash_includes_aliases +03054_analyzer_join_alias +03057_analyzer_subquery_alias_join +03061_analyzer_alias_as_right_key_in_join +03064_analyzer_named_subqueries +03065_analyzer_cross_join_and_array_join +03066_analyzer_global_with_statement +03067_analyzer_complex_alias_join +03069_analyzer_with_alias_in_array_join +03070_analyzer_CTE_scalar_as_numbers +03072_analyzer_missing_columns_from_subquery +03073_analyzer_alias_as_column_name +03074_analyzer_alias_column_in_view +03075_analyzer_subquery_alias +03079_analyzer_numeric_literals_as_column_names +03080_analyzer_prefer_column_name_to_alias__virtual_columns +03081_analyzer_agg_func_CTE +03082_analyzer_left_join_correct_column +03084_analyzer_join_column_alias +03085_analyzer_alias_column_group_by +03086_analyzer_window_func_part_of_group_by +03087_analyzer_subquery_with_alias +03089_analyzer_alias_replacement +03090_analyzer_multiple_using_statements +03093_analyzer_column_alias +03093_analyzer_miel_test +03093_virtual_column_override_group_by +03093_with_fill_support_constant_expression +03094_named_tuple_bug24607 +03094_transform_return_first +03096_largest_triangle_3b_crash +03096_update_non_indexed_columns +03097_query_log_join_processes +03099_analyzer_multi_join +03100_lwu_05_basics +03100_lwu_08_multiple_blocks +03100_lwu_19_nullable +03100_lwu_33_add_column +03100_lwu_34_multistep_prewhere +03100_lwu_37_update_all_columns +03100_lwu_44_missing_default +03100_lwu_45_query_condition_cache +03100_lwu_47_apply_patches_after_drop_column +03100_lwu_49_update_skip_indexes +03101_analyzer_invalid_join_on +03108_describe_union_all +03109_ast_too_big +03110_unicode_alias +03112_analyzer_not_found_column_in_block +03115_alias_exists_column +03116_analyzer_explicit_alias_as_column_name +03117_analyzer_same_column_name_as_func +03118_analyzer_multi_join_prewhere +03122_analyzer_collate_in_window_function +03127_argMin_combinator_state +03129_cte_with_final +03129_low_cardinality_nullable_non_first_primary_key +03130_abs_in_key_condition_bug +03130_analyzer_array_join_prefer_column +03142_alter_comment_parameterized_view +03142_skip_ANSI_in_UTF8_compute_width +03143_ttl_in_system_parts_columns_table +03144_aggregate_states_with_different_types +03144_alter_column_and_read +03145_unicode_quotes +03146_bug47862 +03146_tpc_ds_grouping +03149_variant_pop_back_typo +03151_analyzer_view_read_only_necessary_columns +03151_pmj_join_non_procssed_clash +03151_redundant_distinct_with_window +03155_datasketches_ubsan +03155_explain_current_transaction +03156_tuple_map_low_cardinality +03161_decimal_binary_math +03161_ipv4_ipv6_equality +03164_analyzer_rewrite_aggregate_function_with_if +03164_create_as_default +03164_early_constant_folding_analyzer +03165_distinct_with_window_func_crash +03165_order_by_duplicate +03165_string_functions_with_token_text_indexes +03167_empty_tuple_concat +03167_fancy_quotes_off_by_one +03167_parametrized_view_with_cte +03168_cld2_tsan +03168_fuzz_multiIf_short_circuit +03169_display_column_names_in_footer +03169_modify_column_data_loss +03169_optimize_injective_functions_inside_uniq_crash +03170_part_offset_as_table_column +03173_distinct_combinator_alignment +03174_merge_join_bug +03195_group_concat_deserialization_fix +03196_max_intersections_arena_crash +03197_fix_parse_mysql_iso_date +03198_group_array_intersect +03198_json_extract_more_types +03199_fix_auc_tie_handling +03199_queries_with_new_analyzer +03200_reverse_by_separator +03200_subcolumns_join_use_nulls +03201_analyzer_resolve_in_parent_scope +03201_sumIf_to_countIf_return_type +03203_count_with_non_deterministic_function +03203_drop_detached_partition_all +03203_system_numbers_limit_and_offset_simple +03204_index_hint_fuzzer +03205_column_type_check +03205_hashing_empty_tuples +03205_json_cast_from_string +03206_is_null_constant_result_old_analyzer_bug +03208_datetime_cast_losing_precision +03208_groupArrayIntersect_serialization +03208_numbers_total_rows_approx +03208_uniq_with_empty_tuple +03209_functions_json_msan_fuzzer_issue +03210_fix_single_value_data_assertion +03210_lag_lead_inframe_types +03210_nested_short_circuit_functions_bug +03210_variant_with_aggregate_function_type +03211_convert_outer_join_to_inner_join_anti_join +03213_array_element_msan +03214_join_on_tuple_comparison_elimination_bug +03214_json_typed_dynamic_path +03215_fix_get_index_in_tuple +03215_key_condition_bug +03215_partition_in_tuple +03215_toStartOfWeek_with_dateTime64_fix +03215_varian_as_common_type_integers +03215_view_with_recursive +03217_fliter_pushdown_no_keys +03217_primary_index_memory_leak +03221_key_condition_bug +03224_trim_empty_string +03225_const_prewhere_non_ataptive +03227_test_sample_n +03228_dynamic_subcolumns_from_subquery +03228_join_to_rerange_right_table +03228_url_engine_response_headers +03228_variant_permutation_issue +03229_empty_tuple_in_array +03229_json_structure_comparison +03229_query_condition_cache_cte_constant_folding +03230_anyHeavy_merge +03230_system_projections +03231_prewhere_conditions_order +03235_groupArray_string_consistency +03236_create_query_ttl_where +03237_create_table_select_as_with_recursive +03237_get_subcolumn_low_cardinality_column +03237_max_map_state_decimal_serialization +03238_analyzer_unknown_function +03240_cte_in_subquery +03241_view_block_structure +03242_view_block_structure +03243_array_join_lambda +03243_to_start_of_interval_aliases +03245_views_and_filter_push_down_bug +03246_toStartOfInterval_date_timezone_bug +03247_json_extract_lc_nullable +03249_dynamic_alter_consistency +03250_ephemeral_comment +03252_merge_tree_min_bytes_to_seek +03254_project_lwd_respects_row_exists +03254_uniq_exact_two_level_negative_zero +03255_fix_sbstrings_logical_error +03257_json_escape_file_names +03258_multiple_array_joins +03258_old_analyzer_const_expr_bug +03258_quantile_exact_weighted_issue +03259_join_condition_executed_block_bug +03259_negate_key_overflow +03260_dynamic_low_cardinality_dict_bug +03261_any_respect_camelCase_aliases +03261_minmax_indices_by_default_table_copy +03261_sort_cursor_crash +03262_column_sizes_with_dynamic_structure +03262_system_functions_should_not_fill_query_log_functions +03266_with_fill_staleness_cases +03267_join_swap_bug +03268_system_parts_index_granularity +03269_partition_key_not_in_set +03270_empty_tuple_in_array_intersect +03270_fix_column_modifier_write_order +03271_decimal_monotonic_day_of_week +03272_json_to_json_cast_6 +03272_partition_pruning_monotonic_func_bug +03273_dynamic_pretty_json_serialization +03273_group_by_in_order_still_used_when_group_by_key_doesnt_match_order_by_key +03274_grace_hash_max_joined_block_size_rows_bug +03274_philipzucker +03274_with_fill_dup_sort_bug +03275_subcolumns_in_primary_key_bug +03276_index_of_assume_sorted +03278_dateTime64_in_dateTime64_bug +03280_dynamic_if_null +03281_dynamic_coalesce +03282_parallel_join_with_additional_filter +03285_analyzer_extract_common_expr_bug +03285_analyzer_optimize_disjunctions +03286_collation_locale_with_modifier +03286_format_datetime_timezones +03286_serialization_hint_system_columns +03287_format_datetime_mysqlfraction +03289_tuple_element_to_subcolumn +03290_force_normal_projection +03290_partial_arrayROCAUC_and_arrayAUCPR +03290_pr_non_replicated_in_subquery +03299_deep_nested_map_creation +03299_map_named_tuple +03299_pretty_squash +03300_nested_json_empty_keys +03301_is_ipv4_string +03303_alias_inverse_order +03303_dynamic_in_not_xor +03304_pretty_fallback_to_vertical +03306_optimize_table_force_keyword +03310_aggregate_projection_count_nullable +03312_analyzer_unused_projection_fix +03313_h3togeo_result_order +03314_analyzer_resolve_in_parent_scope_2 +03314_analyzer_resolve_in_parent_scope_3 +03314_analyzer_resolve_in_parent_scope_5 +03314_has_column_in_table_alias_column +03314_nullable_key_no_optimize_functions_to_subcolumns +03314_summing_merge_tree_final_not_found_column_in_block +03315_analyzer_correlated_subqueries +03315_array_join_scalar +03315_quantile_bfloat16_ubsan +03321_join_on_is_null_lowcardinality +03322_unused_interpolate_expressions +03323_bfloat16_least_supertype +03323_union_all_constants_bug +03325_alter_modify_projection_primary_key_column +03325_count_summing_merge_tree_order_by_tuple +03326_toStartOfNanosecond_ubsan +03328_formatting_assignment_expression +03352_distinct_sorted_bug +03352_lazy_column_filter_by_uint8 +03354_translate_crap +03355_issue_32743 +03356_analyzer_unused_scalar_subquery +03357_block_structure_union_step +03358_block_structure_match +03359_point_in_polygon_index +03363_constant_nullable_key +03363_function_keccak256 +03364_pretty_json_bool +03365_dynamic_column_datetime +03365_finish_sorting_crash +03365_time64_casts +03365_time_implicit_conversion +03365_time_time64_aggregate_functions +03365_time_time64_best_effort_parsing +03365_time_to_time64_conv_bug +03366_bfloat16_sorting +03366_with_fill_dag +03367_bfloat16_tuple_final +03368_bfloat16_merge_join +03369_bfloat16_map +03370_join_identifiers +03371_bfloat16_special_values +03371_nullable_tuple_string_comparison +03374_date_trunc_with_negatives +03375_bloom_filter_array_equals +03375_bool_partition +03380_accurate_cast_or_null_qbit +03389_regexp_rewrite_nullable_group_by +03392_crash_group_by_use_nulls +03393_smallest_index_floating_point +03394_fix_to_start_of_interval_for_zero_origin_argument +03395_global_join_supported_kind +03399_divide_zero_or_null +03399_mapContains_functions +03399_sparse_grams +03400_datetime64_key_condition_with_number +03401_remote_bool +03402_materialized_tuple_element +03402_zero_streams_after_max_streams_to_max_threads_ratio +03403_read_in_order_streams_memory_usage +03404_bfloat16_insert_values +03404_dynamic_in_interval_bug +03404_geotoh3_input_order +03404_ubsan_distinct_join_const_column +03405_bool_array_to_fixed_strings +03406_reservoir_sample_self_merging +03407_parse_date_time_best_effort_unix_timestamp_with_fraction +03411_analyzer_scalar_correlated_subquery +03411_iceberg_bucket +03411_summing_merge_tree_dynamic_values +03413_analyzer_correlated_subqueries_bug_2 +03414_formatDateTime_compound_formatter_after_varsize_formatter +03415_dont_highlight_probable_hashes +03443_index_match_alternatives +03444_lm_block_mismatch +03447_float_nan_order +03447_function_reverse_for_tuple +03447_grouping_sets_analyzer_const_columns +03447_window_functions_distinct +03448_topk_merging +03449_window_cannot_find_column +03450_parameterized_view_forward +03451_parameterized_views_without_alias +03454_parameterized_view_constant_identifier +03454_parameterized_views_null +03458_numeric_indexed_vector_operations_i8f64 +03458_numeric_indexed_vector_operations_u16f64 +03458_numeric_indexed_vector_operations_u32f32 +03458_numeric_indexed_vector_operations_u32f64 +03458_numeric_indexed_vector_operations_u32i16 +03458_numeric_indexed_vector_operations_u32i32 +03458_numeric_indexed_vector_operations_u32i64 +03458_numeric_indexed_vector_operations_u32i8 +03458_numeric_indexed_vector_operations_u32u16 +03458_numeric_indexed_vector_operations_u32u32 +03458_numeric_indexed_vector_operations_u32u64 +03458_numeric_indexed_vector_operations_u32u8 +03459_join_cannot_add_column +03459_numeric_indexed_vector_decode +03459_socket_asynchronous_metrics +03460_numeric_indexed_vector_to_value_map +03461_numeric_indexed_vector_chain +03461_projection_index_multi_part +03462_numeric_indexed_vector_serialization +03509_stripe_log_compatible_types +03511_formatDateTime_e_space_padding +03513_filter_push_down_rand_bug +03513_resize_pipeline_after_totals +03515_array_join_different_sizes +03516_comparison_pk_bug +03516_int_exp2_join +03517_logical_join_predicate_push_down_with_pre_expression_bug +03518_left_to_cross_incorrect +03519_fulter_push_down_duplicate_column_name_bug +03519_left_to_cross_incorrect +03519_merge_tree_part_info_coverage +03520_left_to_cross_incorrect +03521_bitNot_String_NUL_terminated +03521_system_unicode +03521_tuple_of_dynamic_with_string_comparison +03522_alter_modify_column_and_materialize_projection +03524_nullable_extremes +03525_json_extract_datetime64_from_numbers +03525_json_infer_array_of_dynamic_from_array_of_different_types +03532_divideOrNull_jit_crash +03532_dynamic_column_inside_map_rollback +03538_analyzer_correlated_query_collect_columns_fix +03538_analyzer_scalar_correlated_subquery_fix +03538_higher_order_functions_null_filter +03545_array_join_index_set_bug +03545_map_contains_bloom_index_bug +03545_union_allow_column_with_no_common_type +03547_analyzer_correlated_subqueries +03548_optimize_syntax_fuse_functions_clash +03549_conv_function +03549_system_dimensional_metrics +03550_variant_extend_union +03551_cast_decimal_to_float +03555_inconsistent_formatting_ttl +03560_low_cardinality_keys_filter +03562_json_date_as_integer +03563_coarser_minmax_indexes_first +03565_clickhouse_smaller_indexes_first +03567_json_extract_case_insensitive_edge_cases +03568_ddsketch_merge +03568_json_extract_case_insensitive +03571_join_inequality_constants +03573_concurrent_hash_scatter_bug +03577_assert_on_estimated_block_size_bytes +03577_hash_output_format +03577_ub_max_column_in_block_size_bytes +03577_vairant_lazy_materialization_bug +03579_system_columns_column_alias +03579_zero_copy_aggregating_final_anyLast +03580_external_merge_sort_with_lazy_columns +03580_join_runtime_filter_column_type +03580_join_runtime_filter_pushdown +03581_bool_literal_column_name +03582_initcap_fixedstring +03582_normalize_utf8_empty +03593_any_join_swap_tables +03593_backup_with_broken_projection +03594_alias_subcolumns +03594_coalescing_merge_tree_segfault +03594_push_more_filters_down_joins +03598_json_enum_default_value_in_typed_path +03599_bad_date_and_datetimes_inference +03600_analyzer_setting_bool +03600_replace_fixed_string_bug +03601_histogram_quantile +03601_inconsistent_table_names +03601_json_from_string_accurate_cast_or_null +03601_replace_regex_fixedstring_empty_needle +03602_query_system_tables_definer +03604_and_join_use_nulls_bug_83977 +03604_join_reorder_pinned_bug +03604_key_condition_set_tuple_bug +03604_plan_step_description_limit +03606_nullable_json_group_by +03611_cte_deterministic +03611_uniqExact_bug +03613_empty_tuple_permute_with_limit +03620_json_advanced_shared_data_seek_bug +03622_generic_aggregate_functions__state_compatibility +03623_datetime64_preepoch_fractional_precision +03623_lazy_materialization_array_sizes_bug +03624_resource_exhaustion_window_function +03625_case_without_condition_non_constant_branches +03625_upper_lower_utf8_different_number_of_code_points +03627_non_constant_replacement_in_replace_regexp +03628_named_tuple_element_in_order_by_key +03628_parse_date_time_short_circuit +03630_hash_join_max_block_size +03630_join_blocks_with_different_constness +03631_array_of_empty_tuples +03632_default_minmax_indices_alter +03632_join_logical_assert_85403 +03633_negative_limit_offset +03633_set_index_bulk_filtering +03639_hash_of_dynamic_column +03640_skip_indexes_with_or_and_not +03640_variant_array_null_map_subcolumn +03641_analyzer_issue_85834 +03641_json_array_of_float_and_bool +03643_paste_join_disable_filter_pushdown +03643_system_instrumentation_no_suspicious_lowcardinality +03644_join_order_mixed_comma_and_left +03651_positional_argument_agg_projection +03652_coalescing_merge_tree_fix_empty_tuple +03652_union_columns_2 +03653_fractional_limit_offset +03654_case_non_constant_null +03654_grouping_sets_any_min_max +03656_nan_comparison +03657_gby_overflow_any_sparse +03657_rollup_constant +03666_count_matches_complexity +03667_pr_join_with_cross_join_on_left +03672_columns_same_as_subcolumns +03699_reverse_utf8 +03701_limit_by_in_order +03702_encode_decode_memory_usage +03703_statistics_low_cardinality +03704_default_empty_order_by +03704_fractional_limit_with_ties +03705_count_if_asterisk +03705_fix_compression_T64_unaligned +03707_statistics_cache +03708_exact_rows_before_limit_in +03708_join_or_to_right_any_bug +03708_low_cardinality_aggregate_state_compatibility +03708_statistics_estimator_cast_type +03709_final_prewhere_special_columns +03710_argAndMinMax +03712_json_advanced_shared_data_bug +03713_group_by_injective_function_old_analyzer +03714_base32_base58_short_string +03714_queries_escaping_1 +03714_queries_escaping_2 +03716_join_duplicate_columns_89411 +03717_system_unicode_enums +03719_generic_hash_over_constant_and_non_constant +03719_ntile_no_partition_by_check +03720_datetime64_bad_inference +03721_check_natural_sort_key +03721_insert_replicated_array_nested_sizes_check +03721_join_residual_condition_bug_88635 +03721_statistics_alter_type_bug +03722_iceberg_hash_bug +03722_json_compact_part_substreams_cache_bug +03723_incorrect_implicit_projection +03724_to_date_time_or_null_negative_arg_bug +03725_empty_tuple_some_limit_with_ties_distinct +03725_json_dynamic_subcolumn_prewhere_zero_index_granularity_bytes +03725_variant_element_null_map_subcolumn_prewhere_zero_index_granularity_bytes +03726_array_union_with_dynamic_argument +03727_block_structure_mismatch_after_filter_push_down +03727_ipv4_parsing_bug +03727_named_tuples_pretty_format +03727_tolowcardinality_nullable_cast +03728_analyzer_identifier_resolution_join +03728_explain_column_structure +03731_null_parts_in_storage_snapshot_with_only_analyze +03732_json_duplicated_path_in_dynamic_paths_and_shared_data_bug +03732_json_duplicated_path_in_dynamic_paths_and_shared_data_compact_part_bug +03732_toweek_partition_pruning +03733_anti_join_runtime_filter_3 +03733_base58_decode_bug +03733_summing_merge_tree_nested_low_cardinality +03741_subcolumns_of_materialized_columns_in_mutation +03742_array_join_empty_tuple +03742_lazy_materialization_of_array_after_alter_add_column +03742_test_flattened_crash +03743_fix_estimator_crash +03748_tuple_of_sparse_elements_bug +03749_cross_join_use_nulls_matcher +03749_implicit_index_ephemeral_alias +03751_summing_coalescing_merge_tree_sparse_columns_in_header +03752_fractional_limit_offset_small_blocks +03752_join_part +03753_anti_join_runtime_filter_4 +03753_log_engine_shared_prefixes_bug +03754_fractional_limit_offset_multiple_streams +03754_h3_polygon_to_cells_const +03755_final_prewhere_duplicate_columns +03756_sparse_serialization_nullable_in_tuple +03757_ast_not_formatting +03761_count_distinct_optimization_window +03761_inconsistent_ast_formatting +03761_lazy_materialization_split_expr +03762_count_distinct_optimization_multiple_columns +03763_column_conflicting_with_subcolumn +03763_no_new +03764_empty_tuple_permutation_bug +03766_subcolumns_resolution_in_aliases +03767_implicit_minmax_index_dynamic_variant +03768_rename_alias_column_implicit_index +03769_implicit_index_alias_simple_reference +03771_tokens_crash +03773_join_on_formatting +03773_json_with_typed_path_skip +03773_lightweight_update_index_subquery +03773_nullable_sparse_join +03774_json_skip_regexp_partial_match +03775_join_on_disk_max_files_to_merge +03777_merge_engine_with_aliases_bug +03777_top_k_dynamic_filtering_respect_header_bug +03779_lexer_pointer_overflow_ubsan +03780_has_all_tokens_empty_array +03780_json_typed_paths_with_same_prefix +03781_json_max_dynamic_subcolumns_control_on_parsing +03781_merge_hostname_remote +03781_split_join_filter_bug +03784_correlated_subquery_from_storage +03784_materialize_index_subcolumns +03784_msan_token_iterator +03786_empty_tuple_map_array +03788_hash_join_with_empty_right_side_skip_left_side +03788_header_after_outer_to_semi_or_anti_conversion +03788_statistics_part_pruning_nan +03789_right_join_column_replicated +03790_uniqTheta_error +03791_decimal_string_zero +03791_function_to_subcolumns_optimization_on_subcolumns +03791_system_parts_files_column +03792_jit_date32 +03792_jit_time64 +03792_sparse_offsets_in_substreams_cache_compact_part +03793_logical_expressions_optimizer_group_by_use_nulls +03793_with_fill_staleness_long_jump +03794_read_in_order_virtual_row_and_sparse_primary_key_msan +03795_hash_output_format_blocks +03797_set_index_multiple_conditions_same_expression +03798_nullable_primary_key_logical_error +03799_any_left_join_isnotnull_crash +03799_cross_to_inner_join_view_with_join +03799_parallel_right_join_flag_per_row +03800_string_size_stream_in_nested_types +03801_domain_empty_string_msan +03805_array_filter_nullable_lambda +03807_top_k_limit_zero +03808_correlated_subquery_with_totals +03808_with_duplicate_alias_nested_scopes +03811_inconsistent_formatting_ttl_where_alias +03811_sparse_column_aggregation_with_sum +03812_column_function_prewhere_filter +03812_reading_subcolumns_of_aliases +03815_array_of_json_in_tuple_element +03815_tuple_element_with_json +03821_direct_join_empty_table +03821_json_skip_path_fix +03821_uniq_speedup_boundaries +03822_insert_null_into_non_nullable_enum +03822_join_reorder_fix_expression_sources +03822_lttb_date32_negative +03822_window_function_large_preceding_offset +03823_prefetched_read_pool_priority_assertion +03823_skip_index_disjunction_oob +03825_array_with_constant_fixed_string +03825_bitxor_if_mixed_types +03825_join_right_sorting_empty_maps +03825_xxh3_128_hash_function +03826_array_join_in_bloom_filter +03827_cross_join_no_right_columns +03832_array_join_filter_push_down_cross_join +03832_log_engine_alias_subcolumn +03833_server_ast_fuzzer +03835_todate_monotonicity_boundary +03902_jit_cast_to_bool +03905_grouping_sets_use_nulls_lowcardinality_in_tuple +03905_set_partition_pruning_key_condition +03905_skip_index_set_null +03906_group_by_lowcardinality_parallel_hash_join +03908_lazy_materialization_replacing_merge_tree +03909_union_all_remote_predicate_pushdown +03910_limit_always_read_till_end_with_constant_false_having +03911_ephemeral_virtual_column_old_analyzer +03913_jemalloc_cache_arena_metrics +03913_minmax_statistics_nan_ubsan_error +03913_text_index_preprocessor_expression +03916_coalescing_merge_tree_const_column_from_index +03916_contingency_state_variant_serialization_match +03916_except_intersect_all_multiset +03916_lazy_materialization_union_all +03918_allow_nullable_tuple_in_extracted_subcolumns_not_changeable +03918_if_transform_strings_to_enum_nullable +03918_insert_squashing_const_different_values +03919_json_lazy_type_hints_incompatible +03919_variant_adaptor_lowcardinality +03921_internal_functions_have_internal_category +03921_merge_tree_settings_readonly +03923_union_all_cte_skip_index_prewhere +03924_constant_granularity_last_mark +03924_duplicated_key_with_literal_and_nested_object_in_typed_paths_in_json +03925_exists_with_mutations_execute_on_initator +03925_if_nullable_const_branch +03925_json_shared_data_buckets_missing_stream_bug +03925_sparse_values_in_substreams_cache_bug +03925_tuple_comparison_nothing_type +03926_group_by_use_nulls_constant_type +03926_semi_join_swap_tables +03927_join_use_nulls_subcolumn +03927_json_typed_path_tuple_element +03928_full_join_swap_tables +03928_json_advanced_shared_data_bug +03928_merge_lambda_alias_columns +03976_geometry_functions_accept_subtypes +03979_datetime64_preepoch_partition_pruning +03984_in_single_column_reference +03985_parenthesized_from_join +03988_concat_with_separator_format_variant_low_cardinality +03988_grace_hash_join_leftover_blocks +03989_codec_ignore_nulls_formatting +03992_join_using_remote_table_function_with_alias +03993_join_no_overlapping_columns_85622 +04001_logical_error_with_cube +04002_sign_jit_miscompilation +04003_interpolate_element_with_alias_formatting +04004_finalize_projection_parts_synchronously +04004_insert_compound_select_formatting +04004_kql_array_sort_const +04005_case_with_expression_nullable_nothing +04005_correlated_column_in_lambda +04005_window_single_line_formatting +04005_with_fill_limit_by +04006_convert_join_to_in_dummy_column +04006_correlated_columns_collector_scope +04006_not_subquery_not_like_formatting +04008_count_distinct_optimization_with_qualify +04008_log_engine_nested_subcolumn_cache +04010_column_replicated_handling_in_partial_sorting_transform +04010_explain_actions_pretty_and_verbose +04010_filter_pushdown_non_boolean_output +04010_null_safe_cmp_dynamic_const +04012_versioned_collapsing_final_filter +04013_loop_function_output_columns +04014_json_compact_each_row_null_string +04016_exists_group_by_resolution +04017_column_alias_set_operations +04021_join_on_false_chunk_rows +04024_jit_multiif_mixed_types +04024_prewhere_parquet_keycondition +04026_prewhere_qualify_text_index +04027_final_prewhere_constant_where_remove_unused_columns +04028_recursive_cte_remote_view_segfault +04028_squashing_with_strict_bounds_bytes_underestimation +04029_read_in_order_expression_virtual_row +04029_with_fill_staleness_cross_chunk +04030_array_fold_memory_peak_guard +04030_text_index_condition_logical_error +04032_json_subcolumn_in_projection_sorting_key +04033_exists_with_limit_offset +04033_top_k_dynamic_filter_nullable_string +04034_any_left_join_in_subquery_default_value +04035_toDaysInMonth +04037_map_key_subcolumn_tuple_value_mergetree +04038_check_table_sparse_tuple_dynamic +04039_explain_union_settings_formatting_roundtrip +04039_text_index_direct_read_select_alias +04039_text_index_map_values_subcolumn +04040_clear_column_with_projection_index +04040_window_funnel_strict_deduplication +04041_format_create_as_select_with_output_options +04042_materialized_cte_union +04042_text_index_prewhere_alias_select +04043_text_index_in_with_preprocessor +04044_recursive_cte_remote_view_actual_recursion +04045_ambiguous_column_alias_values +04045_values_insert_trailing_comment +04046_format_alter_modify_query_with_settings +04046_midpoint_mixed_types +04046_sipHash128Keyed_map_with_empty_arrays +04047_arrow_uuid_nested_reader +04049_aggregate_function_numeric_indexed_vector_self_merge +04049_arrayRemove_nullable_tuple +04049_cuturlparameter_pattern_matching +04049_remote_subquery_in_unresolved_table_function_argument +04049_statistics_enum_invalid_value +04049_values_clause_standard_sql +04049_with_func_alias_in_view +04050_accurateCastOrDefault_const +04050_explain_ast_insert_with_output_options +04050_lc_nullable_query_parameters +04050_prune_array_join_numeric_index +04050_time64_cast_uint64_no_wrap +04050_truncate_order_by_after_group_by_keys +04051_array_first_last_variant +04051_clear_column_rebuild_dependencies +04051_runtime_filter_variant_type +04052_kql_array_sort_row_independence +04053_limit_push_down_union +04053_merge_tree_analyze_indexes_union_all +04053_render_tab_as_spaces_regression +04054_block_mismatch +04054_sample_offset_formatting_ambiguity +04054_transform_const_default_column +04056_highlight_null_terminator +04057_explain_except_ast_formatting +04058_positive_modulo_ubsan +04058_statistics_bad_cast +04061_array_dot_product_empty_arrays +04062_join_order_optimizer_outer_join +04064_alias_datetime_timezone_cast +04064_format_cte_column_aliases +04064_join_on_or_is_not_distinct_from +04064_join_use_nulls_apply_lambda_multiple_joins +04064_parameterized_view_scalar_subquery +04064_planner_duplicate_column_identifier +04064_recursive_cte_multiple_subqueries +04068_geo_wkt_wkb_uppercase_aliases +04068_lazy_materialization_map_nested_alter_add +04068_window_function_type_mismatch_with_rollup_and_group_by_use_nulls +04070_array_exists_to_has_null +04070_decimal_min_max_jit_signedness +04070_join_using_type_coercion_round +04070_pad_string_asan_overflow +04070_subnormal_float_literal +04071_jit_decimal_aggregates +04072_jit_decimal_expressions +04073_float_itoa_fast_path +04074_map_dynamic_substreams_cache +04074_where_large_int_boolean_predicate +04075_analyzer_inline_view_subcolumns +04075_float_to_string_format +04076_array_symmetric_difference_nullable_tuple +04076_materialized_cte_dependency_ordering +04076_tuple_json_dotted_subcolumn_bug +04077_formatdatetime_w_is_a_variable_length_formatter +04077_lwd_qualified_column +04077_vector_similarity_cosine_msan +04086_03711_positive_modulo_tuple +04093_negative_time_datetime_comparison +04094_sz_find_null_needle_oob +04094_text_index_like_large_postings +04096_text_index_json_subcolumn +04101_crosstab_window_state_serialization_pool +04102_text_index_alias_direct_read +04103_cast_string_to_date_time_mode_nullable +04103_full_sorting_merge_left_join_nullable_json +04103_plain_rewritable_get_last_modified_bug +04104_functions_context_lifetime_in_defaults +04108_replicated_and_non_replicated_columns_merge +04113_has_subsequence_empty_haystack +04132_column_sparse_ops +04143_if_decimal_int_literal_jit +04143_ub_parsing_datetime +04145_url_glob_parallelize_output +04146_obfuscate_query_known_identifiers +04146_short_circuit_evaluation_for_nulls +04146_to_hour_monotonicity_dt64_timezone +04200_fix_time64_scale_conversion +04201_to_millisecond_negative_datetime64 +04202_array_dot_product_combine_overflow +04202_parsedatetime_or_null_zero_post_instruction_errors +04202_projection_prewhere_materialize +04202_variant_basic_mode_only_nulls +04207_cross_join_starts_with_memory +04217_array_concat_json_extract_keys_index +04217_create_table_as_with_settings +4060_row_policy_projection_not_found_column From 41cfa57ebffcab885fb013d07aaa6f311dc0a601 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 01:50:08 +0200 Subject: [PATCH 21/23] Expand upstream SQL allowlist with 545 additional passing tests (#731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Grows the curated upstream SQL allowlist in `tests/clickhouse-test-runner/upstream-allowlist.txt` from 2,636 to 3,157 entries (+545 net new tests after dedupe) so the harness covers more of `ClickHouse/ClickHouse`'s `0_stateless` suite against `@clickhouse/client`. - **Candidate selection** — enumerated `0_stateless/*.sql` tests not yet in the allowlist (~5k) and pre-filtered names tagged for unsupported infra (`shard`, `replicated`, `zookeeper`, `kafka`, `s3`, `mysql`, `tls`, …). - **Batched local run** — executed the candidates in batches of 500 through `scripts/run-upstream-tests.sh --no-stateful --no-long` against ClickHouse `latest`; kept only `[ OK ]` results (572 tests). - **Prefix-collision guard** — dropped 12 entries whose names are substrings of other test files in `queries/0_stateless` (e.g. `00800_function_java_hash` would pull in `00800_function_java_hash_with_unsigined_types`), per the rule documented in `AGENTS.md` and the allowlist header. - **Concurrency reproducibility** — re-ran the safe set in a single pass and dropped 15 ALTER/mutation-heavy tests that pass individually but time out at 30 s under parallel load (e.g. `01710_projection_optimize_materialize`, `02662_sparse_columns_mutations_*`, `03285_materialize_ttl_only_drop_parts`). - **Out of scope (not in this PR)** — investigation of a representative failing test for a possible client-side fix is deferred; the run captured ~1,730 `[ FAIL ]` candidates as triage input for a follow-up (notable client-suspect surfaces include `00080_show_tables_and_system_tables`, `00076_ip_coding_functions`, `00037_totals_limit`, `00063_check_query`). CI (`upstream-sql-tests.yml`, matrix `{head, latest} × shard 1..10`) is the source of truth — any newly added test that flakes there should be removed rather than retried. ## Checklist - [x] A human-readable description of the changes was provided to include in CHANGELOG Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com> --- .../upstream-allowlist.txt | 541 +++++++++++++++++- 1 file changed, 528 insertions(+), 13 deletions(-) diff --git a/tests/clickhouse-test-runner/upstream-allowlist.txt b/tests/clickhouse-test-runner/upstream-allowlist.txt index 5aacdcc9c..3b8342b2b 100644 --- a/tests/clickhouse-test-runner/upstream-allowlist.txt +++ b/tests/clickhouse-test-runner/upstream-allowlist.txt @@ -12,6 +12,7 @@ # # To extend this list, see tests/clickhouse-test-runner/README.md. + 00001_select_1 00003_reinterpret_as_string 00007_array @@ -463,8 +464,10 @@ 00740_optimize_predicate_expression 00745_compile_scalar_subquery 00746_compile_non_deterministic_function +00746_hashing_tuples 00747_contributors 00750_merge_tree_merge_with_o_direct +00751_hashing_ints 00752_low_cardinality_array_result 00752_low_cardinality_mv_1 00752_low_cardinality_permute @@ -478,7 +481,9 @@ 00757_enum_defaults_const 00757_enum_defaults_const_analyzer 00759_kodieg +00760_insert_json_with_defaults 00760_url_functions_overflow +00761_lower_utf8_bug 00765_sql_compatibility_aliases 00780_unaligned_array_join 00799_function_dry_run @@ -487,54 +492,68 @@ 00801_daylight_saving_time_hour_underflow 00802_daylight_saving_time_shift_backwards_at_midnight 00802_system_parts_with_datetime_partition +00803_odbc_driver_2_format 00803_xxhash 00804_rollup_with_having 00807_regexp_quote_meta 00810_in_operators_segfault 00812_prewhere_alias_array -01428_hash_set_nan_key - -# --- Added in batch: tests harvested from tests/queries/0_stateless that -# pass through this harness against ClickHouse `latest` on the -# CLICKHOUSE_CLIENT_CLI_IMPL=client backend (see AGENTS.md "Strategy for -# growing the allowlist"). Each entry was filtered to be a unique-prefix -# 0_stateless test stem to avoid the substring/prefix expansion behavior -# of tests/clickhouse-test. -00746_hashing_tuples -00751_hashing_ints -00760_insert_json_with_defaults -00761_lower_utf8_bug -00803_odbc_driver_2_format 00813_parse_date_time_best_effort_more 00814_parsing_ub +00816_join_column_names_sarg 00817_with_simple +00818_join_bug_4271 +00819_ast_refactoring_bugs +00820_multiple_joins +00820_multiple_joins_subquery_requires_alias 00822_array_insert_default +00823_sequence_match_dfa 00824_filesystem +00826_cross_to_inner_join +00829_bitmap64_function 00834_date_datetime_cmp 00834_not_between 00836_numbers_table_function_zero 00839_bitmask_negative 00840_top_k_weighted 00844_join_lightee2 +00845_join_on_aliases 00847_multiple_join_same_column 00853_join_with_nulls_crash 00854_multiple_join_asterisks +00856_no_column_issue_4242 +00859_distinct_with_join +00860_unknown_identifier_bug +00862_decimal_in +00870_t64_codec +00871_t64_codec_signed +00873_t64_codec_date +00874_issue_3495 00876_wrong_arraj_join_column 00880_decimal_in_key 00881_unknown_identifier_in_in +00882_multiple_join_no_alias 00897_flatten 00898_quantile_timing_parameter_check 00901_joint_entropy +00902_entropy 00903_array_with_constant_function 00904_array_with_constant_2 +00905_compile_expressions_compare_big_dates 00905_field_with_aggregate_function_state 00906_low_cardinality_const_argument 00906_low_cardinality_rollup +00907_set_index_with_nullable_and_low_cardinality 00907_set_index_with_nullable_and_low_cardinality_bug 00908_analyze_query +00909_ngram_distance +00910_buffer_prewhere_different_types 00910_decimal_group_array_crash_3783 00912_string_comparison 00914_join_bgranvea +00914_replicate +00915_simple_aggregate_function +00915_simple_aggregate_function_summing_merge_tree 00915_tuple_orantius 00916_add_materialized_column_after 00916_create_or_replace_view @@ -543,6 +562,11 @@ 00919_histogram_merge 00919_sum_aggregate_states_constants 00920_multiply_aggregate_states_constants +00926_adaptive_index_granularity_collapsing_merge_tree +00926_adaptive_index_granularity_merge_tree +00926_adaptive_index_granularity_replacing_merge_tree +00926_adaptive_index_granularity_versioned_collapsing_merge_tree +00926_multimatch 00927_asof_join_noninclusive 00927_asof_joins 00928_multi_match_constant_constant @@ -552,7 +576,12 @@ 00931_low_cardinality_read_with_empty_array 00931_low_cardinality_set_index_in_key_condition 00932_array_intersect_bug +00932_geohash_support +00933_reserved_word +00933_ttl_with_default +00934_is_valid_utf8 00935_to_iso_week_first_year +00936_crc_functions 00938_basename 00939_limit_by_offset 00939_test_null_in @@ -781,6 +810,7 @@ 01326_fixed_string_comparison_denny_crane 01326_hostname_alias 01328_bad_peephole_optimization +01338_sha256_fixedstring 01338_uuid_without_separator 01341_datetime64_wrong_supertype 01345_array_join_LittleMaverick @@ -795,14 +825,20 @@ 01354_order_by_tuple_collate_const 01354_tuple_low_cardinality_array_mapped_bug 01355_defaultValueOfArgumentType_bug +01355_ilike 01356_initialize_aggregation 01357_result_rows +01358_mutation_delete_null_rows 01359_codeql 01359_geodistance_loop 01360_division_overflow +01361_buffer_table_flush_with_materialized_view 01362_year_of_ISO8601_week_modificators_for_formatDateTime 01372_wrong_order_by_removal 01374_if_nullable_filimonov +01375_null_issue_3767 +01375_storage_file_write_prefix_csv_with_names +01375_storage_file_write_prefix_tsv_with_names 01376_array_fill_empty 01376_null_logical 01379_with_fill_several_columns @@ -812,11 +848,14 @@ 01389_filter_by_virtual_columns 01390_check_table_codec 01390_remove_injective_in_uniq +01391_join_on_dict_crash 01392_column_resolve 01396_negative_datetime_saturate_to_zero 01398_in_tuple_func +01400_join_get_with_multi_keys 01403_datetime64_constant_arg 01407_lambda_arrayJoin +01409_topK_merge 01410_full_join_and_null_predicates 01410_nullable_key_and_index_negate_cond 01411_from_unixtime @@ -832,6 +871,7 @@ 01417_update_permutation_crash 01418_index_analysis_bug 01419_materialize_null +01419_skip_index_compact_parts 01420_logical_functions_materialized_null 01421_array_nullable_element_nullable_index 01422_array_nullable_element_nullable_index @@ -839,17 +879,20 @@ 01425_default_value_of_type_name 01426_geohash_constants 01427_pk_and_expression_with_different_type +01428_hash_set_nan_key 01430_fix_any_rewrite_aliases 01431_finish_sorting_with_consts 01431_utf8_ubsan 01432_parse_date_time_best_effort_timestamp 01434_netloc_fuzz +01440_big_int_exotic_casts 01440_big_int_shift 01440_to_date_monotonicity 01441_array_combinator 01450_set_null_const 01451_normalize_query 01452_normalized_query_hash +01453_fixsed_string_sort 01453_normalize_query_alias_uuid 01455_nullable_type_with_if_agg_combinator 01455_time_zones @@ -860,8 +903,10 @@ 01457_order_by_nulls_first 01458_count_digits 01458_is_decimal_overflow +01458_named_tuple_millin 01459_decimal_casts 01460_allow_dollar_and_number_in_identifier +01460_mark_inclusion_search_crash 01470_columns_transformers2 01471_limit_by_format 01471_with_format @@ -977,6 +1022,7 @@ 01659_array_aggregation_ubsan 01659_test_base64Decode_mysql_compatibility 01660_join_or_all +01660_system_parts_smoke 01660_test_toDayOfYear_mysql_compatibility 01661_join_complex 01661_test_toDayOfWeek_mysql_compatibility @@ -995,13 +1041,17 @@ 01668_test_toMonth_mysql_dialect 01669_join_or_duplicates 01669_test_toYear_mysql_dialect +01670_sign_function 01670_test_repeat_mysql_dialect +01671_aggregate_function_group_bitmap_data 01671_test_toQuarter_mysql_dialect 01672_actions_dag_merge_crash 01672_test_toSecond_mysql_dialect 01673_test_toMinute_mysql_dialect 01674_filter_by_uint8 +01674_htm_xml_coarse_parse 01674_unicode_asan +01676_range_hashed_dictionary 01676_round_int_ubsan 01677_array_enumerate_bug 01678_great_circle_angle @@ -1022,6 +1072,7 @@ 01703_rewrite_aggregate_function_case_insensitive 01704_transform_with_float_key 01706_optimize_normalize_count_variants +01707_join_use_nulls 01710_aggregate_projection_with_grouping_set 01710_aggregate_projection_with_hashing 01710_aggregate_projection_with_monotonic_key_expr @@ -1031,30 +1082,38 @@ 01710_normal_projection_fix1 01710_normal_projection_format 01710_normal_projection_join_plan_fix +01710_normal_projection_with_query_plan_optimization +01710_projection_additional_filters 01710_projection_aggregate_functions_null_for_empty 01710_projection_aggregation_in_order 01710_projection_array_join 01710_projection_detach_part 01710_projection_external_aggregate +01710_projection_in_index 01710_projection_in_set 01710_projection_materialize_with_missing_columns 01710_projection_mutation 01710_projection_optimize_aggregators_of_group_by_keys 01710_projection_optimize_group_by_function_keys 01710_projection_part_check +01710_projection_pk_trivial_count 01710_projection_query_plan_optimization_misc 01710_projection_row_policy 01710_projection_with_ast_rewrite_settings +01710_projection_with_column_transformers 01710_projection_with_joins 01710_projection_with_nullable_keys 01710_projections_group_by_no_key +01711_cte_subquery_fix 01711_decimal_multiplication +01712_no_adaptive_granularity_vertical_merge 01716_array_difference_overflow 01718_subtract_seconds_date 01719_join_timezone 01720_union_distinct_with_limit 01732_explain_syntax_union_query 01733_transform_ubsan +01735_join_get_low_card_fix 01735_to_datetime64 01736_null_as_default 01744_tuple_cast_to_map_bugfix @@ -1064,10 +1123,15 @@ 01747_transform_empty_arrays 01748_partition_id_pruning 01753_mutate_table_predicated_with_table +01760_ddl_dictionary_use_current_database_name 01761_cast_to_enum_nullable 01761_round_year_bounds 01762_datetime64_extended_parsing 01763_filter_push_down_bugs +01763_support_map_lowcardinality_type +01764_collapsing_merge_adaptive_granularity +01764_table_function_dictionary +01765_move_to_table_overlapping_block_number 01765_tehran_dst 01766_todatetime64_no_timezone_arg 01768_array_product @@ -1076,16 +1140,25 @@ 01770_extended_range_3 01771_datetime64_no_time_part 01772_intdiv_minus_one_ubsan +01772_to_start_of_hour_align 01773_case_sensitive_revision 01773_case_sensitive_version 01773_min_max_time_system_parts_datetime64 01774_case_sensitive_connection_id +01774_ip_address_in_range_2 01774_tuple_null_in 01778_where_with_column_name 01779_quantile_deterministic_msan +01780_column_sparse_distinct +01780_column_sparse_filter 01780_column_sparse_pk +01780_column_sparse_tuple +01780_dict_get_or_null 01781_map_op_ubsan +01781_merge_tree_deduplication 01781_token_extractor_buffer_overflow +01783_merge_engine_join_key_condition +01785_pmj_lc_bug 01786_group_by_pk_many_streams 01787_arena_assert_column_nothing 01795_TinyLog_rwlock_ub @@ -1095,23 +1168,34 @@ 01800_log_nested 01803_const_nullable_map 01803_untuple_subquery +01809_inactive_parts_to_delay_throw_insert 01811_datename 01811_filter_by_null 01812_has_generic 01813_quantileBfloat16_nans 01818_case_float_value_fangyc +01818_move_partition_simple +01820_unhex_case_insensitive 01821_to_date_time_ubsan +01822_short_circuit 01822_union_and_constans_error 01825_json_type_18 +01825_json_type_9 +01825_json_type_bools 01825_json_type_order_by 01825_json_type_partitions +01831_max_streams +01832_memory_write_suffix 01833_test_collation_alvarotuso 01835_alias_to_primary_key_cyfdecyf +01836_date_time_keep_default_timezone_on_operations_den_crane 01837_cast_to_array_from_empty_array +01838_system_dictionaries_virtual_key_column 01839_join_to_subqueries_rewriter_columns_matcher 01840_tupleElement_formatting_fuzzer 01845_add_testcase_for_arrayElement 01846_alter_column_without_type_bugfix +01846_null_as_default_for_insert_select 01851_fix_row_policy_empty_result 01851_s2_to_geo 01852_cast_operator_2 @@ -1124,19 +1208,26 @@ 01866_aggregate_function_interval_length_sum 01866_bit_positions_to_array 01866_datetime64_cmp_with_constant +01867_fix_storage_memory_mutation +01867_support_datetime64_version_column 01868_order_by_fill_with_datetime64 01869_function_modulo_legacy 01869_reinterpret_as_fixed_string_uuid 01871_merge_tree_compile_expressions +01881_aggregate_functions_versioning +01881_create_as_tuple 01881_to_week_monotonic_fix +01881_total_bytes_storage_buffer 01881_union_header_mismatch_bug 01889_tokenize 01891_not_in_partition_prune 01891_not_like_partition_prune 01891_partition_by_uuid +01906_h3_to_geo 01906_lc_in_bug 01906_partition_by_multiply_by_zero 01908_with_unknown_column +01909_mbtolou 01911_logical_error_minus 01912_bad_cast_join_fuzz 01913_fix_column_transformer_replace_format @@ -1147,20 +1238,29 @@ 01914_ubsan_quantile_timing 01915_json_extract_raw_string 01916_low_cardinality_interval +01916_lowcard_dict_type +01916_multiple_join_view_optimize_predicate_chertus 01917_prewhere_column_type 01922_array_join_with_index 01925_date_date_time_comparison +01925_json_as_string_data_in_square_brackets +01925_merge_prewhere_table 01925_test_group_by_const_consistency 01926_bin_unbin 01926_date_date_time_supertype +01926_order_by_desc_limit 01926_union_all_schmak +01932_alter_index_with_order +01936_empty_function_support_uuid 01936_quantiles_cannot_return_null 01938_joins_identifiers 01940_pad_string 01940_totimezone_operator_monotonicity +01941_dict_get_has_complex_single_key 01942_untuple_transformers_msan 01943_log_column_sizes 01946_profile_sleep +01947_mv_subquery 01948_heredoc 01960_lambda_precedence 02001_join_on_const @@ -1477,6 +1577,7 @@ 02494_query_cache_empty_tuple 02495_sum_if_to_count_if_bug 02497_analyzer_sum_if_count_if_pass_crash_fix +02497_storage_join_right_assert 02498_analyzer_aggregate_functions_arithmetic_operations_pass_fix 02499_analyzer_set_index 02499_escaped_quote_schema_inference @@ -1490,10 +1591,16 @@ 02504_explain_ast_insert 02506_date_time64_floating_point_negative_value 02508_index_analysis_to_date_timezone +02509_h3_arguments 02510_group_by_prewhere_null +02512_array_join_name_resolution 02513_analyzer_duplicate_alias_crash_fix +02513_analyzer_sort_msan 02513_broken_datetime64_init_on_mac +02514_if_with_lazy_low_cardinality +02514_null_dictionary_source 02514_tsv_zero_started_number +02515_aggregate_functions_statistics 02515_analyzer_null_for_empty 02515_and_or_if_multiif_not_return_lc 02515_distinct_zero_size_key_bug_44831 @@ -1502,17 +1609,23 @@ 02516_projections_with_rollup 02517_union_columns_order 02517_uuid_parsing +02518_merge_engine_nullable_43324 02518_qualified_asterisks_alias_table_name 02519_monotonicity_fuzz 02521_analyzer_aggregation_without_column 02521_cannot_find_column_in_projection 02523_range_const_start +02524_fuzz_and_fuss +02524_fuzz_and_fuss_2 02525_analyzer_function_in_crash_fix 02525_jit_logical_functions_nan +02525_range_hashed_dictionary_update_field +02526_merge_join_int_decimal 02530_ip_part_id 02531_semi_join_null_const_bug 02532_analyzer_aggregation_with_rollup 02532_profileevents_server_startup_time +02533_generate_random_schema_inference 02535_analyzer_limit_offset 02535_ip_parser_not_whole 02536_replace_with_nonconst_needle_and_replacement @@ -1523,12 +1636,17 @@ 02539_generate_random_ip 02539_generate_random_low_cardinality 02539_generate_random_map +02540_date_column_consistent_insert_behaviour 02541_analyzer_grouping_sets_crash_fix 02541_empty_function_support_ip 02541_multiple_ignore_with_nested_select 02541_tuple_element_with_null 02542_case_no_else 02542_table_function_format +02551_ipv4_implicit_uint64 +02552_inner_join_with_where_true +02552_regression_crash +02552_sparse_columns_intersect 02553_json_type_attach_partition 02554_format_json_columns_for_empty 02559_add_parts @@ -1536,7 +1654,12 @@ 02559_multiple_read_steps_in_prewhere_fuzz 02559_multiple_read_steps_in_prewhere_missing_columns_2 02559_nested_multiple_levels_default +02560_analyzer_materialized_view 02560_count_digits +02560_null_as_default +02560_quantile_min_max +02561_sorting_constants_and_distinct_crash +02564_analyzer_ssb_cross_to_inner 02564_date_format 02564_read_in_order_final_desc 02565_update_empty_nested @@ -1549,11 +1672,13 @@ 02577_analyzer_array_join_calc_twice 02578_ipv4_codec_t64 02580_like_substring_search_bug +02582_async_reading_with_small_limit 02584_range_ipv4 02587_csv_big_numbers_inference 02591_bson_long_tuple 02662_sparse_columns_mutations_4 02662_sparse_columns_mutations_5 +02668_column_block_number_with_projections 02674_and_consistency 02674_date_int_string_json_inference 02674_null_default_structure @@ -1561,19 +1686,27 @@ 02676_distinct_reading_in_order_analyzer 02677_decode_url_component 02677_get_subcolumn_array_of_tuples +02677_grace_hash_limit_race 02679_explain_merge_tree_prewhere_row_policy 02680_instr_alias_for_position_case_insensitive 02681_aggregation_by_partitions_bug +02681_comparsion_tuple_elimination_ast 02690_subquery_identifiers +02691_multiple_joins_backtick_identifiers 02692_multiple_joins_unicode +02697_alter_dependencies 02698_marked_dropped_tables 02699_polygons_sym_difference_rollup 02699_polygons_sym_difference_total_analyzer 02700_regexp_operator +02702_logical_optimizer_with_nulls 02705_grouping_keys_equal_keys 02705_projection_and_ast_optimizations_bug 02707_analyzer_nested_lambdas_types +02708_parallel_replicas_not_found_column 02709_generate_random_valid_decimals_and_bools +02709_storage_memory_compressed +02710_aggregation_nested_map_ip_uuid 02710_date_diff_aliases 02710_topk_with_empty_array 02711_trim_aliases @@ -1583,14 +1716,21 @@ 02715_or_null 02716_int256_arrayfunc 02717_pretty_json +02719_aggregate_with_empty_string_key 02720_row_policy_column_with_dots 02724_function_in_left_table_clause_asof_join 02724_jit_logical_functions +02724_mutliple_storage_join +02724_persist_interval_type 02725_agg_projection_respect_PK 02725_alias_with_restricted_keywords +02725_cnf_large_check +02730_dictionary_hashed_load_factor_element_count +02731_auto_convert_dictionary_layout_to_complex_by_complex_keys 02731_in_operator_with_one_size_tuple 02733_distinct 02733_fix_distinct_in_order_bug_49622 +02733_sparse_columns_reload 02734_big_int_from_float_ubsan 02734_optimize_group_by 02734_sparse_columns_short_circuit @@ -1605,6 +1745,8 @@ 02763_jit_compare_functions_nan 02763_mutate_compact_part_with_skip_indices_and_projections 02764_index_analysis_fix +02764_parallel_replicas_plain_merge_tree +02765_parallel_replicas_final_modifier 02766_bitshift_with_const_arguments 02769_compare_functions_nan 02770_jit_aggregation_nullable_key_fix @@ -1613,6 +1755,7 @@ 02771_log_faminy_truncate_count 02782_inconsistent_formatting_and_constant_folding 02782_values_null_to_lc_nullable +02783_date_predicate_optimizations 02784_move_all_conditions_to_prewhere_analyzer_asan 02784_projections_read_in_order_bug 02784_schema_inference_null_as_default @@ -1620,8 +1763,11 @@ 02785_summing_merge_tree_datetime64 02786_transform_float 02787_transform_null +02789_functions_after_sorting_and_columns_with_same_names_bug +02789_functions_after_sorting_and_columns_with_same_names_bug_2 02789_jit_cannot_convert_column 02789_set_index_nullable_condition_bug +02790_fix_coredump_when_compile_expression 02790_keyed_hash_bug 02790_url_multiple_tsv_files 02795_full_join_assert_cast @@ -1638,6 +1784,8 @@ 02807_math_unary_crash 02808_aliases_inside_case 02809_has_subsequence +02809_has_token +02809_prewhere_and_in 02810_initcap 02810_row_binary_with_defaults 02810_system_jemalloc_bins @@ -1747,9 +1895,13 @@ 02923_cte_equality_disjunction 02923_explain_expired_context 02923_join_use_nulls_modulo +02931_alter_materialized_view_query_inconsistent 02931_ubsan_error_arena_aligned_alloc 02932_non_ready_set_stuck +02932_parallel_replicas_fuzzer +02932_set_ttl_where 02933_compare_with_bool_as_string +02933_ephemeral_mv 02935_ipv6_bit_operations 02935_ipv6_from_uint128_equality 02935_ipv6_from_uint128_one @@ -1757,6 +1909,7 @@ 02935_ipv6_from_uint128_with_bit_and 02935_ipv6_to_and_from_uint128 02941_any_RESPECT_NULL_sparse_column +02943_create_query_interpreter_sample_block_fix 02943_exprs_order_in_group_by_with_rollup 02943_positional_arguments_bugs 02943_tokenbf_and_ngrambf_indexes_support_match_function @@ -1765,9 +1918,14 @@ 02944_variant_as_common_type_analyzer 02945_blake3_msan 02946_literal_alias_misclassification +02946_merge_tree_final_split_ranges_by_primary_key 02947_dropped_tables_parts +02947_parallel_replicas_remote +02949_parallel_replicas_scalar_subquery_big_integer +02949_ttl_group_by_bug 02950_reading_array_tuple_subcolumns 02953_slow_create_view +02955_avro_format_zstd_encode_support 02955_sparkBar_alias_sparkbar 02956_fix_to_start_of_milli_microsecond 02956_format_constexpr @@ -1776,58 +1934,90 @@ 02962_analyzer_const_in_count_distinct 02962_analyzer_constant_set 02962_indexHint_rpn_construction +02962_max_joined_block_rows +02962_parallel_window_functions_different_partitioning 02963_single_value_destructor 02965_projection_with_partition_pruning +02966_float32_promotion 02967_index_hint_crash +02968_analyzer_join_column_not_found 02968_full_sorting_join_fuzz 02968_sumMap_with_nan +02968_url_args 02969_analyzer_eliminate_injective_functions 02969_functions_to_subcolumns_if_null 02970_generate_series +02971_functions_to_subcolumns_column_names 02971_functions_to_subcolumns_map 02971_functions_to_subcolumns_variant 02974_analyzer_array_join_subcolumn 02974_if_with_map 02975_intdiv_with_decimal 02981_translate_fixedstring +02982_create_mv_inner_extra 02982_dont_infer_exponent_floats +02983_empty_map_hasToken 02985_if_over_big_int_decimal 02986_leftpad_fixedstring 02987_group_array_intersect +02987_logical_optimizer_pass_lowcardinality 02989_group_by_tuple +02990_optimize_uniq_to_count_alias +02990_parts_splitter_invalid_ranges 02991_count_rewrite_analyzer 02992_all_columns_should_have_comment +02993_lazy_index_loading 02994_cosineDistanceNullable +02995_preliminary_filters_duplicated_columns 02995_preliminary_filters_duplicated_columns_SimpleAggregateFunction 02995_ulid_short_circuit +02996_index_compaction_counterexample +02997_fix_datetime64_scale_conversion 02998_analyzer_prewhere_report 02998_ipv6_hashing 02998_pretty_format_print_readable_number_if_last_column +02998_primary_key_skip_columns 02998_system_dns_cache_table +02999_scalar_subqueries_bug_2 02999_ulid_short_circuit 03000_minmax_index_first 03000_virtual_columns_in_prewhere 03001_analyzer_nullable_nothing +03001_block_offset_column_2 03001_consider_lwd_when_merge 03001_data_version_column +03002_analyzer_prewhere 03002_map_array_functions_with_low_cardinality +03002_modify_query_cte 03002_sample_factor_where +03003_count_asterisk_filter 03003_enum_and_string_compatible 03003_sql_json_nonsense +03006_buffer_overflow_join 03006_mv_deduplication_throw_if_async_insert 03006_parallel_replicas_cte_explain_syntax_crash 03007_column_nullable_uninitialzed_value +03008_filter_projections_non_deterministoc_functions 03008_groupSortedArray_field +03008_index_small +03008_uniq_exact_equal_ranges +03009_consecutive_keys_nullable +03009_range_dict_get_or_default 03010_read_system_parts_table_test 03010_sum_to_to_count_if_nullable 03010_view_prewhere_in +03010_virtual_memory_mappings_asynchronous_metrics 03011_adaptative_timeout_compatibility 03013_addDays_with_timezone 03013_fuzz_arrayPartialReverseSort +03013_ignore_drop_queries_probability +03013_position_const_start_pos 03013_repeat_with_nonnative_integers 03014_analyzer_group_by_use_nulls 03014_analyzer_groupby_fuzz_60317 +03015_aggregator_empty_data_multiple_blocks 03015_analyzer_groupby_fuzz_60772 +03015_peder1001 03016_analyzer_groupby_fuzz_59796 03017_analyzer_groupby_fuzz_61600 03018_analyzer_greater_null @@ -1842,41 +2032,55 @@ 03032_scalars_create_as_select 03032_variant_bool_number_not_suspicious 03033_analyzer_parametrized_view_alias +03033_analyzer_resolve_from_parent_scope 03033_create_as_copies_comment 03033_cte_numbers_memory 03033_distinct_transform_const_columns +03033_final_undefined_last_mark 03033_from_unixtimestamp_joda_by_int64 +03033_parts_splitter_bug_and_index_loading +03033_scalars_context_data_race 03033_virtual_column_override 03033_with_fill_interpolate 03034_json_extract_variant 03035_argMinMax_numeric_non_extreme_bug +03035_materialized_primary_key 03035_morton_encode_no_rows 03036_prewhere_lambda_function 03036_with_numbers 03037_dot_product_overflow +03037_precent_rank 03037_union_view 03038_ambiguous_column +03038_move_partition_to_oneself_deadlock 03039_unknown_identifier_window_function 03040_alias_column_join 03040_array_sum_and_join 03040_dynamic_type_alters_1_compact_merge_tree +03040_dynamic_type_alters_1_memory 03040_dynamic_type_alters_1_wide_merge_tree 03040_dynamic_type_alters_2_compact_merge_tree 03041_analyzer_gigachad_join 03041_select_with_query_result 03042_analyzer_alias_join +03042_not_found_column_c1 03043_group_array_result_is_expected +03044_analyzer_alias_join 03044_array_join_columns_in_nested_table 03045_analyzer_alias_join_with_if 03046_column_in_block_array_join 03047_analyzer_alias_join 03047_group_by_field_identified_aggregation 03048_not_found_column_xxx_in_block +03049_analyzer_group_by_alias 03050_select_one_one_one 03051_many_ctes 03052_query_hash_includes_aliases 03054_analyzer_join_alias +03055_analyzer_subquery_group_array 03057_analyzer_subquery_alias_join +03059_analyzer_join_engine_missing_column +03060_analyzer_regular_view_alias 03061_analyzer_alias_as_right_key_in_join 03064_analyzer_named_subqueries 03065_analyzer_cross_join_and_array_join @@ -1884,10 +2088,13 @@ 03067_analyzer_complex_alias_join 03069_analyzer_with_alias_in_array_join 03070_analyzer_CTE_scalar_as_numbers +03071_fix_short_circuit_logic 03072_analyzer_missing_columns_from_subquery 03073_analyzer_alias_as_column_name 03074_analyzer_alias_column_in_view 03075_analyzer_subquery_alias +03077_analyzer_multi_scalar_subquery_aliases +03078_analyzer_multi_scalar_subquery_aliases 03079_analyzer_numeric_literals_as_column_names 03080_analyzer_prefer_column_name_to_alias__virtual_columns 03081_analyzer_agg_func_CTE @@ -1900,17 +2107,27 @@ 03090_analyzer_multiple_using_statements 03093_analyzer_column_alias 03093_analyzer_miel_test +03093_reading_bug_with_parallel_replicas 03093_virtual_column_override_group_by 03093_with_fill_support_constant_expression +03094_analyzer_fiddle_multiif +03094_grouparraysorted_memory 03094_named_tuple_bug24607 03094_transform_return_first +03095_join_filter_push_down_right_stream_filled +03095_merge_and_buffer_tables 03096_largest_triangle_3b_crash +03096_order_by_system_tables 03096_update_non_indexed_columns 03097_query_log_join_processes 03099_analyzer_multi_join +03100_analyzer_constants_in_multiif 03100_lwu_05_basics +03100_lwu_07_merge_patches 03100_lwu_08_multiple_blocks +03100_lwu_18_sequence 03100_lwu_19_nullable +03100_lwu_32_on_fly_filter 03100_lwu_33_add_column 03100_lwu_34_multistep_prewhere 03100_lwu_37_update_all_columns @@ -1919,6 +2136,9 @@ 03100_lwu_47_apply_patches_after_drop_column 03100_lwu_49_update_skip_indexes 03101_analyzer_invalid_join_on +03102_prefer_column_name_to_alias +03104_create_view_join +03105_table_aliases_in_mv 03108_describe_union_all 03109_ast_too_big 03110_unicode_alias @@ -1927,67 +2147,111 @@ 03116_analyzer_explicit_alias_as_column_name 03117_analyzer_same_column_name_as_func 03118_analyzer_multi_join_prewhere +03121_analyzer_filed_redefenition_in_subquery 03122_analyzer_collate_in_window_function +03125_analyzer_CTE_two_joins 03127_argMin_combinator_state +03128_merge_tree_index_lazy_load +03128_system_unload_primary_key 03129_cte_with_final 03129_low_cardinality_nullable_non_first_primary_key 03130_abs_in_key_condition_bug 03130_analyzer_array_join_prefer_column +03132_sqlancer_union_all +03141_wildcard_grants_on_tables 03142_alter_comment_parameterized_view 03142_skip_ANSI_in_UTF8_compute_width +03142_window_function_limit_by +03143_join_filter_push_down_filled_join_fix 03143_ttl_in_system_parts_columns_table 03144_aggregate_states_with_different_types 03144_alter_column_and_read 03145_unicode_quotes 03146_bug47862 03146_tpc_ds_grouping +03147_datetime64_constant_index_analysis +03148_query_log_used_dictionaries +03149_analyzer_join_projection_name +03149_analyzer_join_projection_name_2 03149_variant_pop_back_typo +03150_url_hash_non_constant_level 03151_analyzer_view_read_only_necessary_columns +03151_external_cross_join 03151_pmj_join_non_procssed_clash 03151_redundant_distinct_with_window +03152_dynamic_type_simple +03153_dynamic_type_empty 03155_datasketches_ubsan 03155_explain_current_transaction 03156_tuple_map_low_cardinality +03157_dynamic_type_json +03160_dynamic_type_agg 03161_decimal_binary_math 03161_ipv4_ipv6_equality 03164_analyzer_rewrite_aggregate_function_with_if +03164_analyzer_validate_tree_size 03164_create_as_default 03164_early_constant_folding_analyzer +03164_optimize_read_in_order_nullable 03165_distinct_with_window_func_crash 03165_order_by_duplicate 03165_string_functions_with_token_text_indexes +03166_mv_prewhere_duplicating_name_bug +03167_base64_url_functions_sh 03167_empty_tuple_concat 03167_fancy_quotes_off_by_one 03167_parametrized_view_with_cte 03168_cld2_tsan 03168_fuzz_multiIf_short_circuit +03169_cache_complex_dict_short_circuit_bug 03169_display_column_names_in_footer 03169_modify_column_data_loss 03169_optimize_injective_functions_inside_uniq_crash 03170_part_offset_as_table_column +03171_direct_dict_short_circuit_bug +03171_hashed_dictionary_short_circuit_bug_fix +03172_system_detached_tables_no_loop 03173_distinct_combinator_alignment 03174_merge_join_bug +03174_multiple_authentication_methods_show_create +03174_split_parts_ranges_into_intersecting_and_non_intersecting_final_and_read-in-order_bug +03175_sparse_and_skip_index 03195_group_concat_deserialization_fix 03196_max_intersections_arena_crash 03197_fix_parse_mysql_iso_date +03198_dictionary_validate_primary_key_type 03198_group_array_intersect +03198_h3_polygon_to_cells 03198_json_extract_more_types 03199_fix_auc_tie_handling +03199_has_lc_fixed_string +03199_join_with_materialized_column 03199_queries_with_new_analyzer +03200_memory_engine_alter_dynamic 03200_reverse_by_separator 03200_subcolumns_join_use_nulls 03201_analyzer_resolve_in_parent_scope 03201_sumIf_to_countIf_return_type +03202_enum_json_cast +03202_system_load_primary_key 03203_count_with_non_deterministic_function 03203_drop_detached_partition_all +03203_fill_missed_subcolumns +03203_multiif_and_where_2_conditions_old_analyzer_bug +03203_optimize_disjunctions_chain_to_in 03203_system_numbers_limit_and_offset_simple 03204_index_hint_fuzzer +03204_storage_join_optimize 03205_column_type_check 03205_hashing_empty_tuples 03205_json_cast_from_string +03205_json_syntax +03205_parallel_window_finctions_and_column_sparse_bug +03205_system_sync_replica_format 03206_is_null_constant_result_old_analyzer_bug 03208_datetime_cast_losing_precision 03208_groupArrayIntersect_serialization +03208_multiple_joins_with_storage_join 03208_numbers_total_rows_approx 03208_uniq_with_empty_tuple 03209_functions_json_msan_fuzzer_issue @@ -1997,19 +2261,27 @@ 03210_variant_with_aggregate_function_type 03211_convert_outer_join_to_inner_join_anti_join 03213_array_element_msan +03213_denseRank_percentRank_alias 03214_join_on_tuple_comparison_elimination_bug 03214_json_typed_dynamic_path 03215_fix_get_index_in_tuple 03215_key_condition_bug 03215_partition_in_tuple 03215_toStartOfWeek_with_dateTime64_fix +03215_udf_with_union 03215_varian_as_common_type_integers 03215_view_with_recursive 03217_fliter_pushdown_no_keys 03217_primary_index_memory_leak 03221_key_condition_bug +03221_refreshable_matview_progress +03222_ignore_nulls_query_tree_elimination +03223_nested_json_in_shared_data_merges +03224_json_merges_new_type_in_shared_data +03224_nested_json_merges_new_type_in_shared_data 03224_trim_empty_string 03225_const_prewhere_non_ataptive +03227_dynamic_subcolumns_enumerate_streams 03227_test_sample_n 03228_dynamic_subcolumns_from_subquery 03228_join_to_rerange_right_table @@ -2017,99 +2289,174 @@ 03228_variant_permutation_issue 03229_empty_tuple_in_array 03229_json_structure_comparison +03229_query_condition_cache_bug83262 03229_query_condition_cache_cte_constant_folding +03229_query_condition_cache_in_operator +03229_query_condition_cache_recursive_cte 03230_anyHeavy_merge +03230_show_create_query_identifier_quoting_style +03230_subcolumns_mv 03230_system_projections 03231_prewhere_conditions_order +03232_json_uniq_group_by +03232_workload_create_and_drop 03235_groupArray_string_consistency 03236_create_query_ttl_where +03236_squashing_high_memory +03236_test_zero_field_decimal 03237_create_table_select_as_with_recursive 03237_get_subcolumn_low_cardinality_column 03237_max_map_state_decimal_serialization 03238_analyzer_unknown_function +03239_if_constant_folding +03240_array_element_or_null_for_map 03240_cte_in_subquery +03240_quantile_exact_weighted_interpolated 03241_view_block_structure 03242_view_block_structure 03243_array_join_lambda 03243_to_start_of_interval_aliases +03244_skip_index_in_final_query_part_of_pk +03244_skip_index_in_final_query_with_pk_rescan_basic +03244_skip_index_in_final_query_with_pk_rescan_extremes +03244_skip_index_in_final_query_with_pk_rescan_no_final_mark +03244_skip_index_in_final_query_with_pk_rescan_pk_subset +03245_ripemd160 03245_views_and_filter_push_down_bug +03246_range_literal_replacement_works 03246_toStartOfInterval_date_timezone_bug 03247_json_extract_lc_nullable +03247_object_column_copy +03248_max_parts_to_move +03248_with_insert +03248_with_insert_with 03249_dynamic_alter_consistency 03250_ephemeral_comment +03250_json_group_by_sub_object_subcolumn +03252_fill_missed_arrays 03252_merge_tree_min_bytes_to_seek +03254_attach_part_order +03254_normalize_aggregate_states_with_named_tuple_args +03254_prewarm_mark_cache_columns 03254_project_lwd_respects_row_exists 03254_uniq_exact_two_level_negative_zero 03255_fix_sbstrings_logical_error 03257_json_escape_file_names +03257_reverse_sorting_key_simple 03258_multiple_array_joins 03258_old_analyzer_const_expr_bug 03258_quantile_exact_weighted_issue 03259_join_condition_executed_block_bug 03259_negate_key_overflow +03259_orc_date_out_of_range 03260_dynamic_low_cardinality_dict_bug 03261_any_respect_camelCase_aliases 03261_minmax_indices_by_default_table_copy 03261_sort_cursor_crash +03261_variant_permutation_bug +03262_analyzer_materialized_view_in_with_cte 03262_column_sizes_with_dynamic_structure +03262_const_adaptive_index_granularity 03262_system_functions_should_not_fill_query_log_functions +03263_analyzer_materialized_view_cte_nested 03266_with_fill_staleness_cases 03267_join_swap_bug +03268_empty_tuple_update 03268_system_parts_index_granularity 03269_partition_key_not_in_set 03270_empty_tuple_in_array_intersect 03270_fix_column_modifier_write_order 03271_decimal_monotonic_day_of_week +03272_bitmapTransform_error_counter +03272_json_to_json_cast_1 +03272_json_to_json_cast_3 03272_json_to_json_cast_6 03272_partition_pruning_monotonic_func_bug +03272_prewarm_mark_cache_add_column +03273_better_json_subcolumns_parsing 03273_dynamic_pretty_json_serialization 03273_group_by_in_order_still_used_when_group_by_key_doesnt_match_order_by_key +03273_primary_index_cache +03273_primary_index_cache_low_cardinality +03274_dynamic_column_sizes_vertical_merge 03274_grace_hash_max_joined_block_size_rows_bug 03274_philipzucker +03274_squashing_transform_sparse_bug 03274_with_fill_dup_sort_bug +03275_matview_with_union 03275_subcolumns_in_primary_key_bug +03276_functions_to_subcolumns_lc +03276_index_empty_part 03276_index_of_assume_sorted +03276_merge_tree_index_lazy_load +03276_parquet_output_compression_level +03277_logging_elapsed_ns 03278_dateTime64_in_dateTime64_bug +03279_not_empty_json 03280_dynamic_if_null 03281_dynamic_coalesce +03282_dynamic_in_functions_convert +03282_json_equal_comparison 03282_parallel_join_with_additional_filter +03283_json_binary_serialization_use_default_settings 03285_analyzer_extract_common_expr_bug 03285_analyzer_optimize_disjunctions 03286_collation_locale_with_modifier 03286_format_datetime_timezones +03286_parallel_replicas_cross_join_bug 03286_serialization_hint_system_columns 03287_format_datetime_mysqlfraction 03289_tuple_element_to_subcolumn +03290_final_collapsing +03290_final_replacing +03290_final_sample 03290_force_normal_projection +03290_mix_engine_and_query_settings 03290_partial_arrayROCAUC_and_arrayAUCPR 03290_pr_non_replicated_in_subquery +03292_format_tty_friendly +03298_analyzer_group_by_all_fix +03298_server_client_native_settings 03299_deep_nested_map_creation 03299_map_named_tuple 03299_pretty_squash 03300_nested_json_empty_keys 03301_is_ipv4_string +03301_subcolumns_in_mv +03302_any_enum_aggregation 03303_alias_inverse_order 03303_dynamic_in_not_xor 03304_pretty_fallback_to_vertical +03305_compressed_memory_eng_crash_reading_subcolumn 03306_optimize_table_force_keyword +03307_parallel_hash_max_joined_rows 03310_aggregate_projection_count_nullable +03311_subcolumns_in_default_and_materialized_expressions 03312_analyzer_unused_projection_fix +03312_sparse_column_tuple 03313_h3togeo_result_order 03314_analyzer_resolve_in_parent_scope_2 03314_analyzer_resolve_in_parent_scope_3 03314_analyzer_resolve_in_parent_scope_5 +03314_divide_decimal_short_circuit 03314_has_column_in_table_alias_column 03314_nullable_key_no_optimize_functions_to_subcolumns 03314_summing_merge_tree_final_not_found_column_in_block +03314_variant_rowbinary_file 03315_analyzer_correlated_subqueries 03315_array_join_scalar +03315_join_on_optimize_pass_alias 03315_quantile_bfloat16_ubsan +03317_index_hint_prewhere +03321_functions_to_subcolumns_skip_index +03321_inner_materialized_view_nested 03321_join_on_is_null_lowcardinality 03322_unused_interpolate_expressions 03323_bfloat16_least_supertype 03323_union_all_constants_bug 03325_alter_modify_projection_primary_key_column 03325_count_summing_merge_tree_order_by_tuple +03326_parallel_replicas_out_of_range 03326_toStartOfNanosecond_ubsan 03328_formatting_assignment_expression 03352_distinct_sorted_bug @@ -2242,68 +2589,107 @@ 03568_ddsketch_merge 03568_json_extract_case_insensitive 03571_join_inequality_constants +03572_planner_merge_filter_into_join_bug 03573_concurrent_hash_scatter_bug +03573_planner_merge_filter_into_join_bug_2 +03574_analyzer_merge_filter_into_join_bug +03574_parallel_replicas_last_right_join +03575_analyzer_merge_filter_into_join_bug_2 03577_assert_on_estimated_block_size_bytes 03577_hash_output_format 03577_ub_max_column_in_block_size_bytes 03577_vairant_lazy_materialization_bug +03578_parallel_replicas_formatting_tuple 03579_system_columns_column_alias 03579_zero_copy_aggregating_final_anyLast 03580_external_merge_sort_with_lazy_columns 03580_join_runtime_filter_column_type 03580_join_runtime_filter_pushdown 03581_bool_literal_column_name +03581_parallel_replicas_read_empty_ranges 03582_initcap_fixedstring 03582_normalize_utf8_empty +03592_dictionary_columns_trailing_comma 03593_any_join_swap_tables 03593_backup_with_broken_projection 03594_alias_subcolumns 03594_coalescing_merge_tree_segfault +03594_funcs_on_empty_arguments +03594_json_extract_decimal_precision +03594_like_perfect_affix_rewrite_pass 03594_push_more_filters_down_joins +03595_alter_if_exists_mixed_commands +03595_alter_if_exists_runtime_check +03595_funcs_on_zero +03596_parquet_prewhere_page_skip_bug 03598_json_enum_default_value_in_typed_path 03599_bad_date_and_datetimes_inference 03600_analyzer_setting_bool 03600_replace_fixed_string_bug 03601_histogram_quantile 03601_inconsistent_table_names +03601_insert_squashing_remove_const 03601_json_from_string_accurate_cast_or_null 03601_replace_regex_fixedstring_empty_needle 03602_query_system_tables_definer 03604_and_join_use_nulls_bug_83977 +03604_functions_to_subcolumns_outer_join 03604_join_reorder_pinned_bug 03604_key_condition_set_tuple_bug 03604_plan_step_description_limit +03604_to_date_casts 03606_nullable_json_group_by 03611_cte_deterministic +03611_pr_global_join 03611_uniqExact_bug +03612_explain_indexes_bugs 03613_empty_tuple_permute_with_limit 03620_json_advanced_shared_data_seek_bug 03622_generic_aggregate_functions__state_compatibility 03623_datetime64_preepoch_fractional_precision 03623_lazy_materialization_array_sizes_bug +03623_parquet_bool +03624_parquet_row_number +03624_pr_lefl_right_joins_chain 03624_resource_exhaustion_window_function 03625_case_without_condition_non_constant_branches +03625_prewhere-and-default-bug 03625_upper_lower_utf8_different_number_of_code_points +03626_case_function_with_dynamic_argument 03627_non_constant_replacement_in_replace_regexp 03628_named_tuple_element_in_order_by_key 03628_parse_date_time_short_circuit +03629_starts_endswith_caseinsensitive 03630_hash_join_max_block_size 03630_join_blocks_with_different_constness +03630_parquet_bool_bug 03631_array_of_empty_tuples +03631_select_replace_comprehensive 03632_default_minmax_indices_alter 03632_join_logical_assert_85403 03633_negative_limit_offset 03633_set_index_bulk_filtering +03635_in_function_different_types_many_columns +03636_empty_projection_block +03636_index_analysis_with_session_tz +03638_merge_max_dynamic_subcolumns_in_compact_part +03638_merge_max_dynamic_subcolumns_in_wide_part 03639_hash_of_dynamic_column +03639_hash_of_json_column 03640_skip_indexes_with_or_and_not 03640_variant_array_null_map_subcolumn 03641_analyzer_issue_85834 03641_json_array_of_float_and_bool +03642_column_ttl_sparse 03643_paste_join_disable_filter_pushdown 03643_system_instrumentation_no_suspicious_lowcardinality +03644_explain_indices 03644_join_order_mixed_comma_and_left +03644_min_level_for_wide_part +03644_rows_before_aggregation_in_order 03651_positional_argument_agg_projection 03652_coalescing_merge_tree_fix_empty_tuple +03652_join_using_legacy_step 03652_union_columns_2 03653_fractional_limit_offset 03654_case_non_constant_null @@ -2311,40 +2697,69 @@ 03656_nan_comparison 03657_gby_overflow_any_sparse 03657_rollup_constant +03660_udf_subquery +03664_parameterized_view_restart 03666_count_matches_complexity 03667_pr_join_with_cross_join_on_left +03671_dict_in_subquery_in_index_analysis_context_expired 03672_columns_same_as_subcolumns +03672_nested_array_nested_tuple 03699_reverse_utf8 +03700_vertical_format_pretty_print_json +03701_distinct_but_no_group_by_projection_table_use_check 03701_limit_by_in_order 03702_encode_decode_memory_usage +03702_json_datetime_format_settings +03702_optimize_inverse_dictionary_lookup_composite_and_layouts +03703_function_dict_get_keys_large 03703_statistics_low_cardinality 03704_default_empty_order_by 03704_fractional_limit_with_ties +03704_function_dict_get_keys_cache_type 03705_count_if_asterisk 03705_fix_compression_T64_unaligned +03705_function_dict_get_keys_multiple_dict_and_no_caching +03707_autopr_unsupported_cases +03707_set_index_bad_get_null_bug 03707_statistics_cache +03708_analyzer_convert_any_outer_to_inner_2 03708_exact_rows_before_limit_in +03708_flush_async_insert_queue_for_table 03708_join_or_to_right_any_bug 03708_low_cardinality_aggregate_state_compatibility 03708_statistics_estimator_cast_type +03709_anti_join_runtime_filters +03709_coalescing_final 03709_final_prewhere_special_columns +03709_remove_unused_columns_from_prewhere 03710_argAndMinMax +03710_midpoint_jit +03710_pr_insert_into_mv_with_join +03710_pr_join_with_mv +03711_top_k_by_skip_index_negative 03712_json_advanced_shared_data_bug 03713_group_by_injective_function_old_analyzer 03714_base32_base58_short_string +03714_empty_tuple_reverse_function 03714_queries_escaping_1 03714_queries_escaping_2 +03715_empty_tuple_functions_conversion +03716_bson_each_row_empty_tuple_column 03716_join_duplicate_columns_89411 +03716_join_right_side_sorting 03717_system_unicode_enums 03719_generic_hash_over_constant_and_non_constant 03719_ntile_no_partition_by_check 03720_datetime64_bad_inference +03720_insert_mem_no_self_deduplication 03721_check_natural_sort_key 03721_insert_replicated_array_nested_sizes_check 03721_join_residual_condition_bug_88635 +03721_right_join_logical_step 03721_statistics_alter_type_bug 03722_iceberg_hash_bug 03722_json_compact_part_substreams_cache_bug +03722_normal_projection_key_columns 03723_incorrect_implicit_projection 03724_to_date_time_or_null_negative_arg_bug 03725_empty_tuple_some_limit_with_ties_distinct @@ -2354,6 +2769,7 @@ 03727_block_structure_mismatch_after_filter_push_down 03727_ipv4_parsing_bug 03727_named_tuples_pretty_format +03727_prewhere_intermediate_columns 03727_tolowcardinality_nullable_cast 03728_analyzer_identifier_resolution_join 03728_explain_column_structure @@ -2364,58 +2780,99 @@ 03733_anti_join_runtime_filter_3 03733_base58_decode_bug 03733_summing_merge_tree_nested_low_cardinality +03740_alter_modify_query_dict_name_in_cse +03741_insert_select_subquery_from_file 03741_subcolumns_of_materialized_columns_in_mutation 03742_array_join_empty_tuple 03742_lazy_materialization_of_array_after_alter_add_column 03742_test_flattened_crash 03743_fix_estimator_crash 03748_tuple_of_sparse_elements_bug +03749_arrayROCAUC_variant_type 03749_cross_join_use_nulls_matcher 03749_implicit_index_ephemeral_alias +03749_materialized_view_not_supports_parallel_write +03750_json_in_tuple_element +03751_if_uint64_int32_type_mismatch +03751_join_empty_string_nullable 03751_summing_coalescing_merge_tree_sparse_columns_in_header 03752_fractional_limit_offset_small_blocks 03752_join_part 03753_anti_join_runtime_filter_4 +03753_join_runtime_filter_dynamically_disable 03753_log_engine_shared_prefixes_bug 03754_fractional_limit_offset_multiple_streams 03754_h3_polygon_to_cells_const +03754_point_in_polygon_primary_key_index +03755_concurrent_hash_join_dispatch_bug 03755_final_prewhere_duplicate_columns +03755_hive_partitioning_used_to_break_insert 03756_sparse_serialization_nullable_in_tuple 03757_ast_not_formatting +03760_consistent-in-formatting +03760_join_reorder_outer_inner_nulls +03761_convert_outer_to_inner_join_keeps_header 03761_count_distinct_optimization_window 03761_inconsistent_ast_formatting 03761_lazy_materialization_split_expr +03761_log_with_string_size 03762_count_distinct_optimization_multiple_columns +03762_parquet_cast_to_json 03763_column_conflicting_with_subcolumn 03763_no_new 03764_empty_tuple_permutation_bug +03764_join_nothing_type_column_crash 03766_subcolumns_resolution_in_aliases 03767_implicit_minmax_index_dynamic_variant 03768_rename_alias_column_implicit_index 03769_implicit_index_alias_simple_reference +03770_long_index_name_hash 03771_tokens_crash 03773_join_on_formatting 03773_json_with_typed_path_skip +03773_key_condition_not_materialize_not_cast 03773_lightweight_update_index_subquery 03773_nullable_sparse_join +03773_parquet_roundtrip_bug 03774_json_skip_regexp_partial_match +03774_ttl_group_by_lowcardinality 03775_join_on_disk_max_files_to_merge +03776_subcolumns_in_partition_by +03776_text_index_covered_sparse_grams +03777_analyzer_unused_columns_removal_correlated_subquery +03777_join_precalculate_keys +03777_join_runtime_filter_duplicate_columns +03777_join_runtime_filter_prepared_right_table +03777_join_runtime_filter_unsupported_type +03777_join_runtime_filter_with_consts +03777_join_runtime_filter_with_totals 03777_merge_engine_with_aliases_bug +03777_runtime_filter_and_merge_table 03777_top_k_dynamic_filtering_respect_header_bug +03778_read_subcolumns_from_projection +03779_float_nan_key_condition 03779_lexer_pointer_overflow_ubsan 03780_has_all_tokens_empty_array 03780_json_typed_paths_with_same_prefix 03781_json_max_dynamic_subcolumns_control_on_parsing 03781_merge_hostname_remote 03781_split_join_filter_bug +03782_parse_describe_select_without_parentheses_around_select 03784_correlated_subquery_from_storage 03784_materialize_index_subcolumns 03784_msan_token_iterator +03785_rebuild_projection_with_part_offset 03786_empty_tuple_map_array +03787_pr_group_by_read_mode_error +03787_storage_join_type_conversion_2 03788_hash_join_with_empty_right_side_skip_left_side 03788_header_after_outer_to_semi_or_anti_conversion +03788_parquet_writer_bad_bool +03788_semi_join_filter_pushdown_bug 03788_statistics_part_pruning_nan +03788_table_function_results_race 03789_right_join_column_replicated +03789_skip_index_bad_utf8 03790_uniqTheta_error 03791_decimal_string_zero 03791_function_to_subcolumns_optimization_on_subcolumns @@ -2432,12 +2889,21 @@ 03799_any_left_join_isnotnull_crash 03799_cross_to_inner_join_view_with_join 03799_parallel_right_join_flag_per_row +03799_row_order_optimize_duplicate_sorting_columns 03800_string_size_stream_in_nested_types 03801_domain_empty_string_msan +03802_ephemeral_enum_type +03802_pr_join_non_mt_table +03804_insert_select_parentheses 03805_array_filter_nullable_lambda +03805_merge_table_filter_push_down_uninitialized_plan 03807_top_k_limit_zero 03808_correlated_subquery_with_totals +03808_inconsistent_formatting_index_ttl_alias 03808_with_duplicate_alias_nested_scopes +03809_time64_not_monotonic +03810_pr_aggr_in_order_read_mode +03811_contingency_window_functions_nested_combinators_merge 03811_inconsistent_formatting_ttl_where_alias 03811_sparse_column_aggregation_with_sum 03812_column_function_prewhere_filter @@ -2446,42 +2912,63 @@ 03815_tuple_element_with_json 03821_direct_join_empty_table 03821_json_skip_path_fix +03821_materialized_cte_with_sets 03821_uniq_speedup_boundaries +03822_alias_column_skip_index_no_merge 03822_insert_null_into_non_nullable_enum 03822_join_reorder_fix_expression_sources 03822_lttb_date32_negative +03822_lwu_patch_type_mismatch_crash 03822_window_function_large_preceding_offset +03823_lwu_patch_type_mismatch_dataloss 03823_prefetched_read_pool_priority_assertion 03823_skip_index_disjunction_oob +03824_index_reader_const_granularity_last_mark +03824_join_swap_output_header_mismatch 03825_array_with_constant_fixed_string 03825_bitxor_if_mixed_types 03825_join_right_sorting_empty_maps 03825_xxh3_128_hash_function 03826_array_join_in_bloom_filter 03827_cross_join_no_right_columns +03829_distance_transposed_pass_nullable_nothing 03832_array_join_filter_push_down_cross_join 03832_log_engine_alias_subcolumn 03833_server_ast_fuzzer +03835_join_transitive_predicates_outer 03835_todate_monotonicity_boundary +03836_array_join_in_filter_partial_evaluation +03902_format_datetime_fractional_msan 03902_jit_cast_to_bool +03904_fix_empty_statistics_load 03905_grouping_sets_use_nulls_lowcardinality_in_tuple +03905_select_replace_where_same_column 03905_set_partition_pruning_key_condition 03905_skip_index_set_null +03905_window_function_constant_in_group_by 03906_group_by_lowcardinality_parallel_hash_join 03908_lazy_materialization_replacing_merge_tree 03909_union_all_remote_predicate_pushdown 03910_limit_always_read_till_end_with_constant_false_having +03910_right_join_storage_join_qualify_type_mismatch 03911_ephemeral_virtual_column_old_analyzer +03912_set_index_dynamic_type +03912_tuple_comparison_group_by_use_nulls 03913_jemalloc_cache_arena_metrics 03913_minmax_statistics_nan_ubsan_error 03913_text_index_preprocessor_expression +03914_parquet_v3_prewhere_non_bool_filter 03916_coalescing_merge_tree_const_column_from_index 03916_contingency_state_variant_serialization_match 03916_except_intersect_all_multiset +03916_jit_datetime_to_datetime64_scale 03916_lazy_materialization_union_all +03916_window_functions_group_by_use_nulls 03918_allow_nullable_tuple_in_extracted_subcolumns_not_changeable +03918_array_element_negate_int_min 03918_if_transform_strings_to_enum_nullable 03918_insert_squashing_const_different_values +03918_replace_table_clone_as_verbose_result 03919_json_lazy_type_hints_incompatible 03919_variant_adaptor_lowcardinality 03921_internal_functions_have_internal_category @@ -2489,26 +2976,45 @@ 03923_union_all_cte_skip_index_prewhere 03924_constant_granularity_last_mark 03924_duplicated_key_with_literal_and_nested_object_in_typed_paths_in_json +03924_sorting_step_stats 03925_exists_with_mutations_execute_on_initator 03925_if_nullable_const_branch 03925_json_shared_data_buckets_missing_stream_bug 03925_sparse_values_in_substreams_cache_bug 03925_tuple_comparison_nothing_type +03925_variant_adaptor_const_lowcardinality +03926_convert_to_full_recursive_tuple_sparse 03926_group_by_use_nulls_constant_type 03926_semi_join_swap_tables +03927_correlated_subquery_table_function_context +03927_formatting_consistency_tuple_on_clause 03927_join_use_nulls_subcolumn 03927_json_typed_path_tuple_element +03927_nested_columns_duplicate_identifier +03927_row_filter_in_read_in_order_optimization +03928_deferred_filters_after_final_in_explain 03928_full_join_swap_tables 03928_json_advanced_shared_data_bug +03928_materialized_cte_index +03928_materialized_cte_index_2 03928_merge_lambda_alias_columns 03976_geometry_functions_accept_subtypes +03977_pr_expected_join 03979_datetime64_preepoch_partition_pruning +03980_datetime64_preepoch_partition_pruning_explain 03984_in_single_column_reference 03985_parenthesized_from_join +03987_concat_variant_low_cardinality 03988_concat_with_separator_format_variant_low_cardinality 03988_grace_hash_join_leftover_blocks +03988_lazy_final_replacing_merge_tree +03988_map_contains_key_like_tokenbf +03988_map_with_buckets_create_and_write 03989_codec_ignore_nulls_formatting +03990_map_subcolumn_skip_index +03991_json_single_bucket_empty_shared_data 03992_join_using_remote_table_function_with_alias +03992_map_subcolumns_small_memory 03993_join_no_overlapping_columns_85622 04001_logical_error_with_cube 04002_sign_jit_miscompilation @@ -2644,6 +3150,7 @@ 04145_url_glob_parallelize_output 04146_obfuscate_query_known_identifiers 04146_short_circuit_evaluation_for_nulls +04146_spilling_hash_join_low_threshold 04146_to_hour_monotonicity_dt64_timezone 04200_fix_time64_scale_conversion 04201_to_millisecond_negative_datetime64 @@ -2652,6 +3159,14 @@ 04202_projection_prewhere_materialize 04202_variant_basic_mode_only_nulls 04207_cross_join_starts_with_memory +04216_materialized_view_reused_target_name_dependency 04217_array_concat_json_extract_keys_index +04217_arrayjoin_set_index_lowcardinality_70058 04217_create_table_as_with_settings +04221_dictgetordefault_arrayjoin_80703 +04222_prewhere_final_replacing_86268 +04223_text_index_direct_read_like_90802 +04225_row_policy_groupby_final_91946 +04227_prewhere_parameterized_view_join_102074 +04228_row_policy_view_join_analyzer_102651 4060_row_policy_projection_not_found_column From 63111da0621a5ad0d57ec1c4781a0251ebf2602d Mon Sep 17 00:00:00 2001 From: Peter Leonov Date: Wed, 13 May 2026 21:39:44 +0200 Subject: [PATCH 22/23] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .claude/skills/setup/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md index 69633c92f..8063ae094 100644 --- a/.claude/skills/setup/SKILL.md +++ b/.claude/skills/setup/SKILL.md @@ -20,7 +20,7 @@ Use this skill before running any of the `npm run test:*`, `npm run lint`, `npm ## Prerequisites -- **Node.js 22 recommended** (matches `.nvmrc`). The root `package.json` declares `"engines": { "node": ">=20" }`, and CI tests Node 20, 22, and 24. +- **Node.js 22 recommended** (matches `.nvmrc`). The root `package.json` declares `"engines": { "node": ">=20.19.0" }`, and CI tests Node 20, 22, and 24. - **Docker** with the Compose plugin (`docker compose ...`). Required only for integration tests and any example that talks to a real server. ## 1. Install dependencies From 37573c9aa753a2904e1b08206fa7e0a8a8314080 Mon Sep 17 00:00:00 2001 From: Peter Leonov Date: Wed, 13 May 2026 21:39:57 +0200 Subject: [PATCH 23/23] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b35be0ceb..ce64c07a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ const client = createClient({ }) ``` -- The `@clickhouse/client` npm package now ships an embedded AI-agent skill, `clickhouse-js-node-troubleshooting`, under `node_modules/@clickhouse/client/skills/`. The skill is also declared in the `agents.skills` field of the package manifest for discovery tools that scan `node_modules`. This allows agentic coding tools to load focused, Node-client-specific troubleshooting guidance without any additional setup. ([#682]) +- The `@clickhouse/client` npm package now ships embedded AI-agent skills, `clickhouse-js-node-coding` and `clickhouse-js-node-troubleshooting`, under `node_modules/@clickhouse/client/skills/`. These skills are also declared in the `agents.skills` field of the package manifest for discovery tools that scan `node_modules`. This allows agentic coding tools to load focused, Node-client-specific coding and troubleshooting guidance without any additional setup. ([#682]) [#682]: https://github.com/ClickHouse/clickhouse-js/pull/682