fix(data-grid): preserve literal backslashes

This commit is contained in:
Abeautifulsnow 2026-07-22 15:11:50 +08:00 committed by GitHub
parent 63f375a7d8
commit faca91a8e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 49 additions and 0 deletions

View File

@ -1795,8 +1795,18 @@ pub fn format_grid_sql_literal(
} else {
text
};
if database_type == Some(DatabaseType::Postgres) && literal_text.contains('\\') {
// Escape strings have stable backslash semantics regardless of the
// session's standard_conforming_strings setting.
let escaped_text = literal_text.replace('\\', "\\\\").replace('\'', "''");
return format!("E'{escaped_text}'");
}
let escaped_text = if database_type == Some(DatabaseType::Neo4j) {
literal_text.replace('\\', "\\\\").replace('\'', "\\'")
} else if is_sqlite_literal_database(database_type) {
// SQLite-family engines do not treat backslash as a string-literal
// escape character, so only the quote delimiter needs escaping.
literal_text.replace('\'', "''")
} else {
literal_text.replace('\\', "\\\\").replace('\'', "''")
};
@ -1808,6 +1818,13 @@ pub fn format_grid_sql_literal(
}
}
fn is_sqlite_literal_database(database_type: Option<DatabaseType>) -> bool {
matches!(
database_type,
Some(DatabaseType::Sqlite | DatabaseType::Rqlite | DatabaseType::Turso | DatabaseType::CloudflareD1)
)
}
fn format_grid_save_sql_literal(
value: &Value,
database_type: Option<DatabaseType>,
@ -4754,6 +4771,38 @@ mod tests {
assert_eq!(format_grid_sql_literal(&json!("0"), Some(DatabaseType::Postgres), Some(&bit)), "'0'");
}
#[test]
fn postgres_literals_use_escape_strings_for_stable_backslash_semantics() {
let value = json!(r#"{"json_raw":"{\"foo\":1,\"bar\":\"sometext\"}"}"#);
assert_eq!(
format_grid_sql_literal(&value, Some(DatabaseType::Postgres), None),
r#"E'{"json_raw":"{\\"foo\\":1,\\"bar\\":\\"sometext\\"}"}'"#
);
assert_eq!(format_grid_sql_literal(&json!("it's"), Some(DatabaseType::Postgres), None), "'it''s'");
}
#[test]
fn sqlite_family_literals_do_not_double_escape_backslashes() {
let value = json!(r#"{"json_raw":"{\"foo\":1,\"bar\":\"sometext\"}"}"#);
for database_type in
[DatabaseType::Sqlite, DatabaseType::Rqlite, DatabaseType::Turso, DatabaseType::CloudflareD1]
{
assert_eq!(
format_grid_sql_literal(&value, Some(database_type), None),
r#"'{"json_raw":"{\"foo\":1,\"bar\":\"sometext\"}"}'"#
);
assert_eq!(format_grid_sql_literal(&json!("it's"), Some(database_type), None), "'it''s'");
}
}
#[test]
fn mysql_and_neo4j_literals_keep_doubling_backslashes() {
// MySQL (default sql_mode, without NO_BACKSLASH_ESCAPES) and Neo4j do treat
// backslash as an escape character, so this behavior must be preserved.
assert_eq!(format_grid_sql_literal(&json!(r"a\b"), Some(DatabaseType::Mysql), None), r"'a\\b'");
assert_eq!(format_grid_sql_literal(&json!(r"a\b"), Some(DatabaseType::Neo4j), None), r"'a\\b'");
}
#[test]
fn prepares_sqlserver_bitn_updates_with_numeric_literals() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {