Skip to content

Commit 144dcce

Browse files
dstoeckelabonander
authored andcommitted
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
1 parent 228729e commit 144dcce

File tree

1 file changed

+47
-18
lines changed

1 file changed

+47
-18
lines changed

sqlx-core/src/postgres/message/response.rs

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,29 @@ impl PgSeverity {
2626
}
2727
}
2828

29+
impl std::convert::TryFrom<&str> for PgSeverity {
30+
type Error = Error;
31+
32+
fn try_from(s: &str) -> Result<PgSeverity, Error> {
33+
let result = match s {
34+
"PANIC" => PgSeverity::Panic,
35+
"FATAL" => PgSeverity::Fatal,
36+
"ERROR" => PgSeverity::Error,
37+
"WARNING" => PgSeverity::Warning,
38+
"NOTICE" => PgSeverity::Notice,
39+
"DEBUG" => PgSeverity::Debug,
40+
"INFO" => PgSeverity::Info,
41+
"LOG" => PgSeverity::Log,
42+
43+
severity => {
44+
return Err(err_protocol!("unknown severity: {:?}", severity));
45+
}
46+
};
47+
48+
Ok(result)
49+
}
50+
}
51+
2952
#[derive(Debug)]
3053
pub struct Notice {
3154
storage: Bytes,
@@ -84,7 +107,12 @@ impl Notice {
84107

85108
impl Decode<'_> for Notice {
86109
fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
87-
let mut severity = PgSeverity::Log;
110+
// In order to support PostgreSQL 9.5 and older we need to parse the localized S field.
111+
// Newer versions additionally come with the V field that is guaranteed to be in English.
112+
// We thus read both versions and prefer the unlocalized one if available.
113+
const DEFAULT_SEVERITY: PgSeverity = PgSeverity::Log;
114+
let mut severity_v = None;
115+
let mut severity_s = None;
88116
let mut message = (0, 0);
89117
let mut code = (0, 0);
90118

@@ -103,23 +131,24 @@ impl Decode<'_> for Notice {
103131
break;
104132
}
105133

134+
use std::convert::TryInto;
106135
match field {
107-
b'S' | b'V' => {
108-
// unwrap: impossible to fail at this point
109-
severity = match from_utf8(&buf[v.0 as usize..v.1 as usize]).unwrap() {
110-
"PANIC" => PgSeverity::Panic,
111-
"FATAL" => PgSeverity::Fatal,
112-
"ERROR" => PgSeverity::Error,
113-
"WARNING" => PgSeverity::Warning,
114-
"NOTICE" => PgSeverity::Notice,
115-
"DEBUG" => PgSeverity::Debug,
116-
"INFO" => PgSeverity::Info,
117-
"LOG" => PgSeverity::Log,
118-
119-
severity => {
120-
return Err(err_protocol!("unknown severity: {:?}", severity));
121-
}
122-
};
136+
b'S' => {
137+
// Discard potential errors, because the message might be localized
138+
severity_s = from_utf8(&buf[v.0 as usize..v.1 as usize])
139+
.unwrap()
140+
.try_into()
141+
.ok();
142+
}
143+
144+
b'V' => {
145+
// Propagate errors here, because V is not localized and thus we are missing a possible
146+
// variant.
147+
severity_v = Some(
148+
from_utf8(&buf[v.0 as usize..v.1 as usize])
149+
.unwrap()
150+
.try_into()?,
151+
);
123152
}
124153

125154
b'M' => {
@@ -135,7 +164,7 @@ impl Decode<'_> for Notice {
135164
}
136165

137166
Ok(Self {
138-
severity,
167+
severity: severity_v.or(severity_s).unwrap_or(DEFAULT_SEVERITY),
139168
message,
140169
code,
141170
storage: buf,

0 commit comments

Comments
 (0)