fix(postgres): parse GaussDB boolean bytes
This commit is contained in:
parent
06ed8dc5f6
commit
5f2fd528b0
|
|
@ -523,7 +523,7 @@ pub(crate) fn pg_value_to_json_classified(row: &Row, idx: usize, col_type: PgCol
|
|||
}
|
||||
serde_json::Value::Null
|
||||
}
|
||||
PgColType::Bool => row.try_get::<_, bool>(idx).map(serde_json::Value::Bool).unwrap_or(serde_json::Value::Null),
|
||||
PgColType::Bool => pg_bool_value_to_json(row, idx),
|
||||
PgColType::Interval => row
|
||||
.try_get::<_, PgInterval>(idx)
|
||||
.map(|interval| serde_json::Value::String(format_pg_interval(interval)))
|
||||
|
|
@ -2636,11 +2636,42 @@ fn parse_enum_values_from_row(row: &Row, index: usize) -> Option<Vec<String>> {
|
|||
serde_json::from_str::<Vec<String>>(&raw).ok()
|
||||
}
|
||||
|
||||
/// Decode a boolean column to JSON, tolerating databases (e.g. GaussDB) that
|
||||
/// encode booleans as the ASCII bytes `t` (0x74) / `f` (0x66) in the binary
|
||||
/// protocol instead of the standard PostgreSQL 0x00 / 0x01.
|
||||
fn pg_bool_value_to_json(row: &Row, idx: usize) -> serde_json::Value {
|
||||
if let Some(v) = pg_row_try_bool(row, idx) {
|
||||
return serde_json::Value::Bool(v);
|
||||
}
|
||||
serde_json::Value::Null
|
||||
}
|
||||
|
||||
/// Map raw boolean bytes to a Rust `bool`.
|
||||
///
|
||||
/// Standard PostgreSQL binary uses `[0x00]` / `[0x01]`; GaussDB sends the ASCII
|
||||
/// text representation `[b't']` / `[b'f']` instead.
|
||||
fn decode_bool_bytes(raw: &[u8]) -> Option<bool> {
|
||||
match raw {
|
||||
[0x00] => Some(false),
|
||||
[0x01] => Some(true),
|
||||
[b't'] | [b'T'] => Some(true),
|
||||
[b'f'] | [b'F'] => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_bool_candidates(raw: Option<&[u8]>, standard: Option<bool>) -> Option<bool> {
|
||||
raw.and_then(decode_bool_bytes).or(standard)
|
||||
}
|
||||
|
||||
/// Read a boolean column from a PostgreSQL row, tolerating databases that
|
||||
/// encode booleans as integers (0/1) or text ('t'/'f') instead of the standard
|
||||
/// `bool` OID. Returns `None` when the column is NULL or truly unreadable.
|
||||
fn pg_row_try_bool(row: &Row, idx: usize) -> Option<bool> {
|
||||
if let Ok(v) = row.try_get::<_, bool>(idx) {
|
||||
// GaussDB encodes boolean as ASCII 't' (0x74) / 'f' (0x66) in binary.
|
||||
let raw = row.try_get::<_, PgRawBytes>(idx).ok();
|
||||
let standard = row.try_get::<_, bool>(idx).ok();
|
||||
if let Some(v) = decode_bool_candidates(raw.as_ref().map(|value| value.0.as_slice()), standard) {
|
||||
return Some(v);
|
||||
}
|
||||
if let Ok(v) = row.try_get::<_, i32>(idx) {
|
||||
|
|
@ -4501,6 +4532,33 @@ mod tests {
|
|||
assert_eq!(raw.0, vec![0x01, 0xAB, 0xFF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_bool_bytes_handles_standard_and_gaussdb_encodings() {
|
||||
// Standard PostgreSQL binary boolean: 0x00 / 0x01
|
||||
assert_eq!(decode_bool_bytes(&[0x00]), Some(false));
|
||||
assert_eq!(decode_bool_bytes(&[0x01]), Some(true));
|
||||
// GaussDB binary boolean: ASCII 't' (0x74) / 'f' (0x66)
|
||||
assert_eq!(decode_bool_bytes(&[0x74]), Some(true));
|
||||
assert_eq!(decode_bool_bytes(&[0x66]), Some(false));
|
||||
assert_eq!(decode_bool_bytes(b"t"), Some(true));
|
||||
assert_eq!(decode_bool_bytes(b"f"), Some(false));
|
||||
assert_eq!(decode_bool_bytes(b"T"), Some(true));
|
||||
assert_eq!(decode_bool_bytes(b"F"), Some(false));
|
||||
// Unrecognized encodings return None
|
||||
assert_eq!(decode_bool_bytes(&[0x02]), None);
|
||||
assert_eq!(decode_bool_bytes(&[0x74, 0x66]), None);
|
||||
assert_eq!(decode_bool_bytes(&[]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_gaussdb_boolean_takes_precedence_over_standard_decoder() {
|
||||
assert_eq!(decode_bool_candidates(Some(b"f"), Some(true)), Some(false));
|
||||
assert_eq!(decode_bool_candidates(Some(b"t"), Some(true)), Some(true));
|
||||
assert_eq!(decode_bool_candidates(Some(&[0x00]), Some(true)), Some(false));
|
||||
assert_eq!(decode_bool_candidates(Some(&[0x01]), Some(false)), Some(true));
|
||||
assert_eq!(decode_bool_candidates(Some(&[0x02]), Some(false)), Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn postgres_foreign_keys_sql_selects_referential_actions() {
|
||||
let sql = postgres_foreign_keys_sql();
|
||||
|
|
|
|||
Loading…
Reference in New Issue