Skip to content

Fix inserts into tables whose name requires quoting (#602) - #603

Open
polyglotAI-bot wants to merge 1 commit into
mainfrom
polyglot/quote-insert-table-name
Open

Fix inserts into tables whose name requires quoting (#602)#603
polyglotAI-bot wants to merge 1 commit into
mainfrom
polyglot/quote-insert-table-name

Conversation

@polyglotAI-bot

Copy link
Copy Markdown
Collaborator

Description

Fixes #602.

The insert paths spliced the caller's table string straight into the generated SQL, so a legal ClickHouse table name that is not a legal unquoted identifier could never be used: FROM my-table parses as my minus table and the server answers SYNTAX_ERROR, even though the table exists. The column names on the InsertBinaryAsync path already went through EncloseColumnName() a few lines away, and SchemaResolver.BuildCacheKey already enclosed the table name for the cache key — only the name that reached the SQL was left bare. InsertRawStreamAsync left its column names bare as well, unlike every other insert path.

The fix treats the argument as a name rather than a SQL fragment. EncloseQualifiedName() splits a possibly database-qualified name on its top-level separators and encloses each part, so database.table becomes `database`.`table`. A part the caller already enclosed is returned unchanged, which keeps the documented workaround (pre-quoting the argument) working and makes the operation idempotent — needed because the three sites enclose independently.

Changes

  • ClickHouse.Driver/Utility/StringExtensions.cs: new internal EncloseQualifiedName(). Splits on separators outside an enclosed part (backslash escapes inside one are honored) and encloses each part with the existing EncloseColumnName(). A part enclosed in backticks, or in the double quotes ClickHouse equally accepts around an identifier, is left as it is.
  • ClickHouse.Driver/Utility/SchemaResolver.cs: encloses the table name in the SELECT … WHERE 1=0 schema probe, and builds the schema cache key with the same helper, so the bare and pre-quoted spellings of one table no longer occupy two cache entries.
  • ClickHouse.Driver/ClickHouseClient.cs: encloses the table name in the INSERT INTO … FORMAT … statement of both binary insert overloads and of InsertRawStreamAsync, and encloses InsertRawStreamAsync's column names.
  • docs/overview.mdx: states the contract for the bulk-insert and raw-stream-insert sections.
  • changelog.d/602-quote-insert-table-name.fixes.md.

Test

ClickHouse.Driver.Tests/SQL/QuotedIdentifierInsertTests.cs inserts into tables whose names require quoting, against a real server:

  • InsertBinaryAsync, parametrized over three name shapes (hyphen, space, embedded backtick) — covers the schema probe and the INSERT statement. Seven of these cases fail on main with SYNTAX_ERROR.
  • InsertBinaryAsync with InsertOptions.ColumnTypes, where no probe is sent and the INSERT statement is the only use of the name.
  • InsertRawStreamAsync with a name requiring quoting, and with a column name requiring quoting.
  • Two compatibility cases: an argument the caller pre-quoted in backticks (passes on main; it is the existing workaround) and one in double quotes. Both must be quoted once, not twice.

ClickHouse.Driver.Tests/Utility/StringExtensionsTests.cs pins the parsing: qualified and unqualified names, a separator inside an enclosed part, backtick escaping, partially enclosed input, and idempotence.

Two existing tests assert the generated statement text and were updated to expect the quoted name (InsertBinaryQueryPlacementTests, Copy/InsertBinaryRequestBufferingTests). Both still assert exact equality; the expectation is spelled out from the database and table name rather than taken from the helper under test.

Full suite on net10.0: 10963 passed, 0 failed, 142 skipped.

Compatibility

Callers that pass a table name — including a database.table name, and one they pre-quoted themselves — are unaffected. A caller that used the parameter as a SQL fragment (for example INSERT INTO FUNCTION …, or an inlined column list) would now have that fragment enclosed as a name. The parameter is documented as a table name, and InsertRawStreamAsync has a dedicated columns argument, so this is a deliberate narrowing rather than a supported use.

Pre-PR validation gate

  • Deterministic repro confirmed (new tests fail on main, pass on the branch)
  • Root cause documented above
  • Fix targets the root cause, at all three sites that build the SQL
  • Test fails without fix, passes with fix
  • No existing tests weakened; full suite green
  • Convention compliance per AGENTS.md (unique table names, parametrized cases, docs, changelog fragment)
  • Public API unchanged (the new helper is internal)

InsertBinaryAsync and InsertRawStreamAsync concatenated the caller's table
name into the generated SQL, so a legal ClickHouse name that requires
quoting (my-table, user events) failed with SYNTAX_ERROR. Enclose every
part of the possibly database-qualified name, and the column names of
InsertRawStreamAsync, leaving a part the caller already enclosed as it is.

Fixes: #602
Copilot AI lite review requested due to automatic review settings August 31, 2026 14:26
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes ClickHouse bulk/raw insert paths to treat the table argument as an identifier (not a SQL fragment) by consistently enclosing table names (including database.table) and raw-stream column names, enabling inserts into tables/columns that require quoting (e.g. my-table, user events, names with backticks).

Changes:

  • Added EncloseQualifiedName() to safely quote qualified identifiers part-by-part while preserving already-quoted input.
  • Updated schema probe + insert SQL generation to use quoted table names; updated raw-stream inserts to quote column names too.
  • Added/updated tests plus docs and changelog fragment to lock in the new identifier-quoting contract.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
docs/overview.mdx Documents that table/column parameters are treated as names and will be enclosed (qualified names part-by-part).
ClickHouse.Driver/Utility/StringExtensions.cs Introduces EncloseQualifiedName() (splits qualified names safely and encloses each part once).
ClickHouse.Driver/Utility/SchemaResolver.cs Quotes table name in schema probe and normalizes schema cache key to the same quoting behavior.
ClickHouse.Driver/ClickHouseClient.cs Quotes table name in INSERT INTO ... for binary and raw-stream insert paths; quotes raw-stream column list.
ClickHouse.Driver.Tests/Utility/StringExtensionsTests.cs Adds unit tests for qualified-name parsing/quoting and idempotence.
ClickHouse.Driver.Tests/SQL/QuotedIdentifierInsertTests.cs Adds integration tests covering inserts into quoted-required table/column names across affected APIs.
ClickHouse.Driver.Tests/InsertBinaryQueryPlacementTests.cs Updates exact-statement expectations to reflect quoted table names.
ClickHouse.Driver.Tests/Copy/InsertBinaryRequestBufferingTests.cs Updates expected request payload to reflect quoted table names.
changelog.d/602-quote-insert-table-name.fixes.md Adds user-facing changelog entry for the behavior fix.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

InsertBinaryAsync / InsertRawStreamAsync concatenate the raw table name into SQL — names needing backquotes cannot be used

2 participants