1use 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
32pub 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 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
62impl 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
78pub 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
96fn 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 let split_pos = s
295 .chars()
296 .position(|p| !char::is_numeric(p))
297 .unwrap_or_else(|| s.chars().count());
298
299 let d = s[..split_pos]
301 .parse::<u64>()
302 .map_err(|_| VarParseError::InvalidParameterType)?;
303
304 let (f, m): (fn(u64) -> Duration, u64) = match s[split_pos..].trim_start() {
306 "us" => (Duration::from_micros, 1),
307 "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 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#[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 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 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 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#[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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
616pub enum ClientSeverity {
617 Error,
619 Warning,
621 Notice,
623 Log,
625 Debug1,
627 Debug2,
629 Debug3,
631 Debug4,
633 Debug5,
635 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 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 pub fn should_output_to_client(&self, severity: &Severity) -> bool {
691 match (self, severity) {
692 (_, 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 } 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
803pub enum TimeZone {
804 UTC,
806 GMT,
808 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 "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#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
864pub enum IsolationLevel {
865 ReadUncommitted,
866 ReadCommitted,
867 RepeatableRead,
868 Serializable,
869 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 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 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
1125macro_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}