From 1e99f12b1bb9b49f08a9162edd44103a68a2b40a Mon Sep 17 00:00:00 2001 From: amwps290 Date: Fri, 18 Sep 2026 14:11:49 +0800 Subject: [PATCH] fix(postgres): cast xml preview columns to text --- .../dbx-sql/src/sql_dialect/table_select.rs | 8 +++++-- crates/dbx-sql/src/sql_dialect/tests.rs | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/dbx-sql/src/sql_dialect/table_select.rs b/crates/dbx-sql/src/sql_dialect/table_select.rs index b1ddb6d8ae..76e0f08804 100644 --- a/crates/dbx-sql/src/sql_dialect/table_select.rs +++ b/crates/dbx-sql/src/sql_dialect/table_select.rs @@ -76,13 +76,17 @@ fn large_value_preview_kind( None } else if base == "bytea" { Some(LargeValuePreviewKind::Binary) - } else if matches!(base.as_str(), "char" | "character" | "varchar" | "text" | "citext" | "name" | "xml") + } else if matches!(base.as_str(), "char" | "character" | "varchar" | "text" | "citext" | "name") || normalized.starts_with("character varying") { Some(LargeValuePreviewKind::Text) } else if base == "vector" { Some(LargeValuePreviewKind::Vector) - } else if matches!(base.as_str(), "json" | "jsonb" | "tsvector") { + } else if matches!(base.as_str(), "json" | "jsonb" | "tsvector" | "xml") { + // `xml` must be cast to text: there is no `left(xml, int)` + // overload and no implicit xml -> text cast, so the preview + // would otherwise fail with + // `function left(xml, integer) does not exist`. Some(LargeValuePreviewKind::TextCast) } else { None diff --git a/crates/dbx-sql/src/sql_dialect/tests.rs b/crates/dbx-sql/src/sql_dialect/tests.rs index 1dccff2513..5974fc73e8 100644 --- a/crates/dbx-sql/src/sql_dialect/tests.rs +++ b/crates/dbx-sql/src/sql_dialect/tests.rs @@ -1033,6 +1033,28 @@ fn builds_pgvector_table_data_preview_with_compatible_text_cast() { assert!(sql.contains("'V:8192' AS \"__DBX_LARGE_VALUE_BYTES_V_1\"")); } +#[test] +fn postgres_xml_preview_casts_to_text() { + // `left(xml, int)` does not exist and xml has no implicit cast to text, + // so an uncast preview breaks table data reads with + // `function left(xml, integer) does not exist`. + let sql = build_table_data_select_sql(TableDataSelectSqlOptions { + database_type: Some(DatabaseType::Postgres), + schema: Some("public".to_string()), + table_name: "documents".to_string(), + primary_keys: vec!["id".to_string()], + columns: vec!["id".to_string(), "xml_value".to_string()], + column_types: vec!["integer".to_string(), "xml".to_string()], + large_value_preview_size: Some(8192), + limit: Some(100), + ..Default::default() + }); + + assert!(sql.contains("left(\"xml_value\"::text, 8193) AS \"xml_value\""), "sql was: {sql}"); + assert!(sql.contains("'T:8192' AS \"__DBX_LARGE_VALUE_BYTES_T_1\""), "sql was: {sql}"); + assert!(!sql.contains("left(\"xml_value\","), "sql was: {sql}"); +} + #[test] fn table_data_preview_requires_stable_keys_and_parallel_types() { let base = TableDataSelectSqlOptions {