From a85ccc3983eb8d6a40695377b08b7e1f95dcee51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20St=C3=B6ckel?= Date: Sun, 18 Oct 2020 11:32:15 +0200 Subject: [PATCH] fix(postgres): prefer parsing non-localized notice severity field In order to support PostgreSQL <= 9.5, the b'S' field of an error/notice message was parsed. However, this field can be localized and thus parsing can fail for instances that use a non-english locale. In version > 9.5, the b'V' field, that is guaranteed to be in english, was added. However, even for these versions parsing would fail as the b'S' field was also parsed. This patch prefers b'V' over b'S' if it exists and uses a default severity in case b'V' is not present and b'S' could not be parsed. Fixes #734 --- sqlx-core/src/postgres/message/response.rs | 65 ++++++++++++++++------ 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/sqlx-core/src/postgres/message/response.rs b/sqlx-core/src/postgres/message/response.rs index 8da3e10d9c..767dd76732 100644 --- a/sqlx-core/src/postgres/message/response.rs +++ b/sqlx-core/src/postgres/message/response.rs @@ -26,6 +26,29 @@ impl PgSeverity { } } +impl std::convert::TryFrom<&str> for PgSeverity { + type Error = Error; + + fn try_from(s: &str) -> Result { + let result = match s { + "PANIC" => PgSeverity::Panic, + "FATAL" => PgSeverity::Fatal, + "ERROR" => PgSeverity::Error, + "WARNING" => PgSeverity::Warning, + "NOTICE" => PgSeverity::Notice, + "DEBUG" => PgSeverity::Debug, + "INFO" => PgSeverity::Info, + "LOG" => PgSeverity::Log, + + severity => { + return Err(err_protocol!("unknown severity: {:?}", severity)); + } + }; + + Ok(result) + } +} + #[derive(Debug)] pub struct Notice { storage: Bytes, @@ -84,7 +107,12 @@ impl Notice { impl Decode<'_> for Notice { fn decode_with(buf: Bytes, _: ()) -> Result { - let mut severity = PgSeverity::Log; + // In order to support PostgreSQL 9.5 and older we need to parse the localized S field. + // Newer versions additionally come with the V field that is guaranteed to be in English. + // We thus read both versions and prefer the unlocalized one if available. + const DEFAULT_SEVERITY: PgSeverity = PgSeverity::Log; + let mut severity_v = None; + let mut severity_s = None; let mut message = (0, 0); let mut code = (0, 0); @@ -103,23 +131,24 @@ impl Decode<'_> for Notice { break; } + use std::convert::TryInto; match field { - b'S' | b'V' => { - // unwrap: impossible to fail at this point - severity = match from_utf8(&buf[v.0 as usize..v.1 as usize]).unwrap() { - "PANIC" => PgSeverity::Panic, - "FATAL" => PgSeverity::Fatal, - "ERROR" => PgSeverity::Error, - "WARNING" => PgSeverity::Warning, - "NOTICE" => PgSeverity::Notice, - "DEBUG" => PgSeverity::Debug, - "INFO" => PgSeverity::Info, - "LOG" => PgSeverity::Log, - - severity => { - return Err(err_protocol!("unknown severity: {:?}", severity)); - } - }; + b'S' => { + // Discard potential errors, because the message might be localized + severity_s = from_utf8(&buf[v.0 as usize..v.1 as usize]) + .unwrap() + .try_into() + .ok(); + } + + b'V' => { + // Propagate errors here, because V is not localized and thus we are missing a possible + // variant. + severity_v = Some( + from_utf8(&buf[v.0 as usize..v.1 as usize]) + .unwrap() + .try_into()?, + ); } b'M' => { @@ -135,7 +164,7 @@ impl Decode<'_> for Notice { } Ok(Self { - severity, + severity: severity_v.or(severity_s).unwrap_or(DEFAULT_SEVERITY), message, code, storage: buf,