Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@

- Improved SEA connection-failure error messages.

- Fixed string parameters silently losing apostrophes when `supportManyParameters=1` interpolates them into SQL.
`SQLInterpolator` escaped `'` as the SQL-standard `''`, which Databricks does not implement: `'O''Brien'` is read
as the adjacent literals `'O'` and `'Brien'` and concatenated, so `O'Brien` was stored as `OBrien`. Quotes are now
escaped as `\'`, consistent with the backslash escaping already applied to `\`, `\n`, `\r` and `\t`.
Data written through this path before the upgrade still holds the stripped values, so lookups that now send the
apostrophe (`WHERE name = ?` bound to `O'Brien`) no longer match those rows. Only values written with
`supportManyParameters=1` are affected; correcting them requires a data fix.

- Fixed `NullPointerException` being thrown when materializing an array containing nested object types (other arrays, structs or maps) as `DatabricksArray` when some or all elements are literal `null`.
---
*Note: When making changes, please add your change under the appropriate section
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ protected static String escapeInputs(String input) {
i += Character.charCount(codePoint);
switch (codePoint) {
case '\'':
out.append("''");
break; // SQL-standard quote escape
out.append("\\'");
break; // Databricks escapes a quote as \', not as the SQL-standard ''
case '\\':
out.append("\\\\");
break; // escape backslash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void testEscapedValues() throws DatabricksValidationException {
Map<Integer, ImmutableSqlParameter> params = new HashMap<>();
params.put(1, getSqlParam(1, "O'Reilly", DatabricksTypeUtil.STRING));
params.put(2, getSqlParam(2, 200, DatabricksTypeUtil.INT));
String expected = "UPDATE products SET price = 'O''Reilly' WHERE id = 200";
String expected = "UPDATE products SET price = 'O\\'Reilly' WHERE id = 200";
assertEquals(expected, SQLInterpolator.interpolateSQL(sql, params));
}

Expand Down Expand Up @@ -108,7 +108,7 @@ public void testBinaryTypeEscapesMaliciousHexLookalike() throws DatabricksValida
String sql = "SELECT ?";
Map<Integer, ImmutableSqlParameter> params = new HashMap<>();
params.put(1, getSqlParam(1, "X'41' OR 1=1 --", DatabricksTypeUtil.BINARY));
assertEquals("SELECT 'X''41'' OR 1=1 --'", SQLInterpolator.interpolateSQL(sql, params));
assertEquals("SELECT 'X\\'41\\' OR 1=1 --'", SQLInterpolator.interpolateSQL(sql, params));
}

@Test
Expand Down Expand Up @@ -217,8 +217,12 @@ public void testInterpolatePlaceholderAtStartAndEnd() throws DatabricksValidatio

@Test
public void testEscapeInputs() {
// Simple apostrophe doubling
assertEquals("'foo''bar'", SQLInterpolator.escapeInputs("foo'bar"));
// Apostrophe escaped with a backslash; '' would be read as two adjacent literals
assertEquals("'foo\\'bar'", SQLInterpolator.escapeInputs("foo'bar"));
// Leading, trailing and consecutive apostrophes
assertEquals("'\\'lead'", SQLInterpolator.escapeInputs("'lead"));
assertEquals("'trail\\''", SQLInterpolator.escapeInputs("trail'"));
assertEquals("'a\\'\\'b'", SQLInterpolator.escapeInputs("a''b"));
// Escaping newlines
assertEquals("'line1\\nline2'", SQLInterpolator.escapeInputs("line1\nline2"));
// Escaping backslashes
Expand Down