diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index c1ec17beb..0ee945a99 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -59,6 +59,68 @@ fn pg_system_u32_to_json(row: &Row, idx: usize) -> Option { row.try_get::<_, PgSystemU32>(idx).ok().map(|v| pg_u32_number(v.0)) } +fn pg_optional_array_to_json( + values: Vec>, + map_value: impl Fn(T) -> serde_json::Value, +) -> serde_json::Value { + serde_json::Value::Array( + values.into_iter().map(|value| value.map(&map_value).unwrap_or(serde_json::Value::Null)).collect(), + ) +} + +fn pg_float_number(v: f64) -> serde_json::Value { + serde_json::Number::from_f64(v).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null) +} + +fn pg_array_to_json_value(row: &Row, idx: usize) -> Option { + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, serde_json::Value::String)); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, serde_json::Value::Bool)); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_string()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_string()))); + } + if let Ok(values) = row.try_get::<_, Vec>>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_rfc3339()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_string()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_string()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::String(v.to_string()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, pg_u32_number)); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::Number(v.into()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::Number(v.into()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| serde_json::Value::Number(v.into()))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, super::safe_i64_to_json)); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, |v| pg_float_number(v as f64))); + } + if let Ok(values) = row.try_get::<_, Vec>>(idx) { + return Some(pg_optional_array_to_json(values, pg_float_number)); + } + None +} + fn pg_value_to_json(row: &Row, idx: usize, type_name: &str) -> serde_json::Value { let upper = type_name.to_uppercase(); @@ -105,6 +167,10 @@ fn pg_value_to_json(row: &Row, idx: usize, type_name: &str) -> serde_json::Value return pg_system_u32_to_json(row, idx).unwrap_or(serde_json::Value::Null); } + if upper.starts_with('_') { + return pg_array_to_json_value(row, idx).unwrap_or(serde_json::Value::Null); + } + row.try_get::<_, String>(idx) .map(serde_json::Value::String) .or_else(|e| pg_system_u32_to_json(row, idx).ok_or(e)) @@ -112,26 +178,7 @@ fn pg_value_to_json(row: &Row, idx: usize, type_name: &str) -> serde_json::Value .or_else(|_| row.try_get::<_, i32>(idx).map(|v| serde_json::Value::Number(v.into()))) .or_else(|_| row.try_get::<_, i16>(idx).map(|v| serde_json::Value::Number(v.into()))) .or_else(|_| row.try_get::<_, i8>(idx).map(|v| serde_json::Value::Number(v.into()))) - .or_else(|_| { - row.try_get::<_, Vec>(idx) - .map(|v| serde_json::Value::Array(v.into_iter().map(pg_u32_number).collect())) - }) - .or_else(|_| { - row.try_get::<_, Vec>(idx) - .map(|v| serde_json::Value::Array(v.into_iter().map(|v| serde_json::Value::Number(v.into())).collect())) - }) - .or_else(|_| { - row.try_get::<_, Vec>(idx) - .map(|v| serde_json::Value::Array(v.into_iter().map(|v| serde_json::Value::Number(v.into())).collect())) - }) - .or_else(|_| { - row.try_get::<_, Vec>(idx) - .map(|v| serde_json::Value::Array(v.into_iter().map(|v| serde_json::Value::Number(v.into())).collect())) - }) - .or_else(|_| { - row.try_get::<_, Vec>(idx) - .map(|v| serde_json::Value::Array(v.into_iter().map(|v| serde_json::Value::Number(v.into())).collect())) - }) + .or_else(|e| pg_array_to_json_value(row, idx).ok_or(e)) .or_else(|_| { row.try_get::<_, f64>(idx).map(|v| { serde_json::Number::from_f64(v).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null) @@ -854,6 +901,16 @@ mod tests { assert!(!PgSystemU32::accepts(&Type::INT4)); } + #[test] + fn pg_optional_array_to_json_preserves_text_values_and_nulls() { + let value = pg_optional_array_to_json( + vec![Some("productManager".to_string()), None, Some("projectOwner".to_string())], + serde_json::Value::String, + ); + + assert_eq!(value, serde_json::json!(["productManager", null, "projectOwner"])); + } + #[test] fn pg_quote_ident_plain_identifier() { assert_eq!(pg_quote_ident("public"), "\"public\"");