Fix string escaping when creating sql#106
Conversation
There was a problem hiding this comment.
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 invalues.Value()andvalues.NewMigrateValueQuoted(). - Replace local SQL quoting/escaping helpers in
destination/db/sql/sql.gowith calls intovalues. - 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.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
| // 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) { |
| 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)) | ||
| } |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
now it actually escapes it
| pb.DataType_BINARY, | ||
| pb.DataType_XML, | ||
| pb.DataType_JSON: | ||
| return fmt.Sprintf("'%s'", value), nil |
There was a problem hiding this comment.
The bug was here as it only added ' on the sides of the value. Now it also escapes ' and \
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:Fix:
\and'Footnote
conn.Execwith?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.