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