From 5ad5b4fcc3cb03e6ac037bb6aacbd18bb2305a55 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 31 Jul 2026 00:47:23 +0800 Subject: [PATCH] fix(oracle): serialize RAW values with HEXTORAW --- crates/dbx-core/src/data_grid_sql.rs | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/crates/dbx-core/src/data_grid_sql.rs b/crates/dbx-core/src/data_grid_sql.rs index 2830a4790..13cf5fc42 100644 --- a/crates/dbx-core/src/data_grid_sql.rs +++ b/crates/dbx-core/src/data_grid_sql.rs @@ -1787,6 +1787,11 @@ pub fn format_grid_sql_literal( return literal; } } + if is_oracle_raw_literal_column(database_type, column_info) { + if let Some(literal) = format_oracle_raw_literal_text(&text) { + return literal; + } + } if column_info.map(|column| is_numeric_type(&column.data_type)).unwrap_or(false) && is_numeric_literal(&text) { // BigDecimal/BigInteger cells cross JSON-RPC as strings so browsers cannot round them. return text; @@ -2082,6 +2087,24 @@ fn format_mysql_binary_literal_text(text: &str) -> Option { } } +fn is_oracle_raw_literal_column(database_type: Option, column_info: Option<&DataGridColumnInfo>) -> bool { + is_oracle_temporal_literal_database(database_type) + && column_info.map(|column| is_oracle_raw_column_type(&column.data_type)).unwrap_or(false) +} + +fn is_oracle_raw_column_type(data_type: &str) -> bool { + data_type.trim().split(['(', ':', ' ']).next().is_some_and(|base| base.eq_ignore_ascii_case("raw")) +} + +fn format_oracle_raw_literal_text(text: &str) -> Option { + let hex = text.strip_prefix("0x")?; + if hex.len() % 2 == 0 && hex.chars().all(|ch| ch.is_ascii_hexdigit()) { + Some(format!("HEXTORAW('{hex}')")) + } else { + None + } +} + fn is_geometry_column_type(data_type: &str) -> bool { let lower = data_type.to_ascii_lowercase(); let base = lower.split('(').next().unwrap_or(&lower).trim(); @@ -4749,6 +4772,51 @@ mod tests { ); } + #[test] + fn prepares_oracle_raw_update_with_hex_literals() { + let result = prepare_data_grid_save(DataGridSaveStatementOptions { + database_type: Some(DatabaseType::Oracle), + identifier_quote: None, + table_meta: DataGridTableMeta { + catalog: None, + database: None, + schema: Some("APP".to_string()), + table_name: "RAW_VALUES".to_string(), + primary_keys: vec!["ID".to_string()], + columns: Some(vec![column("ID", "RAW(16)", false, None), column("PAYLOAD", "RAW(16)", true, None)]), + }, + columns: vec!["ID".to_string(), "PAYLOAD".to_string()], + source_columns: None, + rows: vec![vec![json!("0x00112233445566778899aabbccddeeff"), json!("0xaabb")]], + dirty_rows: vec![(0, vec![(1, json!("0xccdd"))])], + deleted_rows: vec![], + new_rows: vec![], + }); + + assert_eq!(result.validation_error, None); + assert_eq!( + result.statements, + vec![ + "UPDATE \"APP\".\"RAW_VALUES\" SET \"PAYLOAD\" = HEXTORAW('ccdd') WHERE \"ID\" = HEXTORAW('00112233445566778899aabbccddeeff');" + ] + ); + } + + #[test] + fn oracle_raw_literals_require_valid_even_length_hex() { + let raw = column("ID", "RAW(16)", false, None); + let text = column("ID", "VARCHAR2(64)", false, None); + let blob = column("ID", "BLOB", false, None); + + for database_type in [DatabaseType::Oracle, DatabaseType::OceanbaseOracle] { + assert_eq!(format_grid_sql_literal(&json!("0x00aB"), Some(database_type), Some(&raw)), "HEXTORAW('00aB')"); + assert_eq!(format_grid_sql_literal(&json!("0xabc"), Some(database_type), Some(&raw)), "'0xabc'"); + assert_eq!(format_grid_sql_literal(&json!("0x00gg"), Some(database_type), Some(&raw)), "'0x00gg'"); + assert_eq!(format_grid_sql_literal(&json!("0x00ab"), Some(database_type), Some(&text)), "'0x00ab'"); + assert_eq!(format_grid_sql_literal(&json!("0x00ab"), Some(database_type), Some(&blob)), "'0x00ab'"); + } + } + #[test] fn prepares_oceanbase_oracle_lob_deletes_with_synthetic_rowid() { let result = prepare_data_grid_save(DataGridSaveStatementOptions {