mz_sql/session/vars/
value.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
10use std::any::Any;
11use std::borrow::Cow;
12use std::fmt::{self, Debug};
13use std::str::FromStr;
14use std::time::Duration;
15
16use chrono::{DateTime, Utc};
17use ipnet::IpNet;
18use itertools::Itertools;
19use mz_pgwire_common::Severity;
20use mz_repr::adt::numeric::Numeric;
21use mz_repr::adt::timestamp::CheckedTimestamp;
22use mz_repr::strconv;
23use mz_rocksdb_types::config::{CompactionStyle, CompressionType};
24use mz_sql_parser::ast::{Ident, TransactionIsolationLevel};
25use mz_tracing::{CloneableEnvFilter, SerializableDirective};
26use serde::{Deserialize, Serialize};
27use uncased::UncasedStr;
28
29use super::VarInput;
30use super::errors::VarParseError;
31
32/// Defines a value that get stored as part of a System or Session variable.
33///
34/// This trait is partially object safe, see [`VarDefinition`] for more details.
35///
36/// [`VarDefinition`]: crate::session::vars::definitions::VarDefinition
37pub trait Value: Any + AsAny + Debug + Send + Sync {
38    fn type_name() -> Cow<'static, str>
39    where
40        Self: Sized;
41
42    fn parse(input: VarInput) -> Result<Self, VarParseError>
43    where
44        Self: Sized;
45
46    fn format(&self) -> String;
47
48    fn box_clone(&self) -> Box<dyn Value>;
49
50    /// Parse an instance of `Self` from [`VarInput`], returning it as a `Box<dyn Value>`.
51    fn parse_dyn_value(input: VarInput) -> Result<Box<dyn Value>, VarParseError>
52    where
53        Self: Sized,
54    {
55        Self::parse(input).map(|val| {
56            let dyn_val: Box<dyn Value> = Box::new(val);
57            dyn_val
58        })
59    }
60}
61
62// Note(parkmycar): We have a blanket impl for `PartialEq` instead of requiring it as a trait
63// bound because otherwise it's tricky to make `Value` object safe.
64impl PartialEq for Box<dyn Value> {
65    fn eq(&self, other: &Box<dyn Value>) -> bool {
66        self.format().eq(&other.format())
67    }
68}
69impl Eq for Box<dyn Value> {}
70
71impl<'a> PartialEq for &'a dyn Value {
72    fn eq(&self, other: &&dyn Value) -> bool {
73        self.format().eq(&other.format())
74    }
75}
76impl<'a> Eq for &'a dyn Value {}
77
78/// Helper trait to cast a `&dyn T` to a `&dyn Any`.
79///
80/// In Rust all types that are `'static` implement [`std::any::Any`] and thus can be casted to a
81/// `&dyn Any`. But once you create a trait object, the type is erased and thus so is the
82/// implementation for [`Any`]. This trait essentially adds a types' [`Any`] impl to the vtable
83/// created when casted to trait object, if [`AsAny`] is a supertrait.
84///
85/// See [`Value`] for an example of using [`AsAny`].
86pub trait AsAny {
87    fn as_any(&self) -> &dyn Any;
88}
89
90impl<T: Any> AsAny for T {
91    fn as_any(&self) -> &dyn Any {
92        self
93    }
94}
95
96/// Helper method to extract a single value from any kind of [`VarInput`].
97fn extract_single_value(input: VarInput) -> Result<&str, VarParseError> {
98    match input {
99        VarInput::Flat(value) => Ok(value),
100        VarInput::SqlSet([value]) => Ok(value),
101        VarInput::SqlSet(values) => Err(VarParseError::InvalidParameterValue {
102            invalid_values: values.to_vec(),
103            reason: "expects a single value".into(),
104        }),
105    }
106}
107
108impl<V: Value + Clone> Value for Option<V> {
109    fn type_name() -> Cow<'static, str>
110    where
111        Self: Sized,
112    {
113        format!("optional {}", V::type_name()).into()
114    }
115
116    fn parse(input: VarInput) -> Result<Self, VarParseError>
117    where
118        Self: Sized,
119    {
120        let s = extract_single_value(input)?;
121        match s {
122            "" => Ok(None),
123            _ => <V as Value>::parse(VarInput::Flat(s)).map(Some),
124        }
125    }
126
127    fn box_clone(&self) -> Box<dyn Value> {
128        Box::new(self.clone())
129    }
130
131    fn format(&self) -> String {
132        match self {
133            Some(s) => s.format(),
134            None => "".to_string(),
135        }
136    }
137}
138
139impl Value for String {
140    fn type_name() -> Cow<'static, str>
141    where
142        Self: Sized,
143    {
144        "string".into()
145    }
146
147    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
148    where
149        Self: Sized,
150    {
151        let s = extract_single_value(input)?;
152        Ok(s.to_string())
153    }
154
155    fn box_clone(&self) -> Box<dyn Value> {
156        Box::new(self.clone())
157    }
158
159    fn format(&self) -> String {
160        self.to_string()
161    }
162}
163
164impl Value for Cow<'static, str> {
165    fn type_name() -> Cow<'static, str>
166    where
167        Self: Sized,
168    {
169        "string".into()
170    }
171
172    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
173    where
174        Self: Sized,
175    {
176        let s = extract_single_value(input)?;
177        Ok(s.to_string().into())
178    }
179
180    fn box_clone(&self) -> Box<dyn Value> {
181        Box::new(self.clone())
182    }
183
184    fn format(&self) -> String {
185        self.to_string()
186    }
187}
188
189impl Value for bool {
190    fn type_name() -> Cow<'static, str>
191    where
192        Self: Sized,
193    {
194        "boolean".into()
195    }
196
197    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
198    where
199        Self: Sized,
200    {
201        let s = extract_single_value(input)?;
202        match s {
203            "t" | "true" | "on" => Ok(true),
204            "f" | "false" | "off" => Ok(false),
205            _ => Err(VarParseError::InvalidParameterType),
206        }
207    }
208
209    fn box_clone(&self) -> Box<dyn Value> {
210        Box::new(self.clone())
211    }
212
213    fn format(&self) -> String {
214        match self {
215            true => "on".into(),
216            false => "off".into(),
217        }
218    }
219}
220
221impl Value for Option<CheckedTimestamp<DateTime<Utc>>> {
222    fn type_name() -> Cow<'static, str>
223    where
224        Self: Sized,
225    {
226        "timestamptz".into()
227    }
228
229    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
230    where
231        Self: Sized,
232    {
233        let s = extract_single_value(input)?;
234        strconv::parse_timestamptz(s)
235            .map_err(|_| VarParseError::InvalidParameterType)
236            .map(Some)
237    }
238
239    fn box_clone(&self) -> Box<dyn Value> {
240        Box::new(self.clone())
241    }
242
243    fn format(&self) -> String {
244        self.map(|t| t.to_string()).unwrap_or_default()
245    }
246}
247
248impl Value for Numeric {
249    fn type_name() -> Cow<'static, str>
250    where
251        Self: Sized,
252    {
253        "numeric".into()
254    }
255
256    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
257    where
258        Self: Sized,
259    {
260        let s = extract_single_value(input)?;
261        s.parse::<Numeric>()
262            .map_err(|_| VarParseError::InvalidParameterType)
263    }
264
265    fn box_clone(&self) -> Box<dyn Value> {
266        Box::new(self.clone())
267    }
268
269    fn format(&self) -> String {
270        self.to_standard_notation_string()
271    }
272}
273
274const SEC_TO_MIN: u64 = 60u64;
275const SEC_TO_HOUR: u64 = 60u64 * 60;
276const SEC_TO_DAY: u64 = 60u64 * 60 * 24;
277const MICRO_TO_MILLI: u32 = 1000u32;
278
279impl Value for Duration {
280    fn type_name() -> Cow<'static, str>
281    where
282        Self: Sized,
283    {
284        "duration".into()
285    }
286
287    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
288    where
289        Self: Sized,
290    {
291        let s = extract_single_value(input)?;
292        let s = s.trim();
293        // Take all numeric values from [0..]
294        let split_pos = s
295            .chars()
296            .position(|p| !char::is_numeric(p))
297            .unwrap_or_else(|| s.chars().count());
298
299        // Error if the numeric values don't parse, i.e. there aren't any.
300        let d = s[..split_pos]
301            .parse::<u64>()
302            .map_err(|_| VarParseError::InvalidParameterType)?;
303
304        // We've already trimmed end
305        let (f, m): (fn(u64) -> Duration, u64) = match s[split_pos..].trim_start() {
306            "us" => (Duration::from_micros, 1),
307            // Default unit is milliseconds
308            "ms" | "" => (Duration::from_millis, 1),
309            "s" => (Duration::from_secs, 1),
310            "min" => (Duration::from_secs, SEC_TO_MIN),
311            "h" => (Duration::from_secs, SEC_TO_HOUR),
312            "d" => (Duration::from_secs, SEC_TO_DAY),
313            o => {
314                return Err(VarParseError::InvalidParameterValue {
315                    invalid_values: vec![o.to_string()],
316                    reason: "expected us, ms, s, min, h, or d but got {o:?}".to_string(),
317                });
318            }
319        };
320
321        let d = f(d
322            .checked_mul(m)
323            .ok_or_else(|| VarParseError::InvalidParameterValue {
324                invalid_values: vec![s.to_string()],
325                reason: "expected value to fit in u64".into(),
326            })?);
327        Ok(d)
328    }
329
330    fn box_clone(&self) -> Box<dyn Value> {
331        Box::new(self.clone())
332    }
333
334    // The strategy for formatting these strings is to find the least
335    // significant unit of time that can be printed as an integer––we know this
336    // is always possible because the input can only be an integer of a single
337    // unit of time.
338    fn format(&self) -> String {
339        let micros = self.subsec_micros();
340        if micros > 0 {
341            match micros {
342                ms if ms != 0 && ms % MICRO_TO_MILLI == 0 => {
343                    format!(
344                        "{} ms",
345                        self.as_secs() * 1000 + u64::from(ms / MICRO_TO_MILLI)
346                    )
347                }
348                us => format!("{} us", self.as_secs() * 1_000_000 + u64::from(us)),
349            }
350        } else {
351            match self.as_secs() {
352                zero if zero == u64::MAX => "0".to_string(),
353                d if d != 0 && d % SEC_TO_DAY == 0 => format!("{} d", d / SEC_TO_DAY),
354                h if h != 0 && h % SEC_TO_HOUR == 0 => format!("{} h", h / SEC_TO_HOUR),
355                m if m != 0 && m % SEC_TO_MIN == 0 => format!("{} min", m / SEC_TO_MIN),
356                s => format!("{} s", s),
357            }
358        }
359    }
360}
361
362impl Value for serde_json::Value {
363    fn type_name() -> Cow<'static, str>
364    where
365        Self: Sized,
366    {
367        "jsonb".into()
368    }
369
370    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
371    where
372        Self: Sized,
373    {
374        let s = extract_single_value(input)?;
375        serde_json::from_str(s).map_err(|_| VarParseError::InvalidParameterType)
376    }
377
378    fn box_clone(&self) -> Box<dyn Value> {
379        Box::new(self.clone())
380    }
381
382    fn format(&self) -> String {
383        self.to_string()
384    }
385}
386
387/// This style should actually be some more complex struct, but we only support this configuration
388/// of it, so this is fine for the time being.
389#[derive(Debug, Clone, Eq, PartialEq)]
390pub struct DateStyle(pub [&'static str; 2]);
391
392pub static DEFAULT_DATE_STYLE: DateStyle = DateStyle(["ISO", "MDY"]);
393
394impl Value for DateStyle {
395    fn type_name() -> Cow<'static, str>
396    where
397        Self: Sized,
398    {
399        "string list".into()
400    }
401
402    /// This impl is unlike most others because we have under-implemented its backing struct.
403    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
404    where
405        Self: Sized,
406    {
407        let input = match input {
408            VarInput::Flat(v) => mz_sql_parser::parser::split_identifier_string(v)
409                .map_err(|_| VarParseError::InvalidParameterType)?,
410            // Unlike parsing `Vec<Ident>`, we further split each element.
411            // This matches PostgreSQL.
412            VarInput::SqlSet(values) => {
413                let mut out = vec![];
414                for v in values {
415                    let idents = mz_sql_parser::parser::split_identifier_string(v)
416                        .map_err(|_| VarParseError::InvalidParameterType)?;
417                    out.extend(idents)
418                }
419                out
420            }
421        };
422
423        for input in input {
424            if !DEFAULT_DATE_STYLE
425                .0
426                .iter()
427                .any(|valid| UncasedStr::new(valid) == &input)
428            {
429                return Err(VarParseError::FixedValueParameter);
430            }
431        }
432
433        Ok(DEFAULT_DATE_STYLE.clone())
434    }
435
436    fn box_clone(&self) -> Box<dyn Value> {
437        Box::new(self.clone())
438    }
439
440    fn format(&self) -> String {
441        self.0.join(", ")
442    }
443}
444
445impl Value for Vec<Ident> {
446    fn type_name() -> Cow<'static, str>
447    where
448        Self: Sized,
449    {
450        "identifier list".into()
451    }
452
453    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
454    where
455        Self: Sized,
456    {
457        let holder;
458        let values = match input {
459            VarInput::Flat(value) => {
460                holder = mz_sql_parser::parser::split_identifier_string(value)
461                    .map_err(|_| VarParseError::InvalidParameterType)?;
462                &holder
463            }
464            // Unlike parsing `Vec<String>`, we do *not* further split each
465            // element. This matches PostgreSQL.
466            VarInput::SqlSet(values) => values,
467        };
468        let values = values
469            .iter()
470            .map(Ident::new)
471            .collect::<Result<_, _>>()
472            .map_err(|e| VarParseError::InvalidParameterValue {
473                invalid_values: values.to_vec(),
474                reason: e.to_string(),
475            })?;
476        Ok(values)
477    }
478
479    fn box_clone(&self) -> Box<dyn Value> {
480        Box::new(self.clone())
481    }
482
483    fn format(&self) -> String {
484        self.iter().map(|ident| ident.to_string()).join(", ")
485    }
486}
487
488impl Value for Vec<IpNet> {
489    fn type_name() -> Cow<'static, str>
490    where
491        Self: Sized,
492    {
493        "CIDR list".into()
494    }
495
496    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
497    where
498        Self: Sized,
499    {
500        let values = input.to_vec();
501        let values: Vec<IpNet> = values
502            .iter()
503            .flat_map(|i| i.split(','))
504            .map(|d| IpNet::from_str(d.trim()))
505            .collect::<Result<_, _>>()
506            .map_err(|e| VarParseError::InvalidParameterValue {
507                invalid_values: values,
508                reason: e.to_string(),
509            })?;
510        Ok(values)
511    }
512
513    fn box_clone(&self) -> Box<dyn Value> {
514        Box::new(self.clone())
515    }
516
517    fn format(&self) -> String {
518        self.iter().map(|ident| ident.to_string()).join(", ")
519    }
520}
521
522impl Value for Vec<SerializableDirective> {
523    fn type_name() -> Cow<'static, str>
524    where
525        Self: Sized,
526    {
527        "directive list".into()
528    }
529
530    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
531    where
532        Self: Sized,
533    {
534        let values = input.to_vec();
535        let dirs: Result<_, _> = values
536            .iter()
537            .flat_map(|i| i.split(','))
538            .map(|d| SerializableDirective::from_str(d.trim()))
539            .collect();
540        dirs.map_err(|e| VarParseError::InvalidParameterValue {
541            invalid_values: values.to_vec(),
542            reason: e.to_string(),
543        })
544    }
545
546    fn box_clone(&self) -> Box<dyn Value> {
547        Box::new(self.clone())
548    }
549
550    fn format(&self) -> String {
551        self.iter().map(|d| d.to_string()).join(", ")
552    }
553}
554
555// This unorthodox design lets us escape complex errors from value parsing.
556#[derive(Clone, Debug, Eq, PartialEq)]
557pub struct Failpoints;
558
559impl Value for Failpoints {
560    fn type_name() -> Cow<'static, str>
561    where
562        Self: Sized,
563    {
564        "failpoints config".into()
565    }
566
567    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
568    where
569        Self: Sized,
570    {
571        let values = input.to_vec();
572        for mut cfg in values.iter().map(|v| v.trim().split(';')).flatten() {
573            cfg = cfg.trim();
574            if cfg.is_empty() {
575                continue;
576            }
577            let mut splits = cfg.splitn(2, '=');
578            let failpoint = splits
579                .next()
580                .ok_or_else(|| VarParseError::InvalidParameterValue {
581                    invalid_values: input.to_vec(),
582                    reason: "missing failpoint name".into(),
583                })?;
584            let action = splits
585                .next()
586                .ok_or_else(|| VarParseError::InvalidParameterValue {
587                    invalid_values: input.to_vec(),
588                    reason: "missing failpoint action".into(),
589                })?;
590            fail::cfg(failpoint, action).map_err(|e| VarParseError::InvalidParameterValue {
591                invalid_values: input.to_vec(),
592                reason: e.to_string(),
593            })?;
594        }
595
596        Ok(Failpoints)
597    }
598
599    fn box_clone(&self) -> Box<dyn Value> {
600        Box::new(self.clone())
601    }
602
603    fn format(&self) -> String {
604        "<omitted>".to_string()
605    }
606}
607
608/// Severity levels can used to be used to filter which messages get sent
609/// to a client.
610///
611/// The ordering of severity levels used for client-level filtering differs from the
612/// one used for server-side logging in two aspects: INFO messages are always sent,
613/// and the LOG severity is considered as below NOTICE, while it is above ERROR for
614/// server-side logs.
615#[derive(Clone, Copy, Debug, Eq, PartialEq)]
616pub enum ClientSeverity {
617    /// Sends only INFO, ERROR, FATAL and PANIC level messages.
618    Error,
619    /// Sends only WARNING, INFO, ERROR, FATAL and PANIC level messages.
620    Warning,
621    /// Sends only NOTICE, WARNING, INFO, ERROR, FATAL and PANIC level messages.
622    Notice,
623    /// Sends only LOG, NOTICE, WARNING, INFO, ERROR, FATAL and PANIC level messages.
624    Log,
625    /// Sends all messages to the client, since all DEBUG levels are treated as the same right now.
626    Debug1,
627    /// Sends all messages to the client, since all DEBUG levels are treated as the same right now.
628    Debug2,
629    /// Sends all messages to the client, since all DEBUG levels are treated as the same right now.
630    Debug3,
631    /// Sends all messages to the client, since all DEBUG levels are treated as the same right now.
632    Debug4,
633    /// Sends all messages to the client, since all DEBUG levels are treated as the same right now.
634    Debug5,
635    /// Sends only NOTICE, WARNING, INFO, ERROR, FATAL and PANIC level messages.
636    /// Not listed as a valid value, but accepted by Postgres
637    Info,
638}
639
640impl Serialize for ClientSeverity {
641    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
642    where
643        S: serde::Serializer,
644    {
645        serializer.serialize_str(self.as_str())
646    }
647}
648
649impl ClientSeverity {
650    fn as_str(&self) -> &'static str {
651        match self {
652            ClientSeverity::Error => "error",
653            ClientSeverity::Warning => "warning",
654            ClientSeverity::Notice => "notice",
655            ClientSeverity::Info => "info",
656            ClientSeverity::Log => "log",
657            ClientSeverity::Debug1 => "debug1",
658            ClientSeverity::Debug2 => "debug2",
659            ClientSeverity::Debug3 => "debug3",
660            ClientSeverity::Debug4 => "debug4",
661            ClientSeverity::Debug5 => "debug5",
662        }
663    }
664
665    fn valid_values() -> Vec<&'static str> {
666        // INFO left intentionally out, to match Postgres
667        vec![
668            ClientSeverity::Debug5.as_str(),
669            ClientSeverity::Debug4.as_str(),
670            ClientSeverity::Debug3.as_str(),
671            ClientSeverity::Debug2.as_str(),
672            ClientSeverity::Debug1.as_str(),
673            ClientSeverity::Log.as_str(),
674            ClientSeverity::Notice.as_str(),
675            ClientSeverity::Warning.as_str(),
676            ClientSeverity::Error.as_str(),
677        ]
678    }
679
680    /// Checks if a message of a given severity level should be sent to a client.
681    ///
682    /// The ordering of severity levels used for client-level filtering differs from the
683    /// one used for server-side logging in two aspects: INFO messages are always sent,
684    /// and the LOG severity is considered as below NOTICE, while it is above ERROR for
685    /// server-side logs.
686    ///
687    /// Postgres only considers the session setting after the client authentication
688    /// handshake is completed. Since this function is only called after client authentication
689    /// is done, we are not treating this case right now, but be aware if refactoring it.
690    pub fn should_output_to_client(&self, severity: &Severity) -> bool {
691        match (self, severity) {
692            // INFO messages are always sent
693            (_, Severity::Info) => true,
694            (ClientSeverity::Error, Severity::Error | Severity::Fatal | Severity::Panic) => true,
695            (
696                ClientSeverity::Warning,
697                Severity::Error | Severity::Fatal | Severity::Panic | Severity::Warning,
698            ) => true,
699            (
700                ClientSeverity::Notice,
701                Severity::Error
702                | Severity::Fatal
703                | Severity::Panic
704                | Severity::Warning
705                | Severity::Notice,
706            ) => true,
707            (
708                ClientSeverity::Info,
709                Severity::Error
710                | Severity::Fatal
711                | Severity::Panic
712                | Severity::Warning
713                | Severity::Notice,
714            ) => true,
715            (
716                ClientSeverity::Log,
717                Severity::Error
718                | Severity::Fatal
719                | Severity::Panic
720                | Severity::Warning
721                | Severity::Notice
722                | Severity::Log,
723            ) => true,
724            (
725                ClientSeverity::Debug1
726                | ClientSeverity::Debug2
727                | ClientSeverity::Debug3
728                | ClientSeverity::Debug4
729                | ClientSeverity::Debug5,
730                _,
731            ) => true,
732
733            (
734                ClientSeverity::Error,
735                Severity::Warning | Severity::Notice | Severity::Log | Severity::Debug,
736            ) => false,
737            (ClientSeverity::Warning, Severity::Notice | Severity::Log | Severity::Debug) => false,
738            (ClientSeverity::Notice, Severity::Log | Severity::Debug) => false,
739            (ClientSeverity::Info, Severity::Log | Severity::Debug) => false,
740            (ClientSeverity::Log, Severity::Debug) => false,
741        }
742    }
743}
744
745impl Value for ClientSeverity {
746    fn type_name() -> Cow<'static, str>
747    where
748        Self: Sized,
749    {
750        "string".into()
751    }
752
753    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
754    where
755        Self: Sized,
756    {
757        let s = extract_single_value(input)?;
758        let s = UncasedStr::new(s);
759
760        if s == ClientSeverity::Error.as_str() {
761            Ok(ClientSeverity::Error)
762        } else if s == ClientSeverity::Warning.as_str() {
763            Ok(ClientSeverity::Warning)
764        } else if s == ClientSeverity::Notice.as_str() {
765            Ok(ClientSeverity::Notice)
766        } else if s == ClientSeverity::Info.as_str() {
767            Ok(ClientSeverity::Info)
768        } else if s == ClientSeverity::Log.as_str() {
769            Ok(ClientSeverity::Log)
770        } else if s == ClientSeverity::Debug1.as_str() {
771            Ok(ClientSeverity::Debug1)
772        // Postgres treats `debug` as an input as equivalent to `debug2`
773        } else if s == ClientSeverity::Debug2.as_str() || s == "debug" {
774            Ok(ClientSeverity::Debug2)
775        } else if s == ClientSeverity::Debug3.as_str() {
776            Ok(ClientSeverity::Debug3)
777        } else if s == ClientSeverity::Debug4.as_str() {
778            Ok(ClientSeverity::Debug4)
779        } else if s == ClientSeverity::Debug5.as_str() {
780            Ok(ClientSeverity::Debug5)
781        } else {
782            Err(VarParseError::ConstrainedParameter {
783                invalid_values: input.to_vec(),
784                valid_values: Some(ClientSeverity::valid_values()),
785            })
786        }
787    }
788
789    fn box_clone(&self) -> Box<dyn Value> {
790        Box::new(self.clone())
791    }
792
793    fn format(&self) -> String {
794        self.as_str().into()
795    }
796}
797
798/// List of valid time zones.
799///
800/// Names are following the tz database, but only time zones equivalent
801/// to UTC±00:00 are supported.
802#[derive(Clone, Copy, Debug, Eq, PartialEq)]
803pub enum TimeZone {
804    /// UTC
805    UTC,
806    /// GMT
807    GMT,
808    /// Fixed offset from UTC, currently only "+00:00" is supported.
809    /// A string representation is kept here for compatibility with Postgres.
810    FixedOffset(&'static str),
811}
812
813impl TimeZone {
814    fn as_str(&self) -> &'static str {
815        match self {
816            TimeZone::UTC => "UTC",
817            TimeZone::GMT => "GMT",
818            TimeZone::FixedOffset(s) => s,
819        }
820    }
821}
822
823impl Value for TimeZone {
824    fn type_name() -> Cow<'static, str>
825    where
826        Self: Sized,
827    {
828        // TODO(parkmycar): It seems like we should change this?
829        "string".into()
830    }
831
832    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
833    where
834        Self: Sized,
835    {
836        let s = extract_single_value(input)?;
837        let s = UncasedStr::new(s);
838
839        if s == TimeZone::UTC.as_str() {
840            Ok(TimeZone::UTC)
841        } else if s == TimeZone::GMT.as_str() {
842            Ok(TimeZone::GMT)
843        } else if s == "+00:00" {
844            Ok(TimeZone::FixedOffset("+00:00"))
845        } else {
846            Err(VarParseError::ConstrainedParameter {
847                invalid_values: input.to_vec(),
848                valid_values: None,
849            })
850        }
851    }
852
853    fn box_clone(&self) -> Box<dyn Value> {
854        Box::new(self.clone())
855    }
856
857    fn format(&self) -> String {
858        self.as_str().into()
859    }
860}
861
862/// List of valid isolation levels.
863#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
864pub enum IsolationLevel {
865    ReadUncommitted,
866    ReadCommitted,
867    RepeatableRead,
868    Serializable,
869    /* TODO(jkosh44) Move this comment to user facing docs when this isolation level becomes available to users.
870     * The Strong Session Serializable isolation level combines the Serializable isolation level
871     * (https://jepsen.io/consistency/models/serializable) with the Sequential consistency model
872     * (https://jepsen.io/consistency/models/sequential). See
873     * http://dbmsmusings.blogspot.com/2019/06/correctness-anomalies-under.html and
874     * https://cs.uwaterloo.ca/~kmsalem/pubs/DaudjeeICDE04.pdf. Operations within a single session
875     * are linearizable, but operations across sessions are not linearizable.
876     *
877     * Operations in sessions that use Strong Session Serializable are not linearizable with
878     * operations in sessions that use Strict Serializable. For example, consider the following
879     * sequence of events in order:
880     *
881     *   1. Session s0 executes read at timestamp t0 under Strong Session Serializable.
882     *   2. Session s1 executes read at timestamp t1 under Strict Serializable.
883     *
884     * If t0 > t1, then this is not considered a consistency violation. This matches with the
885     * semantics of Serializable, which can execute queries arbitrarily in the future without
886     * violating the consistency of Strict Serializable queries.
887     *
888     * All operations within a session that use Strong Session Serializable are only
889     * linearizable within operations within the same session that also use Strong Session
890     * Serializable. For example, consider the following sequence of events in order:
891     *
892     *   1. Session s0 executes read at timestamp t0 under Strong Session Serializable.
893     *   2. Session s0 executes read at timestamp t1 under I.
894     *
895     * If I is Strong Session Serializable then t0 > t1 is guaranteed. If I is any other isolation
896     * level then t0 < t1 is not considered a consistency violation. This matches the semantics of
897     * Serializable, which can execute queries arbitrarily in the future without violating the
898     * consistency of Strict Serializable queries within the same session.
899     *
900     * The items left TODO before this is considered ready for prod are:
901     *
902     * - Add more tests.
903     * - Linearize writes to system tables under this isolation (most of these are the side effect
904     *   of some DDL).
905     */
906    StrongSessionSerializable,
907    StrictSerializable,
908}
909
910impl IsolationLevel {
911    pub fn as_str(&self) -> &'static str {
912        match self {
913            Self::ReadUncommitted => "read uncommitted",
914            Self::ReadCommitted => "read committed",
915            Self::RepeatableRead => "repeatable read",
916            Self::Serializable => "serializable",
917            Self::StrongSessionSerializable => "strong session serializable",
918            Self::StrictSerializable => "strict serializable",
919        }
920    }
921
922    fn valid_values() -> Vec<&'static str> {
923        vec![
924            Self::ReadUncommitted.as_str(),
925            Self::ReadCommitted.as_str(),
926            Self::RepeatableRead.as_str(),
927            Self::Serializable.as_str(),
928            // TODO(jkosh44) Add StrongSessionSerializable when it becomes available to users.
929            Self::StrictSerializable.as_str(),
930        ]
931    }
932}
933
934impl fmt::Display for IsolationLevel {
935    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
936        f.write_str(self.as_str())
937    }
938}
939
940impl Value for IsolationLevel {
941    fn type_name() -> Cow<'static, str>
942    where
943        Self: Sized,
944    {
945        "string".into()
946    }
947
948    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
949    where
950        Self: Sized,
951    {
952        let s = extract_single_value(input)?;
953        let s = UncasedStr::new(s);
954
955        // We don't have any optimizations for levels below Serializable,
956        // so we upgrade them all to Serializable.
957        if s == Self::ReadUncommitted.as_str()
958            || s == Self::ReadCommitted.as_str()
959            || s == Self::RepeatableRead.as_str()
960            || s == Self::Serializable.as_str()
961        {
962            Ok(Self::Serializable)
963        } else if s == Self::StrongSessionSerializable.as_str() {
964            Ok(Self::StrongSessionSerializable)
965        } else if s == Self::StrictSerializable.as_str() {
966            Ok(Self::StrictSerializable)
967        } else {
968            Err(VarParseError::ConstrainedParameter {
969                invalid_values: input.to_vec(),
970                valid_values: Some(IsolationLevel::valid_values()),
971            })
972        }
973    }
974
975    fn box_clone(&self) -> Box<dyn Value> {
976        Box::new(self.clone())
977    }
978
979    fn format(&self) -> String {
980        self.as_str().into()
981    }
982}
983
984impl From<TransactionIsolationLevel> for IsolationLevel {
985    fn from(transaction_isolation_level: TransactionIsolationLevel) -> Self {
986        match transaction_isolation_level {
987            TransactionIsolationLevel::ReadUncommitted => Self::ReadUncommitted,
988            TransactionIsolationLevel::ReadCommitted => Self::ReadCommitted,
989            TransactionIsolationLevel::RepeatableRead => Self::RepeatableRead,
990            TransactionIsolationLevel::Serializable => Self::Serializable,
991            TransactionIsolationLevel::StrongSessionSerializable => Self::StrongSessionSerializable,
992            TransactionIsolationLevel::StrictSerializable => Self::StrictSerializable,
993        }
994    }
995}
996
997impl Value for CloneableEnvFilter {
998    fn type_name() -> Cow<'static, str>
999    where
1000        Self: Sized,
1001    {
1002        "EnvFilter".into()
1003    }
1004
1005    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1006    where
1007        Self: Sized,
1008    {
1009        let s = extract_single_value(input)?;
1010        CloneableEnvFilter::from_str(s).map_err(|e| VarParseError::InvalidParameterValue {
1011            invalid_values: vec![s.to_string()],
1012            reason: e.to_string(),
1013        })
1014    }
1015
1016    fn box_clone(&self) -> Box<dyn Value> {
1017        Box::new(self.clone())
1018    }
1019
1020    fn format(&self) -> String {
1021        self.to_string()
1022    }
1023}
1024
1025#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1026pub enum ClientEncoding {
1027    Utf8,
1028}
1029
1030impl ClientEncoding {
1031    fn as_str(&self) -> &'static str {
1032        match self {
1033            ClientEncoding::Utf8 => "UTF8",
1034        }
1035    }
1036
1037    fn valid_values() -> Vec<&'static str> {
1038        vec![ClientEncoding::Utf8.as_str()]
1039    }
1040}
1041
1042impl Value for ClientEncoding {
1043    fn type_name() -> Cow<'static, str>
1044    where
1045        Self: Sized,
1046    {
1047        "string".into()
1048    }
1049
1050    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1051    where
1052        Self: Sized,
1053    {
1054        let s = extract_single_value(input)?;
1055        let s = UncasedStr::new(s);
1056        if s == Self::Utf8.as_str() {
1057            Ok(Self::Utf8)
1058        } else {
1059            Err(VarParseError::ConstrainedParameter {
1060                invalid_values: vec![s.to_string()],
1061                valid_values: Some(ClientEncoding::valid_values()),
1062            })
1063        }
1064    }
1065
1066    fn box_clone(&self) -> Box<dyn Value> {
1067        Box::new(self.clone())
1068    }
1069
1070    fn format(&self) -> String {
1071        self.as_str().to_string()
1072    }
1073}
1074
1075#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1076pub enum IntervalStyle {
1077    Postgres,
1078}
1079
1080impl IntervalStyle {
1081    fn as_str(&self) -> &'static str {
1082        match self {
1083            IntervalStyle::Postgres => "postgres",
1084        }
1085    }
1086
1087    fn valid_values() -> Vec<&'static str> {
1088        vec![IntervalStyle::Postgres.as_str()]
1089    }
1090}
1091
1092impl Value for IntervalStyle {
1093    fn type_name() -> Cow<'static, str>
1094    where
1095        Self: Sized,
1096    {
1097        "string".into()
1098    }
1099
1100    fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1101    where
1102        Self: Sized,
1103    {
1104        let s = extract_single_value(input)?;
1105        let s = UncasedStr::new(s);
1106        if s == Self::Postgres.as_str() {
1107            Ok(Self::Postgres)
1108        } else {
1109            Err(VarParseError::ConstrainedParameter {
1110                invalid_values: vec![s.to_string()],
1111                valid_values: Some(IntervalStyle::valid_values()),
1112            })
1113        }
1114    }
1115
1116    fn box_clone(&self) -> Box<dyn Value> {
1117        Box::new(self.clone())
1118    }
1119
1120    fn format(&self) -> String {
1121        self.as_str().to_string()
1122    }
1123}
1124
1125/// Macro to implement [`Value`] for simpler types, i.e. ones that already implement `FromStr` and
1126/// `ToString`.
1127///
1128/// Note: Macros can be hot garbage, if at any point folks think this is too complicated please
1129/// feel free to refactor!
1130macro_rules! impl_value_for_simple {
1131    ($t: ty, $name: literal) => {
1132        impl Value for $t {
1133            fn type_name() -> Cow<'static, str>
1134            where
1135                Self: Sized,
1136            {
1137                $name.into()
1138            }
1139
1140            fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1141            where
1142                Self: Sized,
1143            {
1144                let s = extract_single_value(input)?;
1145                s.parse::<Self>()
1146                    .map_err(|_| VarParseError::InvalidParameterType)
1147            }
1148
1149            fn box_clone(&self) -> Box<dyn Value> {
1150                Box::new(self.clone())
1151            }
1152
1153            fn format(&self) -> String {
1154                self.to_string()
1155            }
1156        }
1157    };
1158}
1159
1160impl_value_for_simple!(i32, "integer");
1161impl_value_for_simple!(u32, "unsigned integer");
1162impl_value_for_simple!(u64, "64-bit unsigned integer");
1163impl_value_for_simple!(usize, "unsigned integer");
1164impl_value_for_simple!(f64, "double-precision floating-point number");
1165
1166impl_value_for_simple!(mz_repr::Timestamp, "mz-timestamp");
1167impl_value_for_simple!(mz_repr::bytes::ByteSize, "bytes");
1168impl_value_for_simple!(CompactionStyle, "rocksdb_compaction_style");
1169impl_value_for_simple!(CompressionType, "rocksdb_compression_type");
1170
1171#[cfg(test)]
1172mod tests {
1173    use mz_ore::assert_err;
1174
1175    use super::*;
1176
1177    #[mz_ore::test]
1178    fn test_value_duration() {
1179        fn inner(t: &'static str, e: Duration, expected_format: Option<&'static str>) {
1180            let d = Duration::parse(VarInput::Flat(t)).expect("invalid duration");
1181            assert_eq!(d, e);
1182            let mut d_format = d.format();
1183            d_format.retain(|c| !c.is_whitespace());
1184            if let Some(expected) = expected_format {
1185                assert_eq!(d_format, expected);
1186            } else {
1187                assert_eq!(
1188                    t.chars().filter(|c| !c.is_whitespace()).collect::<String>(),
1189                    d_format
1190                )
1191            }
1192        }
1193        inner("1", Duration::from_millis(1), Some("1ms"));
1194        inner("0", Duration::from_secs(0), Some("0s"));
1195        inner("1ms", Duration::from_millis(1), None);
1196        inner("1000ms", Duration::from_millis(1000), Some("1s"));
1197        inner("1001ms", Duration::from_millis(1001), None);
1198        inner("1us", Duration::from_micros(1), None);
1199        inner("1000us", Duration::from_micros(1000), Some("1ms"));
1200        inner("1s", Duration::from_secs(1), None);
1201        inner("60s", Duration::from_secs(60), Some("1min"));
1202        inner("3600s", Duration::from_secs(3600), Some("1h"));
1203        inner("3660s", Duration::from_secs(3660), Some("61min"));
1204        inner("1min", Duration::from_secs(1 * SEC_TO_MIN), None);
1205        inner("60min", Duration::from_secs(60 * SEC_TO_MIN), Some("1h"));
1206        inner("1h", Duration::from_secs(1 * SEC_TO_HOUR), None);
1207        inner("24h", Duration::from_secs(24 * SEC_TO_HOUR), Some("1d"));
1208        inner("1d", Duration::from_secs(1 * SEC_TO_DAY), None);
1209        inner("2d", Duration::from_secs(2 * SEC_TO_DAY), None);
1210        inner("  1   s ", Duration::from_secs(1), None);
1211        inner("1s ", Duration::from_secs(1), None);
1212        inner("   1s", Duration::from_secs(1), None);
1213        inner("0d", Duration::from_secs(0), Some("0s"));
1214        inner(
1215            "18446744073709551615",
1216            Duration::from_millis(u64::MAX),
1217            Some("18446744073709551615ms"),
1218        );
1219        inner(
1220            "18446744073709551615 s",
1221            Duration::from_secs(u64::MAX),
1222            Some("0"),
1223        );
1224
1225        fn errs(t: &'static str) {
1226            assert_err!(Duration::parse(VarInput::Flat(t)));
1227        }
1228        errs("1 m");
1229        errs("1 sec");
1230        errs("1 min 1 s");
1231        errs("1m1s");
1232        errs("1.1");
1233        errs("1.1 min");
1234        errs("-1 s");
1235        errs("");
1236        errs("   ");
1237        errs("x");
1238        errs("s");
1239        errs("18446744073709551615 min");
1240    }
1241
1242    #[mz_ore::test]
1243    fn test_should_output_to_client() {
1244        #[rustfmt::skip]
1245        let test_cases = [
1246            (ClientSeverity::Debug1, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1247            (ClientSeverity::Debug2, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1248            (ClientSeverity::Debug3, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1249            (ClientSeverity::Debug4, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1250            (ClientSeverity::Debug5, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1251            (ClientSeverity::Log, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1252            (ClientSeverity::Log, vec![Severity::Debug], false),
1253            (ClientSeverity::Info, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1254            (ClientSeverity::Info, vec![Severity::Debug, Severity::Log], false),
1255            (ClientSeverity::Notice, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1256            (ClientSeverity::Notice, vec![Severity::Debug, Severity::Log], false),
1257            (ClientSeverity::Warning, vec![Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1258            (ClientSeverity::Warning, vec![Severity::Debug, Severity::Log, Severity::Notice], false),
1259            (ClientSeverity::Error, vec![Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1260            (ClientSeverity::Error, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning], false),
1261        ];
1262
1263        for test_case in test_cases {
1264            run_test(test_case)
1265        }
1266
1267        fn run_test(test_case: (ClientSeverity, Vec<Severity>, bool)) {
1268            let client_min_messages_setting = test_case.0;
1269            let expected = test_case.2;
1270            for message_severity in test_case.1 {
1271                assert!(
1272                    client_min_messages_setting.should_output_to_client(&message_severity)
1273                        == expected
1274                )
1275            }
1276        }
1277    }
1278}