Skip to content

Fix string escaping when creating sql#106

Merged
koletzilla merged 6 commits into
mainfrom
clickhouse/fix-escaping-string
Jun 4, 2026
Merged

Fix string escaping when creating sql#106
koletzilla merged 6 commits into
mainfrom
clickhouse/fix-escaping-string

Conversation

@koletzilla

@koletzilla koletzilla commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Problem: We were not escaping the strings when creating the SQL to remove rows in some of the operations. So a ' in the middle of a value would break the SQL causing an error like:

Syntax error: failed at position 1423 (s) (line 2, col 1331): xxxx'xxx"}',''), Expected one of: comma or closing bracket, token, Comma, ClosingRoundBracket, OR, AND, IS NOT DISTINCT FROM, IS DISTINCT FROM, IS NULL, IS NOT NULL, BETWEEN, NOT BETWEEN, LIKE, ILIKE, NOT LIKE, NOT ILIKE, REGEXP, IN, NOT IN, GLOBAL IN, GLOBAL NOT IN, MOD, DIV, alias, AS

Fix:

  • Move escaping logic to a single place (values.go) and include escaping both \ and '
  • Use the new logic in all places where it was used before.
  • Use it inside the Values function, the place where we found the problem.

Footnote

  • We should have probably done this directly using conn.Exec with ? arguments so the clickhouse-go driver will actually do the escaping, but that require a few more changes that will complicate the PR and delay the fix. This PR put the code in a better position to tackle that next time.

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

This PR fixes malformed SQL generated when interpolating string literals containing special characters (notably ' and \) by centralizing ClickHouse string-literal escaping in destination/db/values and reusing it across SQL builders and migration helpers.

Changes:

  • Introduce values.QuoteAndEscapeString() (escaping \\\ and ''') and use it in values.Value() and values.NewMigrateValueQuoted().
  • Replace local SQL quoting/escaping helpers in destination/db/sql/sql.go with calls into values.
  • Update/extend tests and expected SQL strings to reflect the new escaping behavior (notably for backslashes).

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
destination/db/values/values.go Centralizes quoting/escaping logic and updates Value() / migration quoting to use it.
destination/db/values/values_test.go Adds tests for the new quoting/escaping helper (and should also assert the write-path Value() behavior).
destination/db/sql/sql.go Removes local escape helpers and reuses values.QuoteAndEscapeString() for mutation queries.
destination/db/sql/sql_test.go Removes tests for deleted escape helpers while keeping mutation query expectations intact.
destination/db/sql/sql_migrate_test.go Updates migration SQL expectations for backslash escaping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread destination/db/values/values.go
Comment thread destination/db/values/values_test.go
@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread destination/db/sql/sql.go
// GetLocalMutationsCompletedQuery generates a query to check if all mutations on a table are complete.
// Unlike GetAllMutationsCompletedQuery, this queries system.mutations directly (no clusterAllReplicas)
// and works on both local Docker ClickHouse and ClickHouse Cloud.
func GetLocalMutationsCompletedQuery(schemaName string, tableName string) (string, error) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

dead code

Comment thread destination/db/sql/sql.go
Comment on lines -720 to -727
func escapeSQLString(s string) string {
return strings.ReplaceAll(s, "'", "''")
}

// singleQuoted wraps a string in single quotes with escaping for SQL string literals.
func singleQuoted(s string) string {
return fmt.Sprintf("'%s'", escapeSQLString(s))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

moved to values.go

// Backslash in value
stmt, err = GetUpdateColumnValueStatement("s", "t", "col", values.NewMigrateValueQuoted("path\\to\\file"))
assert.NoError(t, err)
assert.Equal(t, "ALTER TABLE `s`.`t` UPDATE `col` = 'path\\to\\file' WHERE true", stmt)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

now it actually escapes it

pb.DataType_BINARY,
pb.DataType_XML,
pb.DataType_JSON:
return fmt.Sprintf("'%s'", value), nil

@koletzilla koletzilla Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The bug was here as it only added ' on the sides of the value. Now it also escapes ' and \

@koletzilla
koletzilla merged commit 8592b11 into main Jun 4, 2026
5 checks passed
@koletzilla
koletzilla deleted the clickhouse/fix-escaping-string branch June 4, 2026 10:00
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.

3 participants