From 1d946173726f2d521e8a4364cd5ca708aea94042 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Thu, 30 Jul 2026 19:59:15 +0200 Subject: [PATCH] Fix database URL path decoding --- .../__tests__/unit/config.test.ts | 37 +++++++++++++++++++ packages/client-common/src/config.ts | 2 +- packages/client-node/CHANGELOG.md | 3 ++ packages/client-web/CHANGELOG.md | 3 ++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/client-common/__tests__/unit/config.test.ts b/packages/client-common/__tests__/unit/config.test.ts index 71a1e806c..03824139b 100644 --- a/packages/client-common/__tests__/unit/config.test.ts +++ b/packages/client-common/__tests__/unit/config.test.ts @@ -8,6 +8,7 @@ import { getConnectionParams, LogWriter, numberConfigURLValue, + toSearchParams, } from "../../src/index"; import { TestLogger } from "../utils/test_logger"; import type { BaseClickHouseClientConfigOptionsWithURL } from "../../src/config"; @@ -33,6 +34,16 @@ describe("config", () => { expect(res).toEqual(defaultConfig); }); + it("should preserve percent sequences in the database option", () => { + const config = prepareConfigWithURL( + { database: "my%20database" }, + logger, + null, + ); + + expect(config.database).toBe("my%20database"); + }); + it("should fall back to default HTTP/HTTPS port numbers", async () => { expect( prepareConfigWithURL({ url: "http://localhost:80" }, logger, null), @@ -971,6 +982,32 @@ describe("config", () => { }); }); + it.each([ + ["spaces", "my%20database", "my database", "my+database"], + ["Unicode characters", "分析", "分析", "%E5%88%86%E6%9E%90"], + [ + "literal percent signs", + "my%2520database", + "my%20database", + "my%2520database", + ], + ])( + "should decode %s in database names from URL paths", + (_characters, path, database, encodedDatabase) => { + const url = new URL(`http://localhost:8124/${path}`); + const [, config] = loadConfigOptionsFromURL(url, null); + const searchParams = toSearchParams({ + database: config.database, + query_id: "query-id", + }); + + expect(config.database).toBe(database); + expect(searchParams.toString().split("&")).toContain( + `database=${encodedDatabase}`, + ); + }, + ); + it("should load only the settings from the URL, without auth", async () => { const url = new URL( "http://localhost:8124/?" + diff --git a/packages/client-common/src/config.ts b/packages/client-common/src/config.ts index a72b1514c..f7d6029e1 100644 --- a/packages/client-common/src/config.ts +++ b/packages/client-common/src/config.ts @@ -498,7 +498,7 @@ export function loadConfigOptionsFromURL( config.password = decodeURIComponent(url.password); } if (url.pathname.trim().length > 1) { - config.database = url.pathname.slice(1); + config.database = decodeURIComponent(url.pathname.slice(1)); } const urlSearchParamsKeys = [...url.searchParams.keys()]; if (urlSearchParamsKeys.length > 0) { diff --git a/packages/client-node/CHANGELOG.md b/packages/client-node/CHANGELOG.md index 435f9b73f..355e497c6 100644 --- a/packages/client-node/CHANGELOG.md +++ b/packages/client-node/CHANGELOG.md @@ -14,9 +14,12 @@ ## Bug fixes +- Fixed database names from URL paths being percent-encoded twice when they contain spaces, Unicode characters, or literal percent signs. ([#977]) + - Fixed `Array(Date)` / `Array(Date32)` query-parameter binding (and other temporal element types nested in arrays, tuples, and maps). A JS `Date` inside a container was serialized as a bare Unix timestamp (e.g. `[1683244800]`), which the server's `Array(Date)` element parser rejects (`CANNOT_PARSE_INPUT_ASSERTION_FAILED`). Container-nested `Date` values are now emitted as a quoted UTC date string (e.g. `['2023-05-05']`), the one encoding every temporal element type accepts. Note: a `Date` used inside `Array(DateTime)` / `Array(DateTime64)` is now bound at day precision (the time-of-day is dropped), since date-only is the only form `Array(Date)` accepts; scalar `Date` / `DateTime` binding is unchanged. ([#947]) [#947]: https://github.com/ClickHouse/clickhouse-js/pull/947 +[#977]: https://github.com/ClickHouse/clickhouse-js/pull/977 # 1.23.1 diff --git a/packages/client-web/CHANGELOG.md b/packages/client-web/CHANGELOG.md index 12fb839c6..cc49a3279 100644 --- a/packages/client-web/CHANGELOG.md +++ b/packages/client-web/CHANGELOG.md @@ -14,9 +14,12 @@ ## Bug fixes +- Fixed database names from URL paths being percent-encoded twice when they contain spaces, Unicode characters, or literal percent signs. ([#977]) + - Fixed `Array(Date)` / `Array(Date32)` query-parameter binding (and other temporal element types nested in arrays, tuples, and maps). A JS `Date` inside a container was serialized as a bare Unix timestamp (e.g. `[1683244800]`), which the server's `Array(Date)` element parser rejects (`CANNOT_PARSE_INPUT_ASSERTION_FAILED`). Container-nested `Date` values are now emitted as a quoted UTC date string (e.g. `['2023-05-05']`), the one encoding every temporal element type accepts. Note: a `Date` used inside `Array(DateTime)` / `Array(DateTime64)` is now bound at day precision (the time-of-day is dropped), since date-only is the only form `Array(Date)` accepts; scalar `Date` / `DateTime` binding is unchanged. ([#947]) [#947]: https://github.com/ClickHouse/clickhouse-js/pull/947 +[#977]: https://github.com/ClickHouse/clickhouse-js/pull/977 # 1.23.1