1use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7use crate::value::Value;
8
9pub const RANGE_EMPTY: u8 = 0x01;
11pub const RANGE_LB_INC: u8 = 0x02;
13pub const RANGE_UB_INC: u8 = 0x04;
15pub const RANGE_LB_INF: u8 = 0x08;
17pub const RANGE_UB_INF: u8 = 0x10;
19
20const EMPTY_LITERAL: &str = "empty";
22
23#[derive(Clone, Debug, PartialEq)]
25pub struct Range {
26 pub flags: u8,
27 pub lower: Value,
28 pub upper: Value,
29}
30
31impl Range {
32 pub fn empty() -> Range {
34 Range { flags: RANGE_EMPTY, lower: Value::Null, upper: Value::Null }
35 }
36
37 pub fn is_empty(&self) -> bool {
39 self.flags & RANGE_EMPTY != 0
40 }
41
42 pub fn has_lower(&self) -> bool {
44 self.flags & (RANGE_EMPTY | RANGE_LB_INF) == 0
45 }
46
47 pub fn has_upper(&self) -> bool {
49 self.flags & (RANGE_EMPTY | RANGE_UB_INF) == 0
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct RangeText {
56 pub flags: u8,
57 pub lower: Option<String>,
58 pub upper: Option<String>,
59}
60
61fn is_space(b: u8) -> bool {
63 matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0B | 0x0C)
64}
65
66fn malformed(text: &str, detail: &str) -> TypeError {
68 let mut error = TypeError::new(
69 SqlState::INVALID_TEXT_REPRESENTATION,
70 format!("malformed range literal: \"{text}\""),
71 );
72 error.detail = Some(detail.to_string());
73 error
74}
75
76pub fn range_parse(text: &str) -> Result<RangeText, TypeError> {
82 let s = text.as_bytes();
83 let mut at = 0;
84 while at < s.len() && is_space(s[at]) {
85 at += 1;
86 }
87 if s.len() - at >= EMPTY_LITERAL.len()
88 && s[at..at + EMPTY_LITERAL.len()].eq_ignore_ascii_case(EMPTY_LITERAL.as_bytes())
89 {
90 at += EMPTY_LITERAL.len();
91 while at < s.len() && is_space(s[at]) {
92 at += 1;
93 }
94 if at < s.len() {
95 return Err(malformed(text, "Junk after \"empty\" key word."));
96 }
97 return Ok(RangeText { flags: RANGE_EMPTY, lower: None, upper: None });
98 }
99 let mut flags = 0;
100 match s.get(at) {
101 Some(b'[') => flags |= RANGE_LB_INC,
102 Some(b'(') => {}
103 _ => return Err(malformed(text, "Missing left parenthesis or bracket.")),
104 }
105 at += 1;
106 let lower = parse_bound(text, &mut at)?;
107 if lower.is_none() {
108 flags |= RANGE_LB_INF;
109 }
110 if s.get(at) != Some(&b',') {
111 return Err(malformed(text, "Missing comma after lower bound."));
112 }
113 at += 1;
114 let upper = parse_bound(text, &mut at)?;
115 if upper.is_none() {
116 flags |= RANGE_UB_INF;
117 }
118 match s.get(at) {
119 Some(b']') => flags |= RANGE_UB_INC,
120 Some(b')') => {}
121 _ => return Err(malformed(text, "Too many commas.")),
122 }
123 at += 1;
124 while at < s.len() && is_space(s[at]) {
125 at += 1;
126 }
127 if at < s.len() {
128 return Err(malformed(text, "Junk after right parenthesis or bracket."));
129 }
130 Ok(RangeText { flags, lower, upper })
131}
132
133fn parse_bound(text: &str, at: &mut usize) -> Result<Option<String>, TypeError> {
135 let s = text.as_bytes();
136 let ends = |b: Option<&u8>| matches!(b, Some(b',' | b')' | b']'));
137 if ends(s.get(*at)) {
138 return Ok(None);
139 }
140 let mut out = Vec::new();
141 let mut quoted = false;
142 while quoted || !ends(s.get(*at)) {
143 let Some(&c) = s.get(*at) else {
144 return Err(malformed(text, "Unexpected end of input."));
145 };
146 *at += 1;
147 match c {
148 b'\\' => {
149 let Some(&next) = s.get(*at) else {
150 return Err(malformed(text, "Unexpected end of input."));
151 };
152 out.push(next);
153 *at += 1;
154 }
155 b'"' if !quoted => quoted = true,
156 b'"' if s.get(*at) == Some(&b'"') => {
157 out.push(b'"');
158 *at += 1;
159 }
160 b'"' => quoted = false,
161 _ => out.push(c),
162 }
163 }
164 Ok(Some(String::from_utf8_lossy(&out).into_owned()))
165}
166
167pub fn range_deparse(flags: u8, lower: Option<&[u8]>, upper: Option<&[u8]>, out: &mut Vec<u8>) {
169 if flags & RANGE_EMPTY != 0 {
170 out.extend_from_slice(EMPTY_LITERAL.as_bytes());
171 return;
172 }
173 out.push(if flags & RANGE_LB_INC != 0 { b'[' } else { b'(' });
174 if let Some(lower) = lower {
175 bound_escape(lower, out);
176 }
177 out.push(b',');
178 if let Some(upper) = upper {
179 bound_escape(upper, out);
180 }
181 out.push(if flags & RANGE_UB_INC != 0 { b']' } else { b')' });
182}
183
184fn bound_escape(value: &[u8], out: &mut Vec<u8>) {
186 let quote = value.is_empty()
187 || value
188 .iter()
189 .any(|&b| matches!(b, b'"' | b'\\' | b'(' | b')' | b'[' | b']' | b',') || is_space(b));
190 if !quote {
191 out.extend_from_slice(value);
192 return;
193 }
194 out.push(b'"');
195 for &b in value {
196 if matches!(b, b'"' | b'\\') {
197 out.push(b);
198 }
199 out.push(b);
200 }
201 out.push(b'"');
202}
203
204pub type RangeBytes<'a> = (u8, Option<&'a [u8]>, Option<&'a [u8]>);
206
207pub fn range_recv<'a>(recv: &mut Recv<'a>) -> Result<RangeBytes<'a>, TypeError> {
213 let flags =
214 recv.byte()? & (RANGE_EMPTY | RANGE_LB_INC | RANGE_LB_INF | RANGE_UB_INC | RANGE_UB_INF);
215 let mut bound = |inf: u8| -> Result<Option<&'a [u8]>, TypeError> {
216 if flags & (RANGE_EMPTY | inf) != 0 {
217 return Ok(None);
218 }
219 let len = recv.u32()?;
220 recv.bytes(usize::try_from(len).unwrap_or(usize::MAX)).map(Some)
221 };
222 let lower = bound(RANGE_LB_INF)?;
223 let upper = bound(RANGE_UB_INF)?;
224 if recv.remaining() != 0 {
225 return Err(TypeError::new(
226 SqlState::PROTOCOL_VIOLATION,
227 "invalid message format".to_string(),
228 ));
229 }
230 Ok((flags, lower, upper))
231}
232
233pub fn range_send(flags: u8, lower: Option<&[u8]>, upper: Option<&[u8]>, out: &mut Vec<u8>) {
235 out.push(flags);
236 for bound in [lower, upper].into_iter().flatten() {
237 let len = u32::try_from(bound.len()).unwrap_or(u32::MAX);
238 out.extend_from_slice(&len.to_be_bytes());
239 out.extend_from_slice(bound);
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 fn parse(text: &str) -> (u8, Option<String>, Option<String>) {
248 let r = range_parse(text).unwrap();
249 (r.flags, r.lower, r.upper)
250 }
251
252 fn detail(text: &str) -> String {
253 range_parse(text).unwrap_err().detail.unwrap()
254 }
255
256 #[test]
257 fn text_forms() {
258 assert_eq!(parse("[1,10)"), (RANGE_LB_INC, Some("1".into()), Some("10".into())));
259 assert_eq!(parse(" (,] "), (RANGE_LB_INF | RANGE_UB_INF | RANGE_UB_INC, None, None));
260 assert_eq!(parse(" EMPTY "), (RANGE_EMPTY, None, None));
261 assert_eq!(parse(r#"["a ""b"" \c",x\,y]"#).1.as_deref(), Some(r#"a "b" c"#));
262 assert_eq!(parse(r#"["a ""b"" \c",x\,y]"#).2.as_deref(), Some("x,y"));
263 assert_eq!(parse(r#"["",)"#).1.as_deref(), Some(""));
264 assert_eq!(detail("emptyx"), "Junk after \"empty\" key word.");
265 assert_eq!(detail(""), "Missing left parenthesis or bracket.");
266 assert_eq!(detail("[1 10)"), "Missing comma after lower bound.");
267 assert_eq!(detail("[1,10"), "Unexpected end of input.");
268 assert_eq!(detail("[1,10,11)"), "Too many commas.");
269 assert_eq!(detail("[1,10)x"), "Junk after right parenthesis or bracket.");
270 assert_eq!(detail("[1)"), "Missing comma after lower bound.");
271 assert_eq!(detail("[1,2\\"), "Unexpected end of input.");
272 let mut out = Vec::new();
273 range_deparse(RANGE_LB_INC, Some(b"a b"), Some(b"x\"y"), &mut out);
274 assert_eq!(out, br#"["a b","x""y")"#);
275 out.clear();
276 range_deparse(RANGE_LB_INF | RANGE_UB_INC, None, Some(b""), &mut out);
277 assert_eq!(out, br#"(,""]"#);
278 }
279
280 #[test]
281 fn binary_forms() {
282 let mut out = Vec::new();
283 range_send(RANGE_LB_INC, Some(&[0, 0, 0, 1]), Some(&[0, 0, 0, 10]), &mut out);
284 assert_eq!(out, [2, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 10]);
285 let mut recv = Recv::new(&out);
286 let (flags, lower, upper) = range_recv(&mut recv).unwrap();
287 assert_eq!(
288 (flags, lower, upper),
289 (RANGE_LB_INC, Some(&[0, 0, 0, 1][..]), Some(&[0, 0, 0, 10][..]))
290 );
291 let mut recv = Recv::new(&[0x81]);
292 assert_eq!(range_recv(&mut recv).unwrap(), (RANGE_EMPTY, None, None));
293 let mut recv = Recv::new(&[RANGE_EMPTY, 0]);
294 assert_eq!(range_recv(&mut recv).unwrap_err().message, "invalid message format");
295 }
296}