diff --git a/core/util.rs b/core/util.rs index 04bc72650..803c0cc59 100644 --- a/core/util.rs +++ b/core/util.rs @@ -2621,6 +2621,25 @@ pub mod tests { ); } + #[test] + fn test_trim_ascii_whitespace_helper() { + assert_eq!(trim_ascii_whitespace(" hello "), "hello"); + assert_eq!(trim_ascii_whitespace("\t\nhello\r\n"), "hello"); + assert_eq!(trim_ascii_whitespace("hello"), "hello"); + assert_eq!(trim_ascii_whitespace(" "), ""); + assert_eq!(trim_ascii_whitespace(""), ""); + + // non-breaking space should NOT be trimmed + assert_eq!( + trim_ascii_whitespace("\u{00A0}hello\u{00A0}"), + "\u{00A0}hello\u{00A0}" + ); + assert_eq!( + trim_ascii_whitespace(" \u{00A0}hello\u{00A0} "), + "\u{00A0}hello\u{00A0}" + ); + } + #[test] fn test_cast_real_to_integer_limits() { let max_exact = ((1i64 << 51) - 1) as f64; diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index e0855caac..fa263ea45 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -10205,4 +10205,62 @@ mod tests { let expected = "3.46.1"; assert_eq!(execute_turso_version(version_integer), expected); } + + #[test] + fn test_ascii_whitespace_is_trimmed() { + // Regular ASCII whitespace SHOULD be trimmed + let ascii_whitespace_cases = vec![ + (" 12", 12i64), // space + ("12 ", 12i64), // trailing space + (" 12 ", 12i64), // both sides + ("\t42\t", 42i64), // tab + ("\n99\n", 99i64), // newline + (" \t\n123\r\n ", 123i64), // mixed ASCII whitespace + ]; + + for (input, expected_int) in ascii_whitespace_cases { + let mut register = Register::Value(Value::Text(input.into())); + apply_affinity_char(&mut register, Affinity::Integer); + + match register { + Register::Value(Value::Integer(i)) => { + assert_eq!( + i, expected_int, + "String '{input}' should convert to {expected_int}, got {i}" + ); + } + other => { + panic!("String '{input}' should be converted to integer {expected_int}, got {other:?}"); + } + } + } + } + + #[test] + fn test_non_breaking_space_not_trimmed() { + let test_strings = vec![ + ("12\u{00A0}", "text", 3), // '12' + non-breaking space (3 chars, 4 bytes) + ("\u{00A0}12", "text", 3), // non-breaking space + '12' (3 chars, 4 bytes) + ("12\u{00A0}34", "text", 5), // '12' + nbsp + '34' (5 chars, 6 bytes) + ]; + + for (input, _expected_type, expected_len) in test_strings { + let mut register = Register::Value(Value::Text(input.into())); + apply_affinity_char(&mut register, Affinity::Integer); + + match register { + Register::Value(Value::Text(t)) => { + assert_eq!( + t.as_str().chars().count(), + expected_len, + "String '{input}' should have {expected_len} characters", + ); + } + Register::Value(Value::Integer(_)) => { + panic!("String '{input}' should NOT be converted to integer"); + } + other => panic!("Unexpected value type: {other:?}"), + } + } + } } diff --git a/testing/affinity.test b/testing/affinity.test index 1c32d4e4d..d580856c2 100755 --- a/testing/affinity.test +++ b/testing/affinity.test @@ -20,3 +20,9 @@ do_execsql_test_on_specific_db {:memory:} affinity-rowid { select * from t where a = '1'; } {1 1} + +do_execsql_test_on_specific_db {:memory:} affinity-ascii-whitespace-1.1 { + CREATE TABLE nb1(i INTEGER); + INSERT INTO nb1 VALUES ('12' || CHAR(160)); + SELECT TYPEOF(i), LENGTH(i) FROM nb1; +} {text|3} \ No newline at end of file