Skip to main content

mz_repr/
strconv.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Routines for converting datum values to and from their string
11//! representation.
12//!
13//! The functions in this module are tightly related to the variants of
14//! [`SqlScalarType`](crate::SqlScalarType). Each variant has a pair of functions in
15//! this module named `parse_VARIANT` and `format_VARIANT`. The type returned
16//! by `parse` functions, and the type accepted by `format` functions, will
17//! be a type that is easily converted into the [`Datum`](crate::Datum) variant
18//! for that type. The functions do not directly convert from `Datum`s to
19//! `String`s so that the logic can be reused when `Datum`s are not available or
20//! desired, as in the pgrepr crate.
21//!
22//! The string representations used are exactly the same as the PostgreSQL
23//! string representations for the corresponding PostgreSQL type. Deviations
24//! should be considered a bug.
25
26use std::borrow::Cow;
27use std::collections::BTreeMap;
28use std::error::Error;
29use std::fmt;
30use std::num::FpCategory;
31use std::str::FromStr;
32use std::sync::LazyLock;
33
34use chrono::offset::{Offset, TimeZone};
35use chrono::{DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
36use dec::OrderedDecimal;
37use mz_lowertest::MzReflect;
38use mz_ore::cast::ReinterpretCast;
39use mz_ore::error::ErrorExt;
40use mz_ore::fmt::FormatBuffer;
41use mz_ore::lex::LexBuf;
42use mz_ore::str::StrExt;
43use mz_pgtz::timezone::{Timezone, TimezoneSpec};
44use mz_proto::{ProtoType, RustType, TryFromProtoError};
45use num_traits::Float as NumFloat;
46#[cfg(any(test, feature = "proptest"))]
47use proptest_derive::Arbitrary;
48use regex::bytes::Regex;
49use ryu::Float as RyuFloat;
50use serde::{Deserialize, Serialize};
51use uuid::Uuid;
52
53use crate::adt::array::ArrayDimension;
54use crate::adt::date::Date;
55use crate::adt::datetime::{self, DateTimeField, ParsedDateTime};
56use crate::adt::interval::Interval;
57use crate::adt::jsonb::{Jsonb, JsonbRef};
58use crate::adt::mz_acl_item::{AclItem, MzAclItem};
59use crate::adt::numeric::{self, NUMERIC_DATUM_MAX_PRECISION, Numeric};
60use crate::adt::pg_legacy_name::NAME_MAX_BYTES;
61use crate::adt::range::{Range, RangeBound, RangeInner};
62use crate::adt::timestamp::CheckedTimestamp;
63
64include!(concat!(env!("OUT_DIR"), "/mz_repr.strconv.rs"));
65
66macro_rules! bail {
67    ($($arg:tt)*) => { return Err(format!($($arg)*)) };
68}
69
70/// Yes should be provided for types that will *never* return true for [`ElementEscaper::needs_escaping`]
71#[derive(Debug)]
72pub enum Nestable {
73    Yes,
74    MayNeedEscaping,
75}
76
77/// Parses a [`bool`] from `s`.
78///
79/// The accepted values are "true", "false", "yes", "no", "on", "off", "1", and
80/// "0", or any unambiguous prefix of one of those values. Leading or trailing
81/// whitespace is permissible.
82pub fn parse_bool(s: &str) -> Result<bool, ParseError> {
83    match s.trim().to_lowercase().as_str() {
84        "t" | "tr" | "tru" | "true" | "y" | "ye" | "yes" | "on" | "1" => Ok(true),
85        "f" | "fa" | "fal" | "fals" | "false" | "n" | "no" | "of" | "off" | "0" => Ok(false),
86        _ => Err(ParseError::invalid_input_syntax("boolean", s)),
87    }
88}
89
90/// Like `format_bool`, but returns a string with a static lifetime.
91///
92/// This function should be preferred to `format_bool` when applicable, as it
93/// avoids an allocation.
94pub fn format_bool_static(b: bool) -> &'static str {
95    match b {
96        true => "t",
97        false => "f",
98    }
99}
100
101/// Writes a boolean value into `buf`.
102///
103/// `true` is encoded as the char `'t'` and `false` is encoded as the char
104/// `'f'`.
105pub fn format_bool<F>(buf: &mut F, b: bool) -> Nestable
106where
107    F: FormatBuffer,
108{
109    buf.write_str(format_bool_static(b));
110    Nestable::Yes
111}
112
113/// Parses an [`i16`] from `s`.
114///
115/// Valid values are whatever the [`std::str::FromStr`] implementation on `i16` accepts,
116/// plus leading and trailing whitespace.
117pub fn parse_int16(s: &str) -> Result<i16, ParseError> {
118    s.trim()
119        .parse()
120        .map_err(|e| ParseError::invalid_input_syntax("smallint", s).with_details(e))
121}
122
123/// Writes an [`i16`] to `buf`.
124pub fn format_int16<F>(buf: &mut F, i: i16) -> Nestable
125where
126    F: FormatBuffer,
127{
128    write!(buf, "{}", i);
129    Nestable::Yes
130}
131
132/// Parses an [`i32`] from `s`.
133///
134/// Valid values are whatever the [`std::str::FromStr`] implementation on `i32` accepts,
135/// plus leading and trailing whitespace.
136pub fn parse_int32(s: &str) -> Result<i32, ParseError> {
137    s.trim()
138        .parse()
139        .map_err(|e| ParseError::invalid_input_syntax("integer", s).with_details(e))
140}
141
142/// Writes an [`i32`] to `buf`.
143pub fn format_int32<F>(buf: &mut F, i: i32) -> Nestable
144where
145    F: FormatBuffer,
146{
147    write!(buf, "{}", i);
148    Nestable::Yes
149}
150
151/// Parses an `i64` from `s`.
152pub fn parse_int64(s: &str) -> Result<i64, ParseError> {
153    s.trim()
154        .parse()
155        .map_err(|e| ParseError::invalid_input_syntax("bigint", s).with_details(e))
156}
157
158/// Writes an `i64` to `buf`.
159pub fn format_int64<F>(buf: &mut F, i: i64) -> Nestable
160where
161    F: FormatBuffer,
162{
163    write!(buf, "{}", i);
164    Nestable::Yes
165}
166
167/// Parses an [`u16`] from `s`.
168///
169/// Valid values are whatever the [`std::str::FromStr`] implementation on `u16` accepts,
170/// plus leading and trailing whitespace.
171pub fn parse_uint16(s: &str) -> Result<u16, ParseError> {
172    s.trim()
173        .parse()
174        .map_err(|e| ParseError::invalid_input_syntax("uint2", s).with_details(e))
175}
176
177/// Writes an `u16` to `buf`.
178pub fn format_uint16<F>(buf: &mut F, u: u16) -> Nestable
179where
180    F: FormatBuffer,
181{
182    write!(buf, "{}", u);
183    Nestable::Yes
184}
185
186/// Parses an [`u32`] from `s`.
187///
188/// Valid values are whatever the [`std::str::FromStr`] implementation on `u32` accepts,
189/// plus leading and trailing whitespace.
190pub fn parse_uint32(s: &str) -> Result<u32, ParseError> {
191    s.trim()
192        .parse()
193        .map_err(|e| ParseError::invalid_input_syntax("uint4", s).with_details(e))
194}
195
196/// Writes an `u32` to `buf`.
197pub fn format_uint32<F>(buf: &mut F, u: u32) -> Nestable
198where
199    F: FormatBuffer,
200{
201    write!(buf, "{}", u);
202    Nestable::Yes
203}
204
205/// Parses an `u64` from `s`.
206pub fn parse_uint64(s: &str) -> Result<u64, ParseError> {
207    s.trim()
208        .parse()
209        .map_err(|e| ParseError::invalid_input_syntax("uint8", s).with_details(e))
210}
211
212/// Writes an `u64` to `buf`.
213pub fn format_uint64<F>(buf: &mut F, u: u64) -> Nestable
214where
215    F: FormatBuffer,
216{
217    write!(buf, "{}", u);
218    Nestable::Yes
219}
220
221/// Parses an `mz_timestamp` from `s`.
222pub fn parse_mz_timestamp(s: &str) -> Result<crate::Timestamp, ParseError> {
223    s.trim()
224        .parse()
225        .map_err(|e| ParseError::invalid_input_syntax("mz_timestamp", s).with_details(e))
226}
227
228/// Writes an `mz_timestamp` to `buf`.
229pub fn format_mz_timestamp<F>(buf: &mut F, u: crate::Timestamp) -> Nestable
230where
231    F: FormatBuffer,
232{
233    write!(buf, "{}", u);
234    Nestable::Yes
235}
236
237/// Parses an OID from `s`.
238pub fn parse_oid(s: &str) -> Result<u32, ParseError> {
239    // For historical reasons, PostgreSQL accepts OID inputs whose value fits in
240    // the range of either `u32` or `i32`. The full `u32` range is accepted
241    // directly, while values given with a minus sign are parsed as `i32` and
242    // reinterpreted as `u32` (e.g. `-1` becomes `4294967295`). Anything outside
243    // both ranges is rejected.
244    //
245    // Do not use this as a model for behavior in other contexts. OIDs should
246    // not in general be thought of as freely convertible from `i32`s.
247    let trimmed = s.trim();
248    if let Ok(oid) = trimmed.parse::<u32>() {
249        return Ok(oid);
250    }
251    let oid: i32 = trimmed
252        .parse()
253        .map_err(|e| ParseError::invalid_input_syntax("oid", s).with_details(e))?;
254    Ok(u32::reinterpret_cast(oid))
255}
256
257/// Parses an OID from `s`, accepting only the `i32` range.
258///
259/// This is the historical [`parse_oid`] behavior: values are parsed as `i32`
260/// and reinterpreted as `u32`, so text in `2147483648..=4294967295` is
261/// rejected even though it denotes a valid OID.
262///
263/// NOTE: This exists solely to keep the persisted PostgreSQL source cast
264/// `CastStringToOid` evaluation-stable across releases (see the stability
265/// contract in `mz_storage_types::sources::casts`). PostgreSQL replication
266/// re-casts the old tuple on delete, so widening this cast would let a value
267/// ingested pre-upgrade as an error be retracted post-upgrade as a value,
268/// leaving the error stuck. Use [`parse_oid`] everywhere else.
269pub fn parse_oid_legacy(s: &str) -> Result<u32, ParseError> {
270    let oid: i32 = s
271        .trim()
272        .parse()
273        .map_err(|e| ParseError::invalid_input_syntax("oid", s).with_details(e))?;
274    Ok(u32::reinterpret_cast(oid))
275}
276
277fn parse_float<Fl>(type_name: &'static str, s: &str) -> Result<Fl, ParseError>
278where
279    Fl: NumFloat + FromStr,
280{
281    // Matching PostgreSQL's float parsing behavior is tricky. PostgreSQL's
282    // implementation delegates almost entirely to strtof(3)/strtod(3), which
283    // will report an out-of-range error if a number was rounded to zero or
284    // infinity. For example, parsing "1e70" as a 32-bit float will yield an
285    // out-of-range error because it is rounded to infinity, but parsing an
286    // explicitly-specified "inf" will yield infinity without an error.
287    //
288    // To @benesch's knowledge, there is no Rust implementation of float parsing
289    // that reports whether underflow or overflow occurred. So we figure it out
290    // ourselves after the fact. If parsing the float returns infinity and the input
291    // was not an explicitly-specified infinity, then we know overflow occurred.
292    // If parsing the float returns zero and the input was not an explicitly-specified
293    // zero, then we know underflow occurred.
294
295    // Matches `0`, `-0`, `+0`, `000000.00000`, `0.0e10`, 0., .0, et al.
296    static ZERO_RE: LazyLock<Regex> =
297        LazyLock::new(|| Regex::new(r#"(?i-u)^[-+]?(0+(\.0*)?|\.0+)(e|$)"#).unwrap());
298    // Matches `inf`, `-inf`, `+inf`, `infinity`, et al.
299    static INF_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new("(?i-u)^[-+]?inf").unwrap());
300
301    let buf = s.trim();
302    let f: Fl = buf
303        .parse()
304        .map_err(|_| ParseError::invalid_input_syntax(type_name, s))?;
305    match f.classify() {
306        FpCategory::Infinite if !INF_RE.is_match(buf.as_bytes()) => {
307            Err(ParseError::out_of_range(type_name, s))
308        }
309        FpCategory::Zero if !ZERO_RE.is_match(buf.as_bytes()) => {
310            Err(ParseError::out_of_range(type_name, s))
311        }
312        _ => Ok(f),
313    }
314}
315
316fn format_float<F, Fl>(buf: &mut F, f: Fl) -> Nestable
317where
318    F: FormatBuffer,
319    Fl: NumFloat + RyuFloat,
320{
321    // Use ryu rather than the standard library. ryu uses scientific notation
322    // when possible, which better matches PostgreSQL. The standard library's
323    // `ToString` implementations print all available digits, which is rather
324    // verbose.
325    //
326    // Note that we have to fix up ryu's formatting in a few cases to match
327    // PostgreSQL. PostgreSQL spells out "Infinity" in full, never emits a
328    // trailing ".0", formats positive exponents as e.g. "1e+10" rather than
329    // "1e10", and emits a negative sign for negative zero. If we need to speed
330    // up float formatting, we can look into forking ryu and making these edits
331    // directly, but for now it doesn't seem worth it.
332
333    match f.classify() {
334        FpCategory::Infinite if f.is_sign_negative() => buf.write_str("-Infinity"),
335        FpCategory::Infinite => buf.write_str("Infinity"),
336        FpCategory::Nan => buf.write_str("NaN"),
337        FpCategory::Zero if f.is_sign_negative() => buf.write_str("-0"),
338        _ => {
339            debug_assert!(f.is_finite());
340            let mut ryu_buf = ryu::Buffer::new();
341            let mut s = ryu_buf.format_finite(f);
342            if let Some(trimmed) = s.strip_suffix(".0") {
343                s = trimmed;
344            }
345            let mut chars = s.chars().peekable();
346            while let Some(ch) = chars.next() {
347                buf.write_char(ch);
348                if ch == 'e' && chars.peek() != Some(&'-') {
349                    buf.write_char('+');
350                }
351            }
352        }
353    }
354
355    Nestable::Yes
356}
357
358/// Parses an `f32` from `s`.
359pub fn parse_float32(s: &str) -> Result<f32, ParseError> {
360    parse_float("real", s)
361}
362
363/// Writes an `f32` to `buf`.
364pub fn format_float32<F>(buf: &mut F, f: f32) -> Nestable
365where
366    F: FormatBuffer,
367{
368    format_float(buf, f)
369}
370
371/// Parses an `f64` from `s`.
372pub fn parse_float64(s: &str) -> Result<f64, ParseError> {
373    parse_float("double precision", s)
374}
375
376/// Writes an `f64` to `buf`.
377pub fn format_float64<F>(buf: &mut F, f: f64) -> Nestable
378where
379    F: FormatBuffer,
380{
381    format_float(buf, f)
382}
383
384/// Use the following grammar to parse `s` into:
385///
386/// - `NaiveDate`
387/// - `NaiveTime`
388/// - Timezone string
389///
390/// `NaiveDate` and `NaiveTime` are appropriate to compute a `NaiveDateTime`,
391/// which can be used in conjunction with a timezone string to generate a
392/// `DateTime<Utc>`.
393///
394/// ```text
395/// <unquoted timestamp string> ::=
396///     <date value> <space> <time value> [ <time zone interval> ]
397/// <date value> ::=
398///     <years value> <minus sign> <months value> <minus sign> <days value>
399/// <time zone interval> ::=
400///     <sign> <hours value> <colon> <minutes value>
401/// ```
402fn parse_timestamp_string(s: &str) -> Result<(NaiveDate, NaiveTime, Timezone), String> {
403    if s.is_empty() {
404        return Err("timestamp string is empty".into());
405    }
406
407    // PostgreSQL special date-time inputs
408    // https://www.postgresql.org/docs/12/datatype-datetime.html#id-1.5.7.13.18.8
409    // We should add support for other values here, e.g. infinity
410    // which @quodlibetor is willing to add to the chrono package.
411    if s == "epoch" {
412        return Ok((
413            NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
414            NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
415            Default::default(),
416        ));
417    }
418
419    let (ts_string, tz_string, era) = datetime::split_timestamp_string(s);
420
421    let pdt = ParsedDateTime::build_parsed_datetime_timestamp(ts_string, era)?;
422    let d: NaiveDate = pdt.compute_date()?;
423    let t: NaiveTime = pdt.compute_time()?;
424
425    let offset = if tz_string.is_empty() {
426        Default::default()
427    } else {
428        Timezone::parse(tz_string, TimezoneSpec::Iso)?
429    };
430
431    Ok((d, t, offset))
432}
433
434/// Parses a [`Date`] from `s`.
435pub fn parse_date(s: &str) -> Result<Date, ParseError> {
436    match parse_timestamp_string(s) {
437        Ok((date, _, _)) => Date::try_from(date).map_err(|_| ParseError::out_of_range("date", s)),
438        Err(e) => Err(ParseError::invalid_input_syntax("date", s).with_details(e)),
439    }
440}
441
442/// Writes a [`Date`] to `buf`.
443pub fn format_date<F>(buf: &mut F, d: Date) -> Nestable
444where
445    F: FormatBuffer,
446{
447    let d: NaiveDate = d.into();
448    let (year_ad, year) = d.year_ce();
449    write!(buf, "{:04}-{}", year, d.format("%m-%d"));
450    if !year_ad {
451        write!(buf, " BC");
452    }
453    Nestable::Yes
454}
455
456/// Parses a `NaiveTime` from `s`, using the following grammar.
457///
458/// ```text
459/// <time value> ::=
460///     <hours value> <colon> <minutes value> <colon> <seconds integer value>
461///     [ <period> [ <seconds fraction> ] ]
462/// ```
463pub fn parse_time(s: &str) -> Result<NaiveTime, ParseError> {
464    ParsedDateTime::build_parsed_datetime_time(s)
465        .and_then(|pdt| pdt.compute_time())
466        .map_err(|e| ParseError::invalid_input_syntax("time", s).with_details(e))
467}
468
469/// Writes a [`NaiveDateTime`] timestamp to `buf`.
470pub fn format_time<F>(buf: &mut F, t: NaiveTime) -> Nestable
471where
472    F: FormatBuffer,
473{
474    write!(buf, "{}", t.format("%H:%M:%S"));
475    format_nanos_to_micros(buf, t.nanosecond());
476    Nestable::Yes
477}
478
479/// Parses a `NaiveDateTime` from `s`.
480pub fn parse_timestamp(s: &str) -> Result<CheckedTimestamp<NaiveDateTime>, ParseError> {
481    match parse_timestamp_string(s) {
482        Ok((date, time, _)) => CheckedTimestamp::from_timestamplike(date.and_time(time))
483            .map_err(|_| ParseError::out_of_range("timestamp", s)),
484        Err(e) => Err(ParseError::invalid_input_syntax("timestamp", s).with_details(e)),
485    }
486}
487
488/// Writes a [`NaiveDateTime`] timestamp to `buf`.
489pub fn format_timestamp<F>(buf: &mut F, ts: &NaiveDateTime) -> Nestable
490where
491    F: FormatBuffer,
492{
493    let (year_ad, year) = ts.year_ce();
494    write!(buf, "{:04}-{}", year, ts.format("%m-%d %H:%M:%S"));
495    format_nanos_to_micros(buf, ts.and_utc().timestamp_subsec_nanos());
496    if !year_ad {
497        write!(buf, " BC");
498    }
499    // This always needs escaping because of the whitespace
500    Nestable::MayNeedEscaping
501}
502
503/// Parses a `DateTime<Utc>` from `s`. See `mz_expr::scalar::func::timezone_timestamp` for timezone anomaly considerations.
504pub fn parse_timestamptz(s: &str) -> Result<CheckedTimestamp<DateTime<Utc>>, ParseError> {
505    parse_timestamp_string(s)
506        .and_then(|(date, time, timezone)| {
507            use Timezone::*;
508            let mut dt = date.and_time(time);
509            let offset = match timezone {
510                FixedOffset(offset) => offset,
511                Tz(tz) => match tz.offset_from_local_datetime(&dt).latest() {
512                    Some(offset) => offset.fix(),
513                    None => {
514                        dt += Duration::try_hours(1).unwrap();
515                        tz.offset_from_local_datetime(&dt)
516                            .latest()
517                            .ok_or_else(|| "invalid timezone conversion".to_owned())?
518                            .fix()
519                    }
520                },
521            };
522            Ok(DateTime::from_naive_utc_and_offset(dt - offset, Utc))
523        })
524        .map_err(|e| {
525            ParseError::invalid_input_syntax("timestamp with time zone", s).with_details(e)
526        })
527        .and_then(|ts| {
528            CheckedTimestamp::from_timestamplike(ts)
529                .map_err(|_| ParseError::out_of_range("timestamp with time zone", s))
530        })
531}
532
533/// Writes a [`DateTime<Utc>`] timestamp to `buf`.
534pub fn format_timestamptz<F>(buf: &mut F, ts: &DateTime<Utc>) -> Nestable
535where
536    F: FormatBuffer,
537{
538    let (year_ad, year) = ts.year_ce();
539    write!(buf, "{:04}-{}", year, ts.format("%m-%d %H:%M:%S"));
540    format_nanos_to_micros(buf, ts.timestamp_subsec_nanos());
541    write!(buf, "+00");
542    if !year_ad {
543        write!(buf, " BC");
544    }
545    // This always needs escaping because of the whitespace
546    Nestable::MayNeedEscaping
547}
548
549/// parse
550///
551/// ```text
552/// <unquoted interval string> ::=
553///   [ <sign> ] { <year-month literal> | <day-time literal> }
554/// <year-month literal> ::=
555///     <years value> [ <minus sign> <months value> ]
556///   | <months value>
557/// <day-time literal> ::=
558///     <day-time interval>
559///   | <time interval>
560/// <day-time interval> ::=
561///   <days value> [ <space> <hours value> [ <colon> <minutes value>
562///       [ <colon> <seconds value> ] ] ]
563/// <time interval> ::=
564///     <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
565///   | <minutes value> [ <colon> <seconds value> ]
566///   | <seconds value>
567/// ```
568pub fn parse_interval(s: &str) -> Result<Interval, ParseError> {
569    parse_interval_w_disambiguator(s, None, DateTimeField::Second)
570}
571
572/// Parse an interval string, using an optional leading precision for time (H:M:S)
573/// and a specific mz_sql_parser::ast::DateTimeField to identify ambiguous elements.
574/// For more information about this operation, see the documentation on
575/// ParsedDateTime::build_parsed_datetime_interval.
576pub fn parse_interval_w_disambiguator(
577    s: &str,
578    leading_time_precision: Option<DateTimeField>,
579    d: DateTimeField,
580) -> Result<Interval, ParseError> {
581    ParsedDateTime::build_parsed_datetime_interval(s, leading_time_precision, d)
582        .and_then(|pdt| pdt.compute_interval())
583        .map_err(|e| ParseError::invalid_input_syntax("interval", s).with_details(e))
584}
585
586pub fn format_interval<F>(buf: &mut F, iv: Interval) -> Nestable
587where
588    F: FormatBuffer,
589{
590    write!(buf, "{}", iv);
591    Nestable::MayNeedEscaping
592}
593
594pub fn parse_numeric(s: &str) -> Result<OrderedDecimal<Numeric>, ParseError> {
595    let mut cx = numeric::cx_datum();
596    let mut n = match cx.parse(s.trim()) {
597        Ok(n) => n,
598        Err(..) => {
599            return Err(ParseError::invalid_input_syntax("numeric", s));
600        }
601    };
602
603    let cx_status = cx.status();
604
605    // Check for values that can only be generated by invalid syntax.
606    if (n.is_infinite() && !cx_status.overflow())
607        || (n.is_nan() && n.is_negative())
608        || n.is_signaling_nan()
609    {
610        return Err(ParseError::invalid_input_syntax("numeric", s));
611    }
612
613    // Process value; only errors if value is out of range of numeric's max precision.
614    let out_of_range = numeric::munge_numeric(&mut n).is_err();
615
616    if cx_status.overflow() || cx_status.subnormal() || out_of_range {
617        Err(ParseError::out_of_range("numeric", s).with_details(format!(
618            "exceeds maximum precision {}",
619            NUMERIC_DATUM_MAX_PRECISION
620        )))
621    } else {
622        Ok(OrderedDecimal(n))
623    }
624}
625
626pub fn format_numeric<F>(buf: &mut F, n: &OrderedDecimal<Numeric>) -> Nestable
627where
628    F: FormatBuffer,
629{
630    write!(buf, "{}", n.0.to_standard_notation_string());
631    Nestable::Yes
632}
633
634pub fn format_string<F>(buf: &mut F, s: &str) -> Nestable
635where
636    F: FormatBuffer,
637{
638    buf.write_str(s);
639    Nestable::MayNeedEscaping
640}
641
642pub fn parse_pg_legacy_name(s: &str) -> String {
643    // To match PostgreSQL, we truncate the string to 64 bytes, while being
644    // careful not to truncate in the middle of any multibyte characters.
645    let mut out = String::new();
646    let mut len = 0;
647    for c in s.chars() {
648        len += c.len_utf8();
649        if len > NAME_MAX_BYTES {
650            break;
651        }
652        out.push(c);
653    }
654    out
655}
656
657pub fn parse_bytes(s: &str) -> Result<Vec<u8>, ParseError> {
658    // If the input starts with "\x", then the remaining bytes are hex encoded
659    // [0]. Otherwise the bytes use the traditional "escape" format. [1]
660    //
661    // [0]: https://www.postgresql.org/docs/current/datatype-binary.html#id-1.5.7.12.9
662    // [1]: https://www.postgresql.org/docs/current/datatype-binary.html#id-1.5.7.12.10
663    if let Some(remainder) = s.strip_prefix(r"\x") {
664        parse_bytes_hex(remainder).map_err(|e| {
665            ParseError::invalid_input_syntax("bytea", s).with_details(e.to_string_with_causes())
666        })
667    } else {
668        parse_bytes_traditional(s)
669    }
670}
671
672pub fn parse_bytes_hex(s: &str) -> Result<Vec<u8>, ParseHexError> {
673    // Can't use `hex::decode` here, as it doesn't tolerate whitespace
674    // between encoded bytes.
675
676    let decode_nibble = |b| match b {
677        b'a'..=b'f' => Ok(b - b'a' + 10),
678        b'A'..=b'F' => Ok(b - b'A' + 10),
679        b'0'..=b'9' => Ok(b - b'0'),
680        _ => Err(ParseHexError::InvalidHexDigit(char::from(b))),
681    };
682
683    let mut buf = vec![];
684    let mut nibbles = s.as_bytes().iter().copied();
685    while let Some(n) = nibbles.next() {
686        if let b' ' | b'\n' | b'\t' | b'\r' = n {
687            continue;
688        }
689        let n = decode_nibble(n)?;
690        let n2 = match nibbles.next() {
691            None => return Err(ParseHexError::OddLength),
692            Some(n2) => decode_nibble(n2)?,
693        };
694        buf.push((n << 4) | n2);
695    }
696    Ok(buf)
697}
698
699pub fn parse_bytes_traditional(s: &str) -> Result<Vec<u8>, ParseError> {
700    // Bytes are interpreted literally, save for the special escape sequences
701    // "\\", which represents a single backslash, and "\NNN", where each N
702    // is an octal digit, which represents the byte whose octal value is NNN.
703    let mut out = Vec::with_capacity(s.len());
704    let mut bytes = s.as_bytes().iter().fuse();
705    while let Some(&b) = bytes.next() {
706        if b != b'\\' {
707            out.push(b);
708            continue;
709        }
710        match bytes.next() {
711            None => {
712                return Err(ParseError::invalid_input_syntax("bytea", s)
713                    .with_details("ends with escape character"));
714            }
715            Some(b'\\') => out.push(b'\\'),
716            b => match (b, bytes.next(), bytes.next()) {
717                (Some(d2 @ b'0'..=b'3'), Some(d1 @ b'0'..=b'7'), Some(d0 @ b'0'..=b'7')) => {
718                    out.push(((d2 - b'0') << 6) + ((d1 - b'0') << 3) + (d0 - b'0'));
719                }
720                _ => {
721                    return Err(ParseError::invalid_input_syntax("bytea", s)
722                        .with_details("invalid escape sequence"));
723                }
724            },
725        }
726    }
727    Ok(out)
728}
729
730pub fn format_bytes<F>(buf: &mut F, bytes: &[u8]) -> Nestable
731where
732    F: FormatBuffer,
733{
734    write!(buf, "\\x{}", hex::encode(bytes));
735    Nestable::MayNeedEscaping
736}
737
738pub fn parse_jsonb(s: &str) -> Result<Jsonb, ParseError> {
739    s.trim()
740        .parse()
741        .map_err(|e| ParseError::invalid_input_syntax("jsonb", s).with_details(e))
742}
743
744pub fn format_jsonb<F>(buf: &mut F, jsonb: JsonbRef) -> Nestable
745where
746    F: FormatBuffer,
747{
748    write!(buf, "{}", jsonb);
749    Nestable::MayNeedEscaping
750}
751
752pub fn format_jsonb_pretty<F>(buf: &mut F, jsonb: JsonbRef)
753where
754    F: FormatBuffer,
755{
756    write!(buf, "{:#}", jsonb)
757}
758
759pub fn parse_uuid(s: &str) -> Result<Uuid, ParseError> {
760    s.trim()
761        .parse()
762        .map_err(|e| ParseError::invalid_input_syntax("uuid", s).with_details(e))
763}
764
765pub fn format_uuid<F>(buf: &mut F, uuid: Uuid) -> Nestable
766where
767    F: FormatBuffer,
768{
769    write!(buf, "{}", uuid);
770    Nestable::Yes
771}
772
773fn format_nanos_to_micros<F>(buf: &mut F, nanos: u32)
774where
775    F: FormatBuffer,
776{
777    if nanos >= 500 {
778        let mut micros = nanos / 1000;
779        let rem = nanos % 1000;
780        if rem >= 500 {
781            micros += 1;
782        }
783        // strip trailing zeros
784        let mut width = 6;
785        while micros % 10 == 0 {
786            width -= 1;
787            micros /= 10;
788        }
789        write!(buf, ".{:0width$}", micros, width = width);
790    }
791}
792
793#[derive(Debug, thiserror::Error)]
794enum ArrayParsingError {
795    #[error("Array value must start with \"{{\"")]
796    OpeningBraceMissing,
797    #[error("Specifying array lower bounds is not supported")]
798    DimsUnsupported,
799    #[error("{0}")]
800    Generic(String),
801    #[error("Unexpected \"{0}\" character.")]
802    UnexpectedChar(char),
803    #[error("Multidimensional arrays must have sub-arrays with matching dimensions.")]
804    NonRectilinearDims,
805    #[error("Unexpected array element.")]
806    UnexpectedElement,
807    #[error("Junk after closing right brace.")]
808    Junk,
809    #[error("Unexpected end of input.")]
810    EarlyTerm,
811}
812
813impl From<String> for ArrayParsingError {
814    fn from(value: String) -> Self {
815        ArrayParsingError::Generic(value)
816    }
817}
818
819pub fn parse_array<'a, T, E>(
820    s: &'a str,
821    make_null: impl FnMut() -> T,
822    gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
823) -> Result<(Vec<T>, Vec<ArrayDimension>), ParseError>
824where
825    E: ToString,
826{
827    parse_array_inner(s, make_null, gen_elem)
828        .map_err(|details| ParseError::invalid_input_syntax("array", s).with_details(details))
829}
830
831fn parse_array_inner<'a, T, E>(
832    s: &'a str,
833    mut make_null: impl FnMut() -> T,
834    mut gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
835) -> Result<(Vec<T>, Vec<ArrayDimension>), ArrayParsingError>
836where
837    E: ToString,
838{
839    use ArrayParsingError::*;
840
841    #[derive(Clone, Debug, Default)]
842    struct Dimension {
843        // If None, still discovering this dimension's permitted width;
844        // otherwise only permits `length` elements per dimension.
845        length: Option<usize>,
846        // Whether this dimension has a staged element that can be committed.
847        // This prevents us from accepting "empty" elements, e.g. `{1,}` or
848        // `{1,,2}`.
849        staged_element: bool,
850        // The total number of elements committed in this dimension since it was
851        // last entered. Zeroed out when exited.
852        committed_element_count: usize,
853    }
854
855    #[derive(Clone, Debug, Default)]
856    struct ArrayBuilder<'a> {
857        // The current character we're operating from.
858        current_command_char: char,
859        // The dimension information, which will get turned into
860        // `ArrayDimensions`.
861        dimensions: Vec<Dimension>,
862        // THe current dimension we're operating on.
863        current_dim: usize,
864        // Whether or not this array may be modified any further.
865        sealed: bool,
866        // The elements extracted from the input str. This is on the array
867        // builder to necessitate using `insert_element` so we understand when
868        // elements are staged.
869        elements: Vec<Option<Cow<'a, str>>>,
870    }
871
872    impl<'a> ArrayBuilder<'a> {
873        fn build(
874            s: &'a str,
875        ) -> Result<(Vec<Option<Cow<'a, str>>>, Vec<ArrayDimension>), ArrayParsingError> {
876            let buf = &mut LexBuf::new(s);
877
878            // TODO: support parsing array dimensions
879            if buf.consume('[') {
880                Err(DimsUnsupported)?;
881            }
882
883            buf.take_while(|ch| ch.is_ascii_whitespace());
884
885            if !buf.consume('{') {
886                Err(OpeningBraceMissing)?;
887            }
888
889            let mut dimensions = 1;
890
891            loop {
892                buf.take_while(|ch| ch.is_ascii_whitespace());
893                if buf.consume('{') {
894                    dimensions += 1;
895                } else {
896                    break;
897                }
898            }
899
900            let mut builder = ArrayBuilder {
901                current_command_char: '{',
902                dimensions: vec![Dimension::default(); dimensions],
903                // We enter the builder at the element-bearing dimension, which is the last
904                // dimension.
905                current_dim: dimensions - 1,
906                sealed: false,
907                elements: vec![],
908            };
909
910            let is_special_char = |c| matches!(c, '{' | '}' | ',' | '\\' | '"');
911            let is_end_of_literal = |c| matches!(c, ',' | '}');
912
913            loop {
914                buf.take_while(|ch| ch.is_ascii_whitespace());
915
916                // Filter command state from terminal states.
917                match buf.next() {
918                    None if builder.sealed => {
919                        break;
920                    }
921                    None => Err(EarlyTerm)?,
922                    Some(_) if builder.sealed => Err(Junk)?,
923                    Some(c) => builder.current_command_char = c,
924                }
925
926                // Run command char
927                match builder.current_command_char {
928                    '{' => builder.enter_dim()?,
929                    '}' => builder.exit_dim()?,
930                    ',' => builder.commit_element(true)?,
931                    c => {
932                        buf.prev();
933                        let s = match c {
934                            '"' => Some(lex_quoted_element(buf)?),
935                            _ => lex_unquoted_element(buf, is_special_char, is_end_of_literal)?,
936                        };
937                        builder.insert_element(s)?;
938                    }
939                }
940            }
941
942            if builder.elements.is_empty() {
943                // Per PG, empty arrays are represented by empty dimensions
944                // rather than one dimension with 0 length.
945                return Ok((vec![], vec![]));
946            }
947
948            let dims = builder
949                .dimensions
950                .into_iter()
951                .map(|dim| ArrayDimension {
952                    length: dim
953                        .length
954                        .expect("every dimension must have its length discovered"),
955                    lower_bound: 1,
956                })
957                .collect();
958
959            Ok((builder.elements, dims))
960        }
961
962        /// Descend into another dimension of the array.
963        fn enter_dim(&mut self) -> Result<(), ArrayParsingError> {
964            let d = &mut self.dimensions[self.current_dim];
965            // Cannot enter a new dimension with an uncommitted element.
966            if d.staged_element {
967                return Err(UnexpectedChar(self.current_command_char));
968            }
969
970            self.current_dim += 1;
971
972            // You have exceeded the maximum dimensions.
973            if self.current_dim >= self.dimensions.len() {
974                return Err(NonRectilinearDims);
975            }
976
977            Ok(())
978        }
979
980        /// Insert a new element into the array, ensuring it is in the proper dimension.
981        fn insert_element(&mut self, s: Option<Cow<'a, str>>) -> Result<(), ArrayParsingError> {
982            // Can only insert elements into data-bearing dimension, which is
983            // the last one.
984            if self.current_dim != self.dimensions.len() - 1 {
985                return Err(UnexpectedElement);
986            }
987
988            self.stage_element()?;
989
990            self.elements.push(s);
991
992            Ok(())
993        }
994
995        /// Stage an element to be committed. Only one element can be staged at
996        /// a time and staged elements must be committed before moving onto the
997        /// next element or leaving the dimension.
998        fn stage_element(&mut self) -> Result<(), ArrayParsingError> {
999            let d = &mut self.dimensions[self.current_dim];
1000            // Cannot stage two elements at once, i.e. previous element wasn't
1001            // followed by committing token (`,` or `}`).
1002            if d.staged_element {
1003                return Err(UnexpectedElement);
1004            }
1005            d.staged_element = true;
1006            Ok(())
1007        }
1008
1009        /// Commit the currently staged element, which can be made optional.
1010        /// This ensures that each element has an appropriate terminal character
1011        /// after it.
1012        fn commit_element(&mut self, require_staged: bool) -> Result<(), ArrayParsingError> {
1013            let d = &mut self.dimensions[self.current_dim];
1014            if !d.staged_element {
1015                // - , requires a preceding staged element
1016                // - } does not require a preceding staged element only when
1017                //   it's the close of an empty dimension.
1018                return if require_staged || d.committed_element_count > 0 {
1019                    Err(UnexpectedChar(self.current_command_char))
1020                } else {
1021                    // This indicates that we have an empty value in this
1022                    // dimension and want to exit before incrementing the
1023                    // committed element count.
1024                    Ok(())
1025                };
1026            }
1027            d.staged_element = false;
1028            d.committed_element_count += 1;
1029
1030            Ok(())
1031        }
1032
1033        /// Exit the current dimension, committing any currently staged element
1034        /// in this dimension, and marking the interior array that this is part
1035        /// of as staged itself. If this is the 0th dimension, i.e. the closed
1036        /// brace matching the first open brace, seal the builder from further
1037        /// modification.
1038        fn exit_dim(&mut self) -> Result<(), ArrayParsingError> {
1039            // Commit an element of this dimension
1040            self.commit_element(false)?;
1041
1042            let d = &mut self.dimensions[self.current_dim];
1043
1044            // Ensure that the elements in this dimension conform to the expected shape.
1045            match d.length {
1046                None => d.length = Some(d.committed_element_count),
1047                Some(l) => {
1048                    if l != d.committed_element_count {
1049                        return Err(NonRectilinearDims);
1050                    }
1051                }
1052            }
1053
1054            // Reset this dimension's counter in case it's re-entered.
1055            d.committed_element_count = 0;
1056
1057            // If we closed the last dimension, this array may not be modified
1058            // any longer.
1059            if self.current_dim == 0 {
1060                self.sealed = true;
1061            } else {
1062                self.current_dim -= 1;
1063                // This object is an element of a higher dimension.
1064                self.stage_element()?;
1065            }
1066
1067            Ok(())
1068        }
1069    }
1070
1071    let (raw_elems, dims) = ArrayBuilder::build(s)?;
1072
1073    let mut elems = Vec::with_capacity(raw_elems.len());
1074
1075    let mut generated = |elem| gen_elem(elem).map_err(|e| e.to_string());
1076
1077    for elem in raw_elems.into_iter() {
1078        elems.push(match elem {
1079            Some(elem) => generated(elem)?,
1080            None => make_null(),
1081        });
1082    }
1083
1084    Ok((elems, dims))
1085}
1086
1087pub fn parse_list<'a, T, E>(
1088    s: &'a str,
1089    is_element_type_list: bool,
1090    make_null: impl FnMut() -> T,
1091    gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
1092) -> Result<Vec<T>, ParseError>
1093where
1094    E: ToString,
1095{
1096    parse_list_inner(s, is_element_type_list, make_null, gen_elem)
1097        .map_err(|details| ParseError::invalid_input_syntax("list", s).with_details(details))
1098}
1099
1100// `parse_list_inner`'s separation from `parse_list` simplifies error handling
1101// by allowing subprocedures to return `String` errors.
1102fn parse_list_inner<'a, T, E>(
1103    s: &'a str,
1104    is_element_type_list: bool,
1105    mut make_null: impl FnMut() -> T,
1106    mut gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
1107) -> Result<Vec<T>, String>
1108where
1109    E: ToString,
1110{
1111    let mut elems = vec![];
1112    let buf = &mut LexBuf::new(s);
1113
1114    // Consume opening paren.
1115    if !buf.consume('{') {
1116        bail!(
1117            "expected '{{', found {}",
1118            match buf.next() {
1119                Some(c) => format!("{}", c),
1120                None => "empty string".to_string(),
1121            }
1122        )
1123    }
1124
1125    // Simplifies calls to `gen_elem` by handling errors
1126    let mut generated = |elem| gen_elem(elem).map_err(|e| e.to_string());
1127    let is_special_char = |c| matches!(c, '{' | '}' | ',' | '\\' | '"');
1128    let is_end_of_literal = |c| matches!(c, ',' | '}');
1129
1130    // Consume elements.
1131    loop {
1132        buf.take_while(|ch| ch.is_ascii_whitespace());
1133        // Check for terminals.
1134        match buf.next() {
1135            Some('}') => {
1136                break;
1137            }
1138            _ if elems.len() == 0 => {
1139                buf.prev();
1140            }
1141            Some(',') => {}
1142            Some(c) => bail!("expected ',' or '}}', got '{}'", c),
1143            None => bail!("unexpected end of input"),
1144        }
1145
1146        buf.take_while(|ch| ch.is_ascii_whitespace());
1147        // Get elements.
1148        let elem = match buf.peek() {
1149            Some('"') => generated(lex_quoted_element(buf)?)?,
1150            Some('{') => {
1151                if !is_element_type_list {
1152                    bail!(
1153                        "unescaped '{{' at beginning of element; perhaps you \
1154                        want a nested list, e.g. '{{a}}'::text list list"
1155                    )
1156                }
1157                generated(lex_embedded_element(buf)?)?
1158            }
1159            Some(_) => match lex_unquoted_element(buf, is_special_char, is_end_of_literal)? {
1160                Some(elem) => generated(elem)?,
1161                None => make_null(),
1162            },
1163            None => bail!("unexpected end of input"),
1164        };
1165        elems.push(elem);
1166    }
1167
1168    buf.take_while(|ch| ch.is_ascii_whitespace());
1169    if let Some(c) = buf.next() {
1170        bail!(
1171            "malformed array literal; contains '{}' after terminal '}}'",
1172            c
1173        )
1174    }
1175
1176    Ok(elems)
1177}
1178
1179pub fn parse_legacy_vector<'a, T, E>(
1180    s: &'a str,
1181    gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
1182) -> Result<Vec<T>, ParseError>
1183where
1184    E: ToString,
1185{
1186    parse_legacy_vector_inner(s, gen_elem)
1187        .map_err(|details| ParseError::invalid_input_syntax("int2vector", s).with_details(details))
1188}
1189
1190/// Parses PostgreSQL's legacy whitespace-separated vector syntax (used in
1191/// Materialize for `int2vector`). Unlike [`parse_array`], this grammar has
1192/// no token for `NULL`, which is why `int2vector` cannot represent `NULL`
1193/// elements. See [`crate::scalar::Int2Vector`].
1194pub fn parse_legacy_vector_inner<'a, T, E>(
1195    s: &'a str,
1196    mut gen_elem: impl FnMut(Cow<'a, str>) -> Result<T, E>,
1197) -> Result<Vec<T>, String>
1198where
1199    E: ToString,
1200{
1201    let mut elems = vec![];
1202    let buf = &mut LexBuf::new(s);
1203
1204    let mut generated = |elem| gen_elem(elem).map_err(|e| e.to_string());
1205
1206    loop {
1207        buf.take_while(|ch| ch.is_ascii_whitespace());
1208        match buf.peek() {
1209            Some(_) => {
1210                let elem = buf.take_while(|ch| !ch.is_ascii_whitespace());
1211                elems.push(generated(elem.into())?);
1212            }
1213            None => break,
1214        }
1215    }
1216
1217    Ok(elems)
1218}
1219
1220fn lex_quoted_element<'a>(buf: &mut LexBuf<'a>) -> Result<Cow<'a, str>, String> {
1221    assert!(buf.consume('"'));
1222    let s = buf.take_while(|ch| !matches!(ch, '"' | '\\'));
1223
1224    // `Cow::Borrowed` optimization for quoted strings without escapes
1225    if let Some('"') = buf.peek() {
1226        buf.next();
1227        return Ok(s.into());
1228    }
1229
1230    let mut s = s.to_string();
1231    loop {
1232        match buf.next() {
1233            Some('\\') => match buf.next() {
1234                Some(c) => s.push(c),
1235                None => bail!("unterminated quoted string"),
1236            },
1237            Some('"') => break,
1238            Some(c) => s.push(c),
1239            None => bail!("unterminated quoted string"),
1240        }
1241    }
1242    Ok(s.into())
1243}
1244
1245fn lex_embedded_element<'a>(buf: &mut LexBuf<'a>) -> Result<Cow<'a, str>, String> {
1246    let pos = buf.pos();
1247    assert!(matches!(buf.next(), Some('{')));
1248    let mut depth = 1;
1249    let mut in_escape = false;
1250    while depth > 0 {
1251        match buf.next() {
1252            Some('\\') => {
1253                buf.next(); // Next character is escaped, so ignore it
1254            }
1255            Some('"') => in_escape = !in_escape, // Begin or end escape
1256            Some('{') if !in_escape => depth += 1,
1257            Some('}') if !in_escape => depth -= 1,
1258            Some(_) => (),
1259            None => bail!("unterminated embedded element"),
1260        }
1261    }
1262    let s = &buf.inner()[pos..buf.pos()];
1263    Ok(Cow::Borrowed(s))
1264}
1265
1266// Result of `None` indicates element is NULL.
1267fn lex_unquoted_element<'a>(
1268    buf: &mut LexBuf<'a>,
1269    is_special_char: impl Fn(char) -> bool,
1270    is_end_of_literal: impl Fn(char) -> bool,
1271) -> Result<Option<Cow<'a, str>>, String> {
1272    // first char is guaranteed to be non-whitespace
1273    assert!(!buf.peek().unwrap().is_ascii_whitespace());
1274
1275    let s = buf.take_while(|ch| !is_special_char(ch) && !ch.is_ascii_whitespace());
1276
1277    // `Cow::Borrowed` optimization for elements without special characters.
1278    match buf.peek() {
1279        Some(',') | Some('}') if !s.is_empty() => {
1280            return Ok(if s.to_uppercase() == "NULL" {
1281                None
1282            } else {
1283                Some(s.into())
1284            });
1285        }
1286        _ => {}
1287    }
1288
1289    // Track whether there are any escaped characters to determine if the string
1290    // "NULL" should be treated as a NULL, or if it had any escaped characters
1291    // and should be treated as the string "NULL".
1292    let mut escaped_char = false;
1293
1294    let mut s = s.to_string();
1295    // As we go, we keep track of where to truncate to in order to remove any
1296    // trailing whitespace.
1297    let mut trimmed_len = s.len();
1298    loop {
1299        match buf.next() {
1300            Some('\\') => match buf.next() {
1301                Some(c) => {
1302                    escaped_char = true;
1303                    s.push(c);
1304                    trimmed_len = s.len();
1305                }
1306                None => return Err("unterminated element".into()),
1307            },
1308            Some(c) if is_end_of_literal(c) => {
1309                // End of literal characters as the first character indicates
1310                // a missing element definition.
1311                if s.is_empty() {
1312                    bail!("malformed literal; missing element")
1313                }
1314                buf.prev();
1315                break;
1316            }
1317            Some(c) if is_special_char(c) => {
1318                bail!("malformed literal; must escape special character '{}'", c)
1319            }
1320            Some(c) => {
1321                s.push(c);
1322                if !c.is_ascii_whitespace() {
1323                    trimmed_len = s.len();
1324                }
1325            }
1326            None => bail!("unterminated element"),
1327        }
1328    }
1329    s.truncate(trimmed_len);
1330    Ok(if s.to_uppercase() == "NULL" && !escaped_char {
1331        None
1332    } else {
1333        Some(Cow::Owned(s))
1334    })
1335}
1336
1337pub fn parse_map<'a, V, E>(
1338    s: &'a str,
1339    is_value_type_map: bool,
1340    gen_elem: impl FnMut(Option<Cow<'a, str>>) -> Result<V, E>,
1341) -> Result<BTreeMap<String, V>, ParseError>
1342where
1343    E: ToString,
1344{
1345    parse_map_inner(s, is_value_type_map, gen_elem)
1346        .map_err(|details| ParseError::invalid_input_syntax("map", s).with_details(details))
1347}
1348
1349fn parse_map_inner<'a, V, E>(
1350    s: &'a str,
1351    is_value_type_map: bool,
1352    mut gen_elem: impl FnMut(Option<Cow<'a, str>>) -> Result<V, E>,
1353) -> Result<BTreeMap<String, V>, String>
1354where
1355    E: ToString,
1356{
1357    let mut map = BTreeMap::new();
1358    let buf = &mut LexBuf::new(s);
1359
1360    // Consume opening paren.
1361    if !buf.consume('{') {
1362        bail!(
1363            "expected '{{', found {}",
1364            match buf.next() {
1365                Some(c) => format!("{}", c),
1366                None => "empty string".to_string(),
1367            }
1368        )
1369    }
1370
1371    // Simplifies calls to generators by handling errors
1372    let gen_key = |key: Option<Cow<'a, str>>| -> Result<String, String> {
1373        match key {
1374            Some(Cow::Owned(s)) => Ok(s),
1375            Some(Cow::Borrowed(s)) => Ok(s.to_owned()),
1376            None => Err("expected key".to_owned()),
1377        }
1378    };
1379    let mut gen_value = |elem| gen_elem(elem).map_err(|e| e.to_string());
1380    let is_special_char = |c| matches!(c, '{' | '}' | ',' | '"' | '=' | '>' | '\\');
1381    let is_end_of_literal = |c| matches!(c, ',' | '}' | '=');
1382
1383    loop {
1384        // Check for terminals.
1385        buf.take_while(|ch| ch.is_ascii_whitespace());
1386        match buf.next() {
1387            Some('}') => break,
1388            _ if map.len() == 0 => {
1389                buf.prev();
1390            }
1391            Some(',') => {}
1392            Some(c) => bail!("expected ',' or end of input, got '{}'", c),
1393            None => bail!("unexpected end of input"),
1394        }
1395
1396        // Get key.
1397        buf.take_while(|ch| ch.is_ascii_whitespace());
1398        let key = match buf.peek() {
1399            Some('"') => Some(lex_quoted_element(buf)?),
1400            Some(_) => lex_unquoted_element(buf, is_special_char, is_end_of_literal)?,
1401            None => bail!("unexpected end of input"),
1402        };
1403        let key = gen_key(key)?;
1404
1405        // Assert mapping arrow (=>) is present.
1406        buf.take_while(|ch| ch.is_ascii_whitespace());
1407        if !buf.consume('=') || !buf.consume('>') {
1408            bail!("expected =>")
1409        }
1410
1411        // Get value.
1412        buf.take_while(|ch| ch.is_ascii_whitespace());
1413        let value = match buf.peek() {
1414            Some('"') => Some(lex_quoted_element(buf)?),
1415            Some('{') => {
1416                if !is_value_type_map {
1417                    bail!(
1418                        "unescaped '{{' at beginning of value; perhaps you \
1419                           want a nested map, e.g. '{{a=>{{a=>1}}}}'::map[text=>map[text=>int]]"
1420                    )
1421                }
1422                Some(lex_embedded_element(buf)?)
1423            }
1424            Some(_) => lex_unquoted_element(buf, is_special_char, is_end_of_literal)?,
1425            None => bail!("unexpected end of input"),
1426        };
1427        let value = gen_value(value)?;
1428
1429        // Insert elements.
1430        map.insert(key, value);
1431    }
1432    Ok(map)
1433}
1434
1435pub fn format_map<F, T, E>(
1436    buf: &mut F,
1437    elems: impl IntoIterator<Item = (impl AsRef<str>, T)>,
1438    mut format_elem: impl FnMut(MapValueWriter<F>, T) -> Result<Nestable, E>,
1439) -> Result<Nestable, E>
1440where
1441    F: FormatBuffer,
1442{
1443    buf.write_char('{');
1444    let mut elems = elems.into_iter().peekable();
1445    while let Some((key, value)) = elems.next() {
1446        // Map key values are always Strings, which always evaluate to
1447        // Nestable::MayNeedEscaping.
1448        let key_start = buf.len();
1449        buf.write_str(key.as_ref());
1450        escape_elem::<_, MapElementEscaper>(buf, key_start);
1451
1452        buf.write_str("=>");
1453
1454        let value_start = buf.len();
1455        if let Nestable::MayNeedEscaping = format_elem(MapValueWriter(buf), value)? {
1456            escape_elem::<_, MapElementEscaper>(buf, value_start);
1457        }
1458
1459        if elems.peek().is_some() {
1460            buf.write_char(',');
1461        }
1462    }
1463    buf.write_char('}');
1464    Ok(Nestable::Yes)
1465}
1466
1467pub fn parse_range<'a, V, E>(
1468    s: &'a str,
1469    gen_elem: impl FnMut(Cow<'a, str>) -> Result<V, E>,
1470) -> Result<Range<V>, ParseError>
1471where
1472    E: ToString,
1473{
1474    Ok(Range {
1475        inner: parse_range_inner(s, gen_elem).map_err(|details| {
1476            ParseError::invalid_input_syntax("range", s).with_details(details)
1477        })?,
1478    })
1479}
1480
1481fn parse_range_inner<'a, V, E>(
1482    s: &'a str,
1483    mut gen_elem: impl FnMut(Cow<'a, str>) -> Result<V, E>,
1484) -> Result<Option<RangeInner<V>>, String>
1485where
1486    E: ToString,
1487{
1488    let buf = &mut LexBuf::new(s);
1489
1490    buf.take_while(|ch| ch.is_ascii_whitespace());
1491
1492    if buf.consume_str("empty") {
1493        buf.take_while(|ch| ch.is_ascii_whitespace());
1494        if buf.next().is_none() {
1495            return Ok(None);
1496        } else {
1497            bail!("Junk after \"empty\" key word.")
1498        }
1499    }
1500
1501    let lower_inclusive = match buf.next() {
1502        Some('[') => true,
1503        Some('(') => false,
1504        _ => bail!("Missing left parenthesis or bracket."),
1505    };
1506
1507    let lower_bound = match buf.peek() {
1508        Some(',') => None,
1509        Some(_) => {
1510            let v = buf.take_while(|c| !matches!(c, ','));
1511            let v = gen_elem(Cow::from(v)).map_err(|e| e.to_string())?;
1512            Some(v)
1513        }
1514        None => bail!("Unexpected end of input."),
1515    };
1516
1517    buf.take_while(|ch| ch.is_ascii_whitespace());
1518
1519    if buf.next() != Some(',') {
1520        bail!("Missing comma after lower bound.")
1521    }
1522
1523    let upper_bound = match buf.peek() {
1524        Some(']' | ')') => None,
1525        Some(_) => {
1526            let v = buf.take_while(|c| !matches!(c, ')' | ']'));
1527            let v = gen_elem(Cow::from(v)).map_err(|e| e.to_string())?;
1528            Some(v)
1529        }
1530        None => bail!("Unexpected end of input."),
1531    };
1532
1533    let upper_inclusive = match buf.next() {
1534        Some(']') => true,
1535        Some(')') => false,
1536        _ => bail!("Missing left parenthesis or bracket."),
1537    };
1538
1539    buf.take_while(|ch| ch.is_ascii_whitespace());
1540
1541    if buf.next().is_some() {
1542        bail!("Junk after right parenthesis or bracket.")
1543    }
1544
1545    let range = Some(RangeInner {
1546        lower: RangeBound {
1547            inclusive: lower_inclusive,
1548            bound: lower_bound,
1549        },
1550        upper: RangeBound {
1551            inclusive: upper_inclusive,
1552            bound: upper_bound,
1553        },
1554    });
1555
1556    Ok(range)
1557}
1558
1559/// Writes a [`Range`] to `buf`.
1560pub fn format_range<F, V, E>(
1561    buf: &mut F,
1562    r: &Range<V>,
1563    mut format_elem: impl FnMut(RangeElementWriter<F>, Option<&V>) -> Result<Nestable, E>,
1564) -> Result<Nestable, E>
1565where
1566    F: FormatBuffer,
1567{
1568    let range = match &r.inner {
1569        None => {
1570            buf.write_str("empty");
1571            return Ok(Nestable::MayNeedEscaping);
1572        }
1573        Some(i) => i,
1574    };
1575
1576    if range.lower.inclusive {
1577        buf.write_char('[');
1578    } else {
1579        buf.write_char('(');
1580    }
1581
1582    let start = buf.len();
1583    if let Nestable::MayNeedEscaping =
1584        format_elem(RangeElementWriter(buf), range.lower.bound.as_ref())?
1585    {
1586        escape_elem::<_, ListElementEscaper>(buf, start);
1587    }
1588
1589    buf.write_char(',');
1590
1591    let start = buf.len();
1592    if let Nestable::MayNeedEscaping =
1593        format_elem(RangeElementWriter(buf), range.upper.bound.as_ref())?
1594    {
1595        escape_elem::<_, ListElementEscaper>(buf, start);
1596    }
1597
1598    if range.upper.inclusive {
1599        buf.write_char(']');
1600    } else {
1601        buf.write_char(')');
1602    }
1603
1604    Ok(Nestable::MayNeedEscaping)
1605}
1606
1607/// A helper for `format_range` that formats a single record element.
1608#[derive(Debug)]
1609pub struct RangeElementWriter<'a, F>(&'a mut F);
1610
1611impl<'a, F> RangeElementWriter<'a, F>
1612where
1613    F: FormatBuffer,
1614{
1615    /// Marks this record element as null.
1616    pub fn write_null(self) -> Nestable {
1617        // In ranges these "null" values represent infinite bounds, which are
1618        // not represented as values, but rather the absence of a value.
1619        Nestable::Yes
1620    }
1621
1622    /// Returns a [`FormatBuffer`] into which a non-null element can be
1623    /// written.
1624    pub fn nonnull_buffer(self) -> &'a mut F {
1625        self.0
1626    }
1627}
1628
1629pub fn format_array<F, T, E>(
1630    buf: &mut F,
1631    dims: &[ArrayDimension],
1632    elems: impl IntoIterator<Item = T>,
1633    mut format_elem: impl FnMut(ListElementWriter<F>, T) -> Result<Nestable, E>,
1634) -> Result<Nestable, E>
1635where
1636    F: FormatBuffer,
1637{
1638    if dims.iter().any(|dim| dim.lower_bound != 1) {
1639        for d in dims.iter() {
1640            let (lower, upper) = d.dimension_bounds();
1641            write!(buf, "[{}:{}]", lower, upper);
1642        }
1643        buf.write_char('=');
1644    }
1645
1646    format_array_inner(buf, dims, &mut elems.into_iter(), &mut format_elem)?;
1647    Ok(Nestable::Yes)
1648}
1649
1650pub fn format_array_inner<F, T, E>(
1651    buf: &mut F,
1652    dims: &[ArrayDimension],
1653    elems: &mut impl Iterator<Item = T>,
1654    format_elem: &mut impl FnMut(ListElementWriter<F>, T) -> Result<Nestable, E>,
1655) -> Result<(), E>
1656where
1657    F: FormatBuffer,
1658{
1659    if dims.is_empty() {
1660        buf.write_str("{}");
1661        return Ok(());
1662    }
1663
1664    buf.write_char('{');
1665    for j in 0..dims[0].length {
1666        if j > 0 {
1667            buf.write_char(',');
1668        }
1669        if dims.len() == 1 {
1670            let start = buf.len();
1671            let elem = elems.next().unwrap();
1672            if let Nestable::MayNeedEscaping = format_elem(ListElementWriter(buf), elem)? {
1673                escape_elem::<_, ListElementEscaper>(buf, start);
1674            }
1675        } else {
1676            format_array_inner(buf, &dims[1..], elems, format_elem)?;
1677        }
1678    }
1679    buf.write_char('}');
1680
1681    Ok(())
1682}
1683
1684pub fn format_legacy_vector<F, T, E>(
1685    buf: &mut F,
1686    elems: impl IntoIterator<Item = T>,
1687    format_elem: impl FnMut(ListElementWriter<F>, T) -> Result<Nestable, E>,
1688) -> Result<Nestable, E>
1689where
1690    F: FormatBuffer,
1691{
1692    format_elems(buf, elems, format_elem, ' ')?;
1693    Ok(Nestable::MayNeedEscaping)
1694}
1695
1696pub fn format_list<F, T, E>(
1697    buf: &mut F,
1698    elems: impl IntoIterator<Item = T>,
1699    format_elem: impl FnMut(ListElementWriter<F>, T) -> Result<Nestable, E>,
1700) -> Result<Nestable, E>
1701where
1702    F: FormatBuffer,
1703{
1704    buf.write_char('{');
1705    format_elems(buf, elems, format_elem, ',')?;
1706    buf.write_char('}');
1707    Ok(Nestable::Yes)
1708}
1709
1710/// Writes each `elem` into `buf`, separating the elems with `sep`.
1711pub fn format_elems<F, T, E>(
1712    buf: &mut F,
1713    elems: impl IntoIterator<Item = T>,
1714    mut format_elem: impl FnMut(ListElementWriter<F>, T) -> Result<Nestable, E>,
1715    sep: char,
1716) -> Result<(), E>
1717where
1718    F: FormatBuffer,
1719{
1720    let mut elems = elems.into_iter().peekable();
1721    while let Some(elem) = elems.next() {
1722        let start = buf.len();
1723        if let Nestable::MayNeedEscaping = format_elem(ListElementWriter(buf), elem)? {
1724            escape_elem::<_, ListElementEscaper>(buf, start);
1725        }
1726        if elems.peek().is_some() {
1727            buf.write_char(sep)
1728        }
1729    }
1730    Ok(())
1731}
1732
1733/// Writes an `mz_acl_item` to `buf`.
1734pub fn format_mz_acl_item<F>(buf: &mut F, mz_acl_item: MzAclItem) -> Nestable
1735where
1736    F: FormatBuffer,
1737{
1738    write!(buf, "{mz_acl_item}");
1739    Nestable::Yes
1740}
1741
1742/// Parses an MzAclItem from `s`.
1743pub fn parse_mz_acl_item(s: &str) -> Result<MzAclItem, ParseError> {
1744    s.trim()
1745        .parse()
1746        .map_err(|e| ParseError::invalid_input_syntax("mz_aclitem", s).with_details(e))
1747}
1748
1749/// Writes an `acl_item` to `buf`.
1750pub fn format_acl_item<F>(buf: &mut F, acl_item: AclItem) -> Nestable
1751where
1752    F: FormatBuffer,
1753{
1754    write!(buf, "{acl_item}");
1755    Nestable::Yes
1756}
1757
1758/// Parses an AclItem from `s`.
1759pub fn parse_acl_item(s: &str) -> Result<AclItem, ParseError> {
1760    s.trim()
1761        .parse()
1762        .map_err(|e| ParseError::invalid_input_syntax("aclitem", s).with_details(e))
1763}
1764
1765pub trait ElementEscaper {
1766    fn needs_escaping(elem: &[u8]) -> bool;
1767    fn escape_char(c: u8) -> u8;
1768}
1769
1770struct ListElementEscaper;
1771
1772impl ElementEscaper for ListElementEscaper {
1773    fn needs_escaping(elem: &[u8]) -> bool {
1774        elem.is_empty()
1775            || elem == b"NULL"
1776            || elem
1777                .iter()
1778                .any(|c| matches!(c, b'{' | b'}' | b',' | b'"' | b'\\') || c.is_ascii_whitespace())
1779    }
1780
1781    fn escape_char(_: u8) -> u8 {
1782        b'\\'
1783    }
1784}
1785
1786struct MapElementEscaper;
1787
1788impl ElementEscaper for MapElementEscaper {
1789    fn needs_escaping(elem: &[u8]) -> bool {
1790        elem.is_empty()
1791            || elem == b"NULL"
1792            || elem.iter().any(|c| {
1793                matches!(c, b'{' | b'}' | b',' | b'"' | b'=' | b'>' | b'\\')
1794                    || c.is_ascii_whitespace()
1795            })
1796    }
1797
1798    fn escape_char(_: u8) -> u8 {
1799        b'\\'
1800    }
1801}
1802
1803struct RecordElementEscaper;
1804
1805impl ElementEscaper for RecordElementEscaper {
1806    fn needs_escaping(elem: &[u8]) -> bool {
1807        elem.is_empty()
1808            || elem
1809                .iter()
1810                .any(|c| matches!(c, b'(' | b')' | b',' | b'"' | b'\\') || c.is_ascii_whitespace())
1811    }
1812
1813    fn escape_char(c: u8) -> u8 {
1814        if c == b'"' { b'"' } else { b'\\' }
1815    }
1816}
1817
1818/// Escapes a list, record, or map element in place.
1819///
1820/// The element must start at `start` and extend to the end of the buffer. The
1821/// buffer will be resized if escaping is necessary to account for the
1822/// additional escape characters.
1823///
1824/// The `needs_escaping` function is used to determine whether an element needs
1825/// to be escaped. It is provided with the bytes of each element and should
1826/// return whether the element needs to be escaped.
1827fn escape_elem<F, E>(buf: &mut F, start: usize)
1828where
1829    F: FormatBuffer,
1830    E: ElementEscaper,
1831{
1832    let elem = &buf.as_ref()[start..];
1833    if !E::needs_escaping(elem) {
1834        return;
1835    }
1836
1837    // We'll need two extra bytes for the quotes at the start and end of the
1838    // element, plus an extra byte for each quote and backslash.
1839    let extras = 2 + elem.iter().filter(|b| matches!(b, b'"' | b'\\')).count();
1840    let orig_end = buf.len();
1841    let new_end = buf.len() + extras;
1842
1843    // Pad the buffer to the new length. These characters will all be
1844    // overwritten.
1845    //
1846    // NOTE(benesch): we never read these characters, so we could instead use
1847    // uninitialized memory, but that's a level of unsafety I'm currently
1848    // uncomfortable with. The performance gain is negligible anyway.
1849    for _ in 0..extras {
1850        buf.write_char('\0');
1851    }
1852
1853    // SAFETY: inserting ASCII characters before other ASCII characters
1854    // preserves UTF-8 encoding.
1855    let elem = unsafe { buf.as_bytes_mut() };
1856
1857    // Walk the string backwards, writing characters at the new end index while
1858    // reading from the old end index, adding quotes at the beginning and end,
1859    // and adding a backslash before every backslash or quote.
1860    let mut wi = new_end - 1;
1861    elem[wi] = b'"';
1862    wi -= 1;
1863    for ri in (start..orig_end).rev() {
1864        elem[wi] = elem[ri];
1865        wi -= 1;
1866        if let b'\\' | b'"' = elem[ri] {
1867            elem[wi] = E::escape_char(elem[ri]);
1868            wi -= 1;
1869        }
1870    }
1871    elem[wi] = b'"';
1872
1873    assert!(wi == start);
1874}
1875
1876/// A helper for `format_list` that formats a single list element.
1877#[derive(Debug)]
1878pub struct ListElementWriter<'a, F>(&'a mut F);
1879
1880impl<'a, F> ListElementWriter<'a, F>
1881where
1882    F: FormatBuffer,
1883{
1884    /// Marks this list element as null.
1885    pub fn write_null(self) -> Nestable {
1886        self.0.write_str("NULL");
1887        Nestable::Yes
1888    }
1889
1890    /// Returns a [`FormatBuffer`] into which a non-null element can be
1891    /// written.
1892    pub fn nonnull_buffer(self) -> &'a mut F {
1893        self.0
1894    }
1895}
1896
1897/// A helper for `format_map` that formats a single map value.
1898#[derive(Debug)]
1899pub struct MapValueWriter<'a, F>(&'a mut F);
1900
1901impl<'a, F> MapValueWriter<'a, F>
1902where
1903    F: FormatBuffer,
1904{
1905    /// Marks this value element as null.
1906    pub fn write_null(self) -> Nestable {
1907        self.0.write_str("NULL");
1908        Nestable::Yes
1909    }
1910
1911    /// Returns a [`FormatBuffer`] into which a non-null element can be
1912    /// written.
1913    pub fn nonnull_buffer(self) -> &'a mut F {
1914        self.0
1915    }
1916}
1917
1918pub fn format_record<F, T, E>(
1919    buf: &mut F,
1920    elems: impl IntoIterator<Item = T>,
1921    mut format_elem: impl FnMut(RecordElementWriter<F>, T) -> Result<Nestable, E>,
1922) -> Result<Nestable, E>
1923where
1924    F: FormatBuffer,
1925{
1926    buf.write_char('(');
1927    let mut elems = elems.into_iter().peekable();
1928    while let Some(elem) = elems.next() {
1929        let start = buf.len();
1930        if let Nestable::MayNeedEscaping = format_elem(RecordElementWriter(buf), elem)? {
1931            escape_elem::<_, RecordElementEscaper>(buf, start);
1932        }
1933        if elems.peek().is_some() {
1934            buf.write_char(',')
1935        }
1936    }
1937    buf.write_char(')');
1938    Ok(Nestable::MayNeedEscaping)
1939}
1940
1941/// A helper for `format_record` that formats a single record element.
1942#[derive(Debug)]
1943pub struct RecordElementWriter<'a, F>(&'a mut F);
1944
1945impl<'a, F> RecordElementWriter<'a, F>
1946where
1947    F: FormatBuffer,
1948{
1949    /// Marks this record element as null.
1950    pub fn write_null(self) -> Nestable {
1951        Nestable::Yes
1952    }
1953
1954    /// Returns a [`FormatBuffer`] into which a non-null element can be
1955    /// written.
1956    pub fn nonnull_buffer(self) -> &'a mut F {
1957        self.0
1958    }
1959}
1960
1961/// An error while parsing an input as a type.
1962#[derive(
1963    Ord,
1964    PartialOrd,
1965    Clone,
1966    Debug,
1967    Eq,
1968    PartialEq,
1969    Serialize,
1970    Deserialize,
1971    Hash,
1972    MzReflect
1973)]
1974#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
1975pub struct ParseError {
1976    pub kind: ParseErrorKind,
1977    pub type_name: Box<str>,
1978    pub input: Box<str>,
1979    pub details: Option<Box<str>>,
1980}
1981
1982#[derive(
1983    Ord,
1984    PartialOrd,
1985    Clone,
1986    Copy,
1987    Debug,
1988    Eq,
1989    PartialEq,
1990    Serialize,
1991    Deserialize,
1992    Hash,
1993    MzReflect
1994)]
1995#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
1996pub enum ParseErrorKind {
1997    OutOfRange,
1998    InvalidInputSyntax,
1999}
2000
2001impl ParseError {
2002    // To ensure that reversing the parameters causes a compile-time error, we
2003    // require that `type_name` be a string literal, even though `ParseError`
2004    // itself stores the type name as a `String`.
2005    fn new<S>(kind: ParseErrorKind, type_name: &'static str, input: S) -> ParseError
2006    where
2007        S: Into<Box<str>>,
2008    {
2009        ParseError {
2010            kind,
2011            type_name: type_name.into(),
2012            input: input.into(),
2013            details: None,
2014        }
2015    }
2016
2017    fn out_of_range<S>(type_name: &'static str, input: S) -> ParseError
2018    where
2019        S: Into<Box<str>>,
2020    {
2021        ParseError::new(ParseErrorKind::OutOfRange, type_name, input)
2022    }
2023
2024    fn invalid_input_syntax<S>(type_name: &'static str, input: S) -> ParseError
2025    where
2026        S: Into<Box<str>>,
2027    {
2028        ParseError::new(ParseErrorKind::InvalidInputSyntax, type_name, input)
2029    }
2030
2031    fn with_details<D>(mut self, details: D) -> ParseError
2032    where
2033        D: fmt::Display,
2034    {
2035        self.details = Some(details.to_string().into());
2036        self
2037    }
2038}
2039
2040impl fmt::Display for ParseError {
2041    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2042        match self.kind {
2043            ParseErrorKind::OutOfRange => {
2044                write!(
2045                    f,
2046                    "{} is out of range for type {}",
2047                    self.input.quoted(),
2048                    self.type_name
2049                )?;
2050                if let Some(details) = &self.details {
2051                    write!(f, ": {}", details)?;
2052                }
2053                Ok(())
2054            }
2055            ParseErrorKind::InvalidInputSyntax => {
2056                write!(f, "invalid input syntax for type {}: ", self.type_name)?;
2057                if let Some(details) = &self.details {
2058                    write!(f, "{}: ", details)?;
2059                }
2060                write!(f, "{}", self.input.quoted())
2061            }
2062        }
2063    }
2064}
2065
2066impl Error for ParseError {}
2067
2068impl RustType<ProtoParseError> for ParseError {
2069    fn into_proto(&self) -> ProtoParseError {
2070        use Kind::*;
2071        use proto_parse_error::*;
2072        let kind = match self.kind {
2073            ParseErrorKind::OutOfRange => OutOfRange(()),
2074            ParseErrorKind::InvalidInputSyntax => InvalidInputSyntax(()),
2075        };
2076        ProtoParseError {
2077            kind: Some(kind),
2078            type_name: self.type_name.into_proto(),
2079            input: self.input.into_proto(),
2080            details: self.details.into_proto(),
2081        }
2082    }
2083
2084    fn from_proto(proto: ProtoParseError) -> Result<Self, TryFromProtoError> {
2085        use proto_parse_error::Kind::*;
2086
2087        if let Some(kind) = proto.kind {
2088            Ok(ParseError {
2089                kind: match kind {
2090                    OutOfRange(()) => ParseErrorKind::OutOfRange,
2091                    InvalidInputSyntax(()) => ParseErrorKind::InvalidInputSyntax,
2092                },
2093                type_name: proto.type_name.into(),
2094                input: proto.input.into(),
2095                details: proto.details.into_rust()?,
2096            })
2097        } else {
2098            Err(TryFromProtoError::missing_field("ProtoParseError::kind"))
2099        }
2100    }
2101}
2102
2103#[derive(
2104    Ord,
2105    PartialOrd,
2106    Copy,
2107    Clone,
2108    Debug,
2109    Eq,
2110    PartialEq,
2111    Serialize,
2112    Deserialize,
2113    Hash,
2114    MzReflect
2115)]
2116#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
2117pub enum ParseHexError {
2118    InvalidHexDigit(char),
2119    OddLength,
2120}
2121impl Error for ParseHexError {}
2122
2123impl fmt::Display for ParseHexError {
2124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2125        match self {
2126            ParseHexError::InvalidHexDigit(c) => {
2127                write!(f, "invalid hexadecimal digit: \"{}\"", c.escape_default())
2128            }
2129            ParseHexError::OddLength => {
2130                f.write_str("invalid hexadecimal data: odd number of digits")
2131            }
2132        }
2133    }
2134}
2135
2136impl RustType<ProtoParseHexError> for ParseHexError {
2137    fn into_proto(&self) -> ProtoParseHexError {
2138        use Kind::*;
2139        use proto_parse_hex_error::*;
2140        let kind = match self {
2141            ParseHexError::InvalidHexDigit(v) => InvalidHexDigit(v.into_proto()),
2142            ParseHexError::OddLength => OddLength(()),
2143        };
2144        ProtoParseHexError { kind: Some(kind) }
2145    }
2146
2147    fn from_proto(error: ProtoParseHexError) -> Result<Self, TryFromProtoError> {
2148        use proto_parse_hex_error::Kind::*;
2149        match error.kind {
2150            Some(kind) => match kind {
2151                InvalidHexDigit(v) => Ok(ParseHexError::InvalidHexDigit(char::from_proto(v)?)),
2152                OddLength(()) => Ok(ParseHexError::OddLength),
2153            },
2154            None => Err(TryFromProtoError::missing_field(
2155                "`ProtoParseHexError::kind`",
2156            )),
2157        }
2158    }
2159}
2160
2161#[cfg(test)]
2162mod tests {
2163    use mz_ore::assert_ok;
2164    use mz_proto::protobuf_roundtrip;
2165    use proptest::prelude::*;
2166
2167    use super::*;
2168
2169    proptest! {
2170        #[mz_ore::test]
2171        #[cfg_attr(miri, ignore)] // too slow
2172        fn parse_error_protobuf_roundtrip(expect in any::<ParseError>()) {
2173            let actual = protobuf_roundtrip::<_, ProtoParseError>(&expect);
2174            assert_ok!(actual);
2175            assert_eq!(actual.unwrap(), expect);
2176        }
2177    }
2178
2179    proptest! {
2180        #[mz_ore::test]
2181        #[cfg_attr(miri, ignore)] // too slow
2182        fn parse_hex_error_protobuf_roundtrip(expect in any::<ParseHexError>()) {
2183            let actual = protobuf_roundtrip::<_, ProtoParseHexError>(&expect);
2184            assert_ok!(actual);
2185            assert_eq!(actual.unwrap(), expect);
2186        }
2187    }
2188
2189    #[mz_ore::test]
2190    fn test_format_nanos_to_micros() {
2191        let cases: Vec<(u32, &str)> = vec![
2192            (0, ""),
2193            (1, ""),
2194            (499, ""),
2195            (500, ".000001"),
2196            (500_000, ".0005"),
2197            (5_000_000, ".005"),
2198            // Leap second. This is possibly wrong and should maybe be reduced (nanosecond
2199            // % 1_000_000_000), but we are at least now aware it does this.
2200            (1_999_999_999, ".2"),
2201        ];
2202        for (nanos, expect) in cases {
2203            let mut buf = String::new();
2204            format_nanos_to_micros(&mut buf, nanos);
2205            assert_eq!(&buf, expect);
2206        }
2207    }
2208
2209    #[mz_ore::test]
2210    fn test_parse_pg_legacy_name() {
2211        let s = "hello world";
2212        assert_eq!(s, parse_pg_legacy_name(s));
2213
2214        let s = "x".repeat(63);
2215        assert_eq!(s, parse_pg_legacy_name(&s));
2216
2217        let s = "x".repeat(64);
2218        assert_eq!("x".repeat(63), parse_pg_legacy_name(&s));
2219
2220        // The Hebrew character Aleph (א) has a length of 2 bytes.
2221        let s = format!("{}{}", "x".repeat(61), "א");
2222        assert_eq!(s, parse_pg_legacy_name(&s));
2223
2224        let s = format!("{}{}", "x".repeat(62), "א");
2225        assert_eq!("x".repeat(62), parse_pg_legacy_name(&s));
2226    }
2227
2228    #[mz_ore::test]
2229    fn test_parse_oid() {
2230        // The full u32 range is accepted, matching PostgreSQL.
2231        assert_eq!(parse_oid("0").unwrap(), 0);
2232        assert_eq!(parse_oid("2147483647").unwrap(), 2147483647);
2233        assert_eq!(parse_oid("2147483648").unwrap(), 2147483648);
2234        assert_eq!(parse_oid("4294967295").unwrap(), 4294967295);
2235
2236        // Negative values in the i32 range are reinterpreted as u32.
2237        assert_eq!(parse_oid("-1").unwrap(), 4294967295);
2238        assert_eq!(parse_oid("-2147483648").unwrap(), 2147483648);
2239
2240        // Surrounding whitespace is ignored.
2241        assert_eq!(parse_oid("  42 ").unwrap(), 42);
2242
2243        // Values outside both the u32 and i32 ranges are rejected.
2244        assert!(parse_oid("4294967296").is_err());
2245        assert!(parse_oid("-2147483649").is_err());
2246        assert!(parse_oid("nope").is_err());
2247    }
2248
2249    #[mz_ore::test]
2250    fn test_parse_oid_legacy() {
2251        // Only the i32 range is accepted, reinterpreting negatives as u32.
2252        assert_eq!(parse_oid_legacy("0").unwrap(), 0);
2253        assert_eq!(parse_oid_legacy("2147483647").unwrap(), 2147483647);
2254        assert_eq!(parse_oid_legacy("-1").unwrap(), 4294967295);
2255        assert_eq!(parse_oid_legacy("-2147483648").unwrap(), 2147483648);
2256
2257        // The frozen behavior rejects the u32-only range that `parse_oid`
2258        // accepts. This divergence must not change (storage stability).
2259        assert!(parse_oid_legacy("2147483648").is_err());
2260        assert!(parse_oid_legacy("4294967295").is_err());
2261        assert!(parse_oid_legacy("nope").is_err());
2262    }
2263}