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