1use 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
33pub 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 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
63impl 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
79pub 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
97fn 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 let split_pos = s
296 .chars()
297 .position(|p| !char::is_numeric(p))
298 .unwrap_or_else(|| s.chars().count());
299
300 let d = s[..split_pos]
302 .parse::<u64>()
303 .map_err(|_| VarParseError::InvalidParameterType)?;
304
305 let (f, m): (fn(u64) -> Duration, u64) = match s[split_pos..].trim_start() {
307 "us" => (Duration::from_micros, 1),
308 "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 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#[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 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 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 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#[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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
617pub enum ClientSeverity {
618 Error,
620 Warning,
622 Notice,
624 Log,
626 Debug1,
628 Debug2,
630 Debug3,
632 Debug4,
634 Debug5,
636 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 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 pub fn should_output_to_client(&self, severity: &Severity) -> bool {
692 match (self, severity) {
693 (_, 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 } 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
804pub enum TimeZone {
805 UTC,
807 GMT,
809 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 "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#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
865pub enum IsolationLevel {
866 ReadUncommitted,
867 ReadCommitted,
868 RepeatableRead,
869 Serializable,
870 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 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 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
1126macro_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}