fix: back-quote insert() column identifiers with special characters#949
fix: back-quote insert() column identifiers with special characters#949polyglotAI-bot wants to merge 3 commits into
Conversation
Column names passed via `insert({ columns })` (both the array form and the
`columns.except` form) were joined verbatim into the generated statement, so a
name containing a space or other special character produced invalid SQL and a
server-side syntax error (e.g. `['test id']` -> `INSERT INTO t (test id) ...`).
Back-quote each identifier, escaping backslashes and backticks the same way the
ClickHouse server's `backQuote` does. Identifiers that are already quoted (with
backticks or double quotes) are passed through unchanged, preserving backward
compatibility for callers that pre-quoted their column names as a workaround.
The shared code lives in the common module bundled into both @clickhouse/client
and @clickhouse/client-web, so the fix applies to both.
Fixes: #945
There was a problem hiding this comment.
Pull request overview
Fixes invalid SQL generation in client.insert() when columns / columns.except include identifiers that require quoting (spaces, special characters) by auto back-quoting column identifiers while preserving backward compatibility for already-quoted inputs.
Changes:
- Add an
escapeColumnhelper and apply it to bothcolumns: string[]andcolumns: { except: string[] }paths ingetInsertQuery. - Update
InsertParams.columnsJSDoc to document the new quoting + passthrough behavior. - Add unit + integration tests covering spaced identifiers and escaping behavior; add changelog entries for both Node and Web packages.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/client-web/CHANGELOG.md | Adds a bug-fix entry for auto-quoting insert.columns / columns.except. |
| packages/client-node/CHANGELOG.md | Adds a bug-fix entry for auto-quoting insert.columns / columns.except. |
| packages/client-common/src/client.ts | Quotes/escapes user-provided column identifiers in generated INSERT column lists. |
| packages/client-common/tests/unit/insert_query_columns.test.ts | New unit coverage asserting exact generated INSERT SQL for tricky identifiers. |
| packages/client-common/tests/integration/insert_specific_columns.test.ts | New integration coverage for spaced column names in columns and columns.except. |
| # 1.23.2 | ||
|
|
||
| ## Bug Fixes | ||
|
|
||
| - `insert`: column names passed via the `columns` parameter (both the array form and the `columns.except` form) are now back-quoted, so names containing spaces or other special characters produce valid SQL instead of a server-side syntax error. Column names that are already quoted (with backticks or double quotes) are passed through unchanged, preserving backward compatibility for callers that pre-quoted them. ([#949]) | ||
|
|
||
| [#949]: https://github.com/ClickHouse/clickhouse-js/pull/949 |
There was a problem hiding this comment.
Thanks for flagging this — I checked it against the repo's changelog convention and current main, and I've deliberately kept the # 1.23.2 heading. Details:
This repo adds the next-version CHANGELOG heading ahead of the package.json bump. Per AGENTS.md: entries go under the top-most heading, and "if the most recent # x.y.z heading corresponds to an already-released version (check git tag), open a new top-level # x.y.z heading." # 1.23.1 is already released — git tag has client-web-1.23.1, and npm dist-tags.latest is 1.23.1 — so a new # 1.23.2 heading is the correct choice. The package.json bump is done later in a dedicated chore: bump version release PR (that's exactly how 1.23.1 landed: #935 added the # 1.23.1 heading while package.json was still 1.23.0, and #939/#940 bumped package.json to 1.23.1 afterward).
Most relevantly: #947 was merged to main earlier today and introduces the # 1.23.2 heading itself, with package.json still at 1.23.1 — precisely this pattern. This branch now merges main, so the # 1.23.2 heading actually comes from #947 and this PR simply appends its bullet underneath; the heading matches merged-main state.
So neither suggested change applies here: bumping package.json inside a bug-fix PR would bypass the dedicated release-PR flow, and the entry can't move under # 1.23.1 because that version is already published. Happy to adjust if the maintainers prefer a different approach.
| # 1.23.2 | ||
|
|
||
| ## Bug Fixes | ||
|
|
||
| - `insert`: column names passed via the `columns` parameter (both the array form and the `columns.except` form) are now back-quoted, so names containing spaces or other special characters produce valid SQL instead of a server-side syntax error. Column names that are already quoted (with backticks or double quotes) are passed through unchanged, preserving backward compatibility for callers that pre-quoted them. ([#949]) | ||
|
|
||
| [#949]: https://github.com/ClickHouse/clickhouse-js/pull/949 |
There was a problem hiding this comment.
Thanks for flagging this — I checked it against the repo's changelog convention and current main, and I've deliberately kept the # 1.23.2 heading. Details:
This repo adds the next-version CHANGELOG heading ahead of the package.json bump. Per AGENTS.md: entries go under the top-most heading, and "if the most recent # x.y.z heading corresponds to an already-released version (check git tag), open a new top-level # x.y.z heading." # 1.23.1 is already released — git tag has client-1.23.1, and npm dist-tags.latest is 1.23.1 — so a new # 1.23.2 heading is the correct choice. The package.json bump is done later in a dedicated chore: bump version release PR (that's exactly how 1.23.1 landed: #935 added the # 1.23.1 heading while package.json was still 1.23.0, and #939/#940 bumped package.json to 1.23.1 afterward).
Most relevantly: #947 was merged to main earlier today and introduces the # 1.23.2 heading itself, with package.json still at 1.23.1 — precisely this pattern. This branch now merges main, so the # 1.23.2 heading actually comes from #947 and this PR simply appends its bullet underneath; the heading matches merged-main state.
So neither suggested change applies here: bumping package.json inside a bug-fix PR would bypass the dedicated release-PR flow, and the entry can't move under # 1.23.1 because that version is already published. Happy to adjust if the maintainers prefer a different approach.
…ns-backtick-quote # Conflicts: # packages/client-node/CHANGELOG.md # packages/client-web/CHANGELOG.md
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
CI note — the failing The single failing check is the whole-tree That file is not part of this PR's diff — it is byte-identical to Root cause: Since I've intentionally kept the |
Description
Fixes #945.
getInsertQuerybuilt theINSERT INTO t (<cols>) FORMAT ...statement by joining thecolumnsarray (and thecolumns.exceptlist) verbatim. A column name containing a space or other special character therefore produced invalid SQL — e.g.columns: ['test id', 'name']generatedINSERT INTO t (test id, name) FORMAT ..., which the server rejects withCode: 62 ... Syntax error.The fix back-quotes each column identifier at the generation site, escaping backslashes and backticks exactly the way the ClickHouse server's own
backQuotedoes (\→\\,`→\`). Identifiers that are already quoted (with backticks or double quotes) are passed through unchanged, so callers that pre-quoted their column names as a workaround keep working — the change is backward compatible.Verified against ClickHouse 26.5:
(\test id`, `name`)` is accepted; the unquoted form is the reported syntax error.`a\b`targets the wrong column) — hence matchingbackQuote.`n.arr1`) targets the same physical column as the unquoted form, so no dot-splitting is needed.The changed code lives in the shared common module that is bundled into both
@clickhouse/clientand@clickhouse/client-web, so the fix (and changelog entries) apply to both.Changes
packages/client-common/src/client.ts: add anescapeColumnhelper and apply it to both the array andcolumns.exceptbranches ofgetInsertQuery; update theInsertParams.columnsJSDoc to document the auto-quoting + passthrough behavior.packages/client-node/CHANGELOG.mdandpackages/client-web/CHANGELOG.md: add a# 1.23.2→## Bug Fixesentry.Test
packages/client-common/__tests__/unit/insert_query_columns.test.ts, new): asserts the exact generated SQL through the realclient.insert()path (mocked connection) for plain, spaced, embedded-backtick, embedded-backslash, combined backslash+backtick, lone-backtick, and dotted/Nested identifiers, plus thecolumns.exceptcases. Includes contrast cases proving already back-quoted / double-quoted identifiers are passed through unchanged.insert_specific_columns.test.ts): inserts into a space-named column viacolumnsand excludes a space-named column viacolumns.except, then reads the data back.Pre-PR validation gate
main, passes on this branch)Note
Related source bug in another client: ClickHouse/clickhouse-cpp#30 (identifier quoting in generated INSERT column lists is an SDK-level concern each client implements independently). No backport is requested here; maintainers own any backport decision.