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.find(|p: char| !p.is_ascii_digit()).unwrap_or(s.len());
300
301 let d = s[..split_pos]
303 .parse::<u64>()
304 .map_err(|_| VarParseError::InvalidParameterType)?;
305
306 let (f, m): (fn(u64) -> Duration, u64) = match s[split_pos..].trim_start() {
308 "us" => (Duration::from_micros, 1),
309 "ms" | "" => (Duration::from_millis, 1),
311 "s" => (Duration::from_secs, 1),
312 "min" => (Duration::from_secs, SEC_TO_MIN),
313 "h" => (Duration::from_secs, SEC_TO_HOUR),
314 "d" => (Duration::from_secs, SEC_TO_DAY),
315 o => {
316 return Err(VarParseError::InvalidParameterValue {
317 invalid_values: vec![o.to_string()],
318 reason: format!("expected us, ms, s, min, h, or d but got {o:?}"),
319 });
320 }
321 };
322
323 let d = f(d
324 .checked_mul(m)
325 .ok_or_else(|| VarParseError::InvalidParameterValue {
326 invalid_values: vec![s.to_string()],
327 reason: "expected value to fit in u64".into(),
328 })?);
329 Ok(d)
330 }
331
332 fn box_clone(&self) -> Box<dyn Value> {
333 Box::new(self.clone())
334 }
335
336 fn format(&self) -> String {
341 let micros = self.subsec_micros();
342 if micros > 0 {
343 match micros {
344 ms if ms != 0 && ms % MICRO_TO_MILLI == 0 => {
345 format!(
346 "{} ms",
347 self.as_secs() * 1000 + u64::from(ms / MICRO_TO_MILLI)
348 )
349 }
350 us => format!("{} us", self.as_secs() * 1_000_000 + u64::from(us)),
351 }
352 } else {
353 match self.as_secs() {
354 zero if zero == u64::MAX => "0".to_string(),
355 d if d != 0 && d % SEC_TO_DAY == 0 => format!("{} d", d / SEC_TO_DAY),
356 h if h != 0 && h % SEC_TO_HOUR == 0 => format!("{} h", h / SEC_TO_HOUR),
357 m if m != 0 && m % SEC_TO_MIN == 0 => format!("{} min", m / SEC_TO_MIN),
358 s => format!("{} s", s),
359 }
360 }
361 }
362}
363
364impl Value for serde_json::Value {
365 fn type_name() -> Cow<'static, str>
366 where
367 Self: Sized,
368 {
369 "jsonb".into()
370 }
371
372 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
373 where
374 Self: Sized,
375 {
376 let s = extract_single_value(input)?;
377 serde_json::from_str(s).map_err(|_| VarParseError::InvalidParameterType)
378 }
379
380 fn box_clone(&self) -> Box<dyn Value> {
381 Box::new(self.clone())
382 }
383
384 fn format(&self) -> String {
385 self.to_string()
386 }
387}
388
389#[derive(Debug, Clone, Eq, PartialEq)]
392pub struct DateStyle(pub [&'static str; 2]);
393
394pub static DEFAULT_DATE_STYLE: DateStyle = DateStyle(["ISO", "MDY"]);
395
396impl Value for DateStyle {
397 fn type_name() -> Cow<'static, str>
398 where
399 Self: Sized,
400 {
401 "string list".into()
402 }
403
404 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
406 where
407 Self: Sized,
408 {
409 let input = match input {
410 VarInput::Flat(v) => mz_sql_parser::parser::split_identifier_string(v)
411 .map_err(|_| VarParseError::InvalidParameterType)?,
412 VarInput::SqlSet(values) => {
415 let mut out = vec![];
416 for v in values {
417 let idents = mz_sql_parser::parser::split_identifier_string(v)
418 .map_err(|_| VarParseError::InvalidParameterType)?;
419 out.extend(idents)
420 }
421 out
422 }
423 };
424
425 for input in input {
426 if !DEFAULT_DATE_STYLE
427 .0
428 .iter()
429 .any(|valid| UncasedStr::new(valid) == &input)
430 {
431 return Err(VarParseError::FixedValueParameter);
432 }
433 }
434
435 Ok(DEFAULT_DATE_STYLE.clone())
436 }
437
438 fn box_clone(&self) -> Box<dyn Value> {
439 Box::new(self.clone())
440 }
441
442 fn format(&self) -> String {
443 self.0.join(", ")
444 }
445}
446
447impl Value for Vec<Ident> {
448 fn type_name() -> Cow<'static, str>
449 where
450 Self: Sized,
451 {
452 "identifier list".into()
453 }
454
455 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
456 where
457 Self: Sized,
458 {
459 let holder;
460 let values = match input {
461 VarInput::Flat(value) => {
462 holder = mz_sql_parser::parser::split_identifier_string(value)
463 .map_err(|_| VarParseError::InvalidParameterType)?;
464 &holder
465 }
466 VarInput::SqlSet(values) => values,
469 };
470 if values.iter().any(|v| v.is_empty()) {
474 if values.len() == 1 {
475 return Ok(vec![]);
476 }
477 return Err(VarParseError::InvalidParameterValue {
478 invalid_values: values.to_vec(),
479 reason: "empty identifier".into(),
480 });
481 }
482 let values = values
483 .iter()
484 .map(Ident::new)
485 .collect::<Result<_, _>>()
486 .map_err(|e| VarParseError::InvalidParameterValue {
487 invalid_values: values.to_vec(),
488 reason: e.to_string(),
489 })?;
490 Ok(values)
491 }
492
493 fn box_clone(&self) -> Box<dyn Value> {
494 Box::new(self.clone())
495 }
496
497 fn format(&self) -> String {
498 self.iter().map(|ident| ident.to_string()).join(", ")
499 }
500}
501
502impl Value for Vec<IpNet> {
503 fn type_name() -> Cow<'static, str>
504 where
505 Self: Sized,
506 {
507 "CIDR list".into()
508 }
509
510 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
511 where
512 Self: Sized,
513 {
514 let values = input.to_vec();
515 let values: Vec<IpNet> = values
516 .iter()
517 .flat_map(|i| i.split(','))
518 .map(|d| IpNet::from_str(d.trim()))
519 .collect::<Result<_, _>>()
520 .map_err(|e| VarParseError::InvalidParameterValue {
521 invalid_values: values,
522 reason: e.to_string(),
523 })?;
524 Ok(values)
525 }
526
527 fn box_clone(&self) -> Box<dyn Value> {
528 Box::new(self.clone())
529 }
530
531 fn format(&self) -> String {
532 self.iter().map(|ident| ident.to_string()).join(", ")
533 }
534}
535
536impl Value for Vec<SerializableDirective> {
537 fn type_name() -> Cow<'static, str>
538 where
539 Self: Sized,
540 {
541 "directive list".into()
542 }
543
544 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
545 where
546 Self: Sized,
547 {
548 let values = input.to_vec();
549 let dirs: Result<_, _> = values
550 .iter()
551 .flat_map(|i| i.split(','))
552 .map(|d| SerializableDirective::from_str(d.trim()))
553 .collect();
554 dirs.map_err(|e| VarParseError::InvalidParameterValue {
555 invalid_values: values.to_vec(),
556 reason: e.to_string(),
557 })
558 }
559
560 fn box_clone(&self) -> Box<dyn Value> {
561 Box::new(self.clone())
562 }
563
564 fn format(&self) -> String {
565 self.iter().map(|d| d.to_string()).join(", ")
566 }
567}
568
569#[derive(Clone, Debug, Eq, PartialEq)]
571pub struct Failpoints;
572
573impl Value for Failpoints {
574 fn type_name() -> Cow<'static, str>
575 where
576 Self: Sized,
577 {
578 "failpoints config".into()
579 }
580
581 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
582 where
583 Self: Sized,
584 {
585 let values = input.to_vec();
586 for mut cfg in values.iter().map(|v| v.trim().split(';')).flatten() {
587 cfg = cfg.trim();
588 if cfg.is_empty() {
589 continue;
590 }
591 let mut splits = cfg.splitn(2, '=');
592 let failpoint = splits
593 .next()
594 .ok_or_else(|| VarParseError::InvalidParameterValue {
595 invalid_values: input.to_vec(),
596 reason: "missing failpoint name".into(),
597 })?;
598 let action = splits
599 .next()
600 .ok_or_else(|| VarParseError::InvalidParameterValue {
601 invalid_values: input.to_vec(),
602 reason: "missing failpoint action".into(),
603 })?;
604 fail::cfg(failpoint, action).map_err(|e| VarParseError::InvalidParameterValue {
605 invalid_values: input.to_vec(),
606 reason: e.to_string(),
607 })?;
608 }
609
610 Ok(Failpoints)
611 }
612
613 fn box_clone(&self) -> Box<dyn Value> {
614 Box::new(self.clone())
615 }
616
617 fn format(&self) -> String {
618 "<omitted>".to_string()
619 }
620}
621
622#[derive(Clone, Copy, Debug, Eq, PartialEq)]
630pub enum ClientSeverity {
631 Error,
633 Warning,
635 Notice,
637 Log,
639 Debug1,
641 Debug2,
643 Debug3,
645 Debug4,
647 Debug5,
649 Info,
652}
653
654impl Serialize for ClientSeverity {
655 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
656 where
657 S: serde::Serializer,
658 {
659 serializer.serialize_str(self.as_str())
660 }
661}
662
663impl ClientSeverity {
664 fn as_str(&self) -> &'static str {
665 match self {
666 ClientSeverity::Error => "error",
667 ClientSeverity::Warning => "warning",
668 ClientSeverity::Notice => "notice",
669 ClientSeverity::Info => "info",
670 ClientSeverity::Log => "log",
671 ClientSeverity::Debug1 => "debug1",
672 ClientSeverity::Debug2 => "debug2",
673 ClientSeverity::Debug3 => "debug3",
674 ClientSeverity::Debug4 => "debug4",
675 ClientSeverity::Debug5 => "debug5",
676 }
677 }
678
679 fn valid_values() -> Vec<&'static str> {
680 vec![
682 ClientSeverity::Debug5.as_str(),
683 ClientSeverity::Debug4.as_str(),
684 ClientSeverity::Debug3.as_str(),
685 ClientSeverity::Debug2.as_str(),
686 ClientSeverity::Debug1.as_str(),
687 ClientSeverity::Log.as_str(),
688 ClientSeverity::Notice.as_str(),
689 ClientSeverity::Warning.as_str(),
690 ClientSeverity::Error.as_str(),
691 ]
692 }
693
694 pub fn should_output_to_client(&self, severity: &Severity) -> bool {
705 match (self, severity) {
706 (_, Severity::Info) => true,
708 (ClientSeverity::Error, Severity::Error | Severity::Fatal | Severity::Panic) => true,
709 (
710 ClientSeverity::Warning,
711 Severity::Error | Severity::Fatal | Severity::Panic | Severity::Warning,
712 ) => true,
713 (
714 ClientSeverity::Notice,
715 Severity::Error
716 | Severity::Fatal
717 | Severity::Panic
718 | Severity::Warning
719 | Severity::Notice,
720 ) => true,
721 (
722 ClientSeverity::Info,
723 Severity::Error
724 | Severity::Fatal
725 | Severity::Panic
726 | Severity::Warning
727 | Severity::Notice,
728 ) => true,
729 (
730 ClientSeverity::Log,
731 Severity::Error
732 | Severity::Fatal
733 | Severity::Panic
734 | Severity::Warning
735 | Severity::Notice
736 | Severity::Log,
737 ) => true,
738 (
739 ClientSeverity::Debug1
740 | ClientSeverity::Debug2
741 | ClientSeverity::Debug3
742 | ClientSeverity::Debug4
743 | ClientSeverity::Debug5,
744 _,
745 ) => true,
746
747 (
748 ClientSeverity::Error,
749 Severity::Warning | Severity::Notice | Severity::Log | Severity::Debug,
750 ) => false,
751 (ClientSeverity::Warning, Severity::Notice | Severity::Log | Severity::Debug) => false,
752 (ClientSeverity::Notice, Severity::Log | Severity::Debug) => false,
753 (ClientSeverity::Info, Severity::Log | Severity::Debug) => false,
754 (ClientSeverity::Log, Severity::Debug) => false,
755 }
756 }
757}
758
759impl Value for ClientSeverity {
760 fn type_name() -> Cow<'static, str>
761 where
762 Self: Sized,
763 {
764 "string".into()
765 }
766
767 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
768 where
769 Self: Sized,
770 {
771 let s = extract_single_value(input)?;
772 let s = UncasedStr::new(s);
773
774 if s == ClientSeverity::Error.as_str() {
775 Ok(ClientSeverity::Error)
776 } else if s == ClientSeverity::Warning.as_str() {
777 Ok(ClientSeverity::Warning)
778 } else if s == ClientSeverity::Notice.as_str() {
779 Ok(ClientSeverity::Notice)
780 } else if s == ClientSeverity::Info.as_str() {
781 Ok(ClientSeverity::Info)
782 } else if s == ClientSeverity::Log.as_str() {
783 Ok(ClientSeverity::Log)
784 } else if s == ClientSeverity::Debug1.as_str() {
785 Ok(ClientSeverity::Debug1)
786 } else if s == ClientSeverity::Debug2.as_str() || s == "debug" {
788 Ok(ClientSeverity::Debug2)
789 } else if s == ClientSeverity::Debug3.as_str() {
790 Ok(ClientSeverity::Debug3)
791 } else if s == ClientSeverity::Debug4.as_str() {
792 Ok(ClientSeverity::Debug4)
793 } else if s == ClientSeverity::Debug5.as_str() {
794 Ok(ClientSeverity::Debug5)
795 } else {
796 Err(VarParseError::ConstrainedParameter {
797 invalid_values: input.to_vec(),
798 valid_values: Some(ClientSeverity::valid_values()),
799 })
800 }
801 }
802
803 fn box_clone(&self) -> Box<dyn Value> {
804 Box::new(self.clone())
805 }
806
807 fn format(&self) -> String {
808 self.as_str().into()
809 }
810}
811
812#[derive(Clone, Copy, Debug, Eq, PartialEq)]
817pub enum TimeZone {
818 UTC,
820 GMT,
822 FixedOffset(&'static str),
825}
826
827impl TimeZone {
828 fn as_str(&self) -> &'static str {
829 match self {
830 TimeZone::UTC => "UTC",
831 TimeZone::GMT => "GMT",
832 TimeZone::FixedOffset(s) => s,
833 }
834 }
835}
836
837impl Value for TimeZone {
838 fn type_name() -> Cow<'static, str>
839 where
840 Self: Sized,
841 {
842 "string".into()
844 }
845
846 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
847 where
848 Self: Sized,
849 {
850 let s = extract_single_value(input)?;
851 let s = UncasedStr::new(s);
852
853 if s == TimeZone::UTC.as_str() {
854 Ok(TimeZone::UTC)
855 } else if s == TimeZone::GMT.as_str() {
856 Ok(TimeZone::GMT)
857 } else if s == "+00:00" {
858 Ok(TimeZone::FixedOffset("+00:00"))
859 } else {
860 Err(VarParseError::ConstrainedParameter {
861 invalid_values: input.to_vec(),
862 valid_values: None,
863 })
864 }
865 }
866
867 fn box_clone(&self) -> Box<dyn Value> {
868 Box::new(self.clone())
869 }
870
871 fn format(&self) -> String {
872 self.as_str().into()
873 }
874}
875
876#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
878pub enum IsolationLevel {
879 ReadUncommitted,
880 ReadCommitted,
881 RepeatableRead,
882 Serializable,
883 StrongSessionSerializable,
921 StrictSerializable,
922 BoundedStaleness(std::time::Duration),
927}
928
929impl IsolationLevel {
930 const READ_UNCOMMITTED: &'static str = "read uncommitted";
931 const READ_COMMITTED: &'static str = "read committed";
932 const REPEATABLE_READ: &'static str = "repeatable read";
933 const SERIALIZABLE: &'static str = "serializable";
934 const STRONG_SESSION_SERIALIZABLE: &'static str = "strong session serializable";
935 const STRICT_SERIALIZABLE: &'static str = "strict serializable";
936 const BOUNDED_STALENESS: &'static str = "bounded staleness";
937 const BOUNDED_STALENESS_HINT: &'static str = "bounded staleness <duration>";
938
939 pub fn is_bounded_staleness(&self) -> bool {
941 matches!(self, Self::BoundedStaleness(_))
942 }
943
944 pub fn as_variant_str(&self) -> &'static str {
948 match self {
949 Self::ReadUncommitted => Self::READ_UNCOMMITTED,
950 Self::ReadCommitted => Self::READ_COMMITTED,
951 Self::RepeatableRead => Self::REPEATABLE_READ,
952 Self::Serializable => Self::SERIALIZABLE,
953 Self::StrongSessionSerializable => Self::STRONG_SESSION_SERIALIZABLE,
954 Self::StrictSerializable => Self::STRICT_SERIALIZABLE,
955 Self::BoundedStaleness(_) => Self::BOUNDED_STALENESS,
956 }
957 }
958
959 fn valid_values() -> Vec<&'static str> {
960 vec![
961 Self::READ_UNCOMMITTED,
962 Self::READ_COMMITTED,
963 Self::REPEATABLE_READ,
964 Self::SERIALIZABLE,
965 Self::STRICT_SERIALIZABLE,
967 Self::BOUNDED_STALENESS_HINT,
968 ]
969 }
970}
971
972impl fmt::Display for IsolationLevel {
973 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
974 match self {
975 Self::BoundedStaleness(d) => write!(
976 f,
977 "{} {}",
978 Self::BOUNDED_STALENESS,
979 humantime::format_duration(*d)
980 ),
981 other => f.write_str(other.as_variant_str()),
982 }
983 }
984}
985
986impl Value for IsolationLevel {
987 fn type_name() -> Cow<'static, str>
988 where
989 Self: Sized,
990 {
991 "string".into()
992 }
993
994 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
995 where
996 Self: Sized,
997 {
998 let invalid = || VarParseError::ConstrainedParameter {
999 invalid_values: input.to_vec(),
1000 valid_values: Some(IsolationLevel::valid_values()),
1001 };
1002
1003 let s = extract_single_value(input)?;
1004 let lower = s.to_ascii_lowercase();
1005 let lower = lower.trim();
1006
1007 match lower {
1008 Self::READ_UNCOMMITTED
1010 | Self::READ_COMMITTED
1011 | Self::REPEATABLE_READ
1012 | Self::SERIALIZABLE => Ok(Self::Serializable),
1013 Self::STRONG_SESSION_SERIALIZABLE => Ok(Self::StrongSessionSerializable),
1014 Self::STRICT_SERIALIZABLE => Ok(Self::StrictSerializable),
1015 other => {
1016 let rest = other
1017 .strip_prefix(Self::BOUNDED_STALENESS)
1018 .ok_or_else(invalid)?;
1019 if !rest.starts_with(char::is_whitespace) {
1022 return Err(invalid());
1023 }
1024 let rest = rest.trim();
1025 if rest.is_empty() {
1026 return Err(invalid());
1027 }
1028 let d = humantime::parse_duration(rest).map_err(|_| invalid())?;
1029 if d < std::time::Duration::from_millis(1) {
1034 return Err(invalid());
1035 }
1036 Ok(Self::BoundedStaleness(d))
1037 }
1038 }
1039 }
1040
1041 fn box_clone(&self) -> Box<dyn Value> {
1042 Box::new(self.clone())
1043 }
1044
1045 fn format(&self) -> String {
1046 self.to_string()
1047 }
1048}
1049
1050impl From<TransactionIsolationLevel> for IsolationLevel {
1051 fn from(transaction_isolation_level: TransactionIsolationLevel) -> Self {
1052 match transaction_isolation_level {
1053 TransactionIsolationLevel::ReadUncommitted => Self::ReadUncommitted,
1054 TransactionIsolationLevel::ReadCommitted => Self::ReadCommitted,
1055 TransactionIsolationLevel::RepeatableRead => Self::RepeatableRead,
1056 TransactionIsolationLevel::Serializable => Self::Serializable,
1057 TransactionIsolationLevel::StrongSessionSerializable => Self::StrongSessionSerializable,
1058 TransactionIsolationLevel::StrictSerializable => Self::StrictSerializable,
1059 }
1060 }
1061}
1062
1063impl Value for CloneableEnvFilter {
1064 fn type_name() -> Cow<'static, str>
1065 where
1066 Self: Sized,
1067 {
1068 "EnvFilter".into()
1069 }
1070
1071 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1072 where
1073 Self: Sized,
1074 {
1075 let s = extract_single_value(input)?;
1076 CloneableEnvFilter::from_str(s).map_err(|e| VarParseError::InvalidParameterValue {
1077 invalid_values: vec![s.to_string()],
1078 reason: e.to_string(),
1079 })
1080 }
1081
1082 fn box_clone(&self) -> Box<dyn Value> {
1083 Box::new(self.clone())
1084 }
1085
1086 fn format(&self) -> String {
1087 self.to_string()
1088 }
1089}
1090
1091#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1092pub enum ClientEncoding {
1093 Utf8,
1094}
1095
1096impl ClientEncoding {
1097 fn as_str(&self) -> &'static str {
1098 match self {
1099 ClientEncoding::Utf8 => "UTF8",
1100 }
1101 }
1102
1103 fn valid_values() -> Vec<&'static str> {
1104 vec![ClientEncoding::Utf8.as_str()]
1105 }
1106}
1107
1108impl Value for ClientEncoding {
1109 fn type_name() -> Cow<'static, str>
1110 where
1111 Self: Sized,
1112 {
1113 "string".into()
1114 }
1115
1116 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1117 where
1118 Self: Sized,
1119 {
1120 let s = extract_single_value(input)?;
1121 let s = UncasedStr::new(s);
1122 if s == Self::Utf8.as_str() {
1123 Ok(Self::Utf8)
1124 } else {
1125 Err(VarParseError::ConstrainedParameter {
1126 invalid_values: vec![s.to_string()],
1127 valid_values: Some(ClientEncoding::valid_values()),
1128 })
1129 }
1130 }
1131
1132 fn box_clone(&self) -> Box<dyn Value> {
1133 Box::new(self.clone())
1134 }
1135
1136 fn format(&self) -> String {
1137 self.as_str().to_string()
1138 }
1139}
1140
1141#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1142pub enum IntervalStyle {
1143 Postgres,
1144}
1145
1146impl IntervalStyle {
1147 fn as_str(&self) -> &'static str {
1148 match self {
1149 IntervalStyle::Postgres => "postgres",
1150 }
1151 }
1152
1153 fn valid_values() -> Vec<&'static str> {
1154 vec![IntervalStyle::Postgres.as_str()]
1155 }
1156}
1157
1158impl Value for IntervalStyle {
1159 fn type_name() -> Cow<'static, str>
1160 where
1161 Self: Sized,
1162 {
1163 "string".into()
1164 }
1165
1166 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1167 where
1168 Self: Sized,
1169 {
1170 let s = extract_single_value(input)?;
1171 let s = UncasedStr::new(s);
1172 if s == Self::Postgres.as_str() {
1173 Ok(Self::Postgres)
1174 } else {
1175 Err(VarParseError::ConstrainedParameter {
1176 invalid_values: vec![s.to_string()],
1177 valid_values: Some(IntervalStyle::valid_values()),
1178 })
1179 }
1180 }
1181
1182 fn box_clone(&self) -> Box<dyn Value> {
1183 Box::new(self.clone())
1184 }
1185
1186 fn format(&self) -> String {
1187 self.as_str().to_string()
1188 }
1189}
1190
1191macro_rules! impl_value_for_simple {
1197 ($t: ty, $name: literal) => {
1198 impl Value for $t {
1199 fn type_name() -> Cow<'static, str>
1200 where
1201 Self: Sized,
1202 {
1203 $name.into()
1204 }
1205
1206 fn parse(input: VarInput<'_>) -> Result<Self, VarParseError>
1207 where
1208 Self: Sized,
1209 {
1210 let s = extract_single_value(input)?;
1211 s.parse::<Self>()
1212 .map_err(|_| VarParseError::InvalidParameterType)
1213 }
1214
1215 fn box_clone(&self) -> Box<dyn Value> {
1216 Box::new(self.clone())
1217 }
1218
1219 fn format(&self) -> String {
1220 self.to_string()
1221 }
1222 }
1223 };
1224}
1225
1226impl_value_for_simple!(i32, "integer");
1227impl_value_for_simple!(u32, "unsigned integer");
1228impl_value_for_simple!(u64, "64-bit unsigned integer");
1229impl_value_for_simple!(usize, "unsigned integer");
1230impl_value_for_simple!(f64, "double-precision floating-point number");
1231
1232impl_value_for_simple!(NonZeroU32, "unsigned integer");
1233
1234impl_value_for_simple!(mz_repr::Timestamp, "mz-timestamp");
1235impl_value_for_simple!(mz_repr::bytes::ByteSize, "bytes");
1236impl_value_for_simple!(CompactionStyle, "rocksdb_compaction_style");
1237impl_value_for_simple!(CompressionType, "rocksdb_compression_type");
1238
1239#[cfg(test)]
1240mod tests {
1241 use mz_ore::assert_err;
1242
1243 use super::*;
1244
1245 #[mz_ore::test]
1246 fn test_value_duration() {
1247 fn inner(t: &'static str, e: Duration, expected_format: Option<&'static str>) {
1248 let d = Duration::parse(VarInput::Flat(t)).expect("invalid duration");
1249 assert_eq!(d, e);
1250 let mut d_format = d.format();
1251 d_format.retain(|c| !c.is_whitespace());
1252 if let Some(expected) = expected_format {
1253 assert_eq!(d_format, expected);
1254 } else {
1255 assert_eq!(
1256 t.chars().filter(|c| !c.is_whitespace()).collect::<String>(),
1257 d_format
1258 )
1259 }
1260 }
1261 inner("1", Duration::from_millis(1), Some("1ms"));
1262 inner("0", Duration::from_secs(0), Some("0s"));
1263 inner("1ms", Duration::from_millis(1), None);
1264 inner("1000ms", Duration::from_millis(1000), Some("1s"));
1265 inner("1001ms", Duration::from_millis(1001), None);
1266 inner("1us", Duration::from_micros(1), None);
1267 inner("1000us", Duration::from_micros(1000), Some("1ms"));
1268 inner("1s", Duration::from_secs(1), None);
1269 inner("60s", Duration::from_secs(60), Some("1min"));
1270 inner("3600s", Duration::from_secs(3600), Some("1h"));
1271 inner("3660s", Duration::from_secs(3660), Some("61min"));
1272 inner("1min", Duration::from_secs(1 * SEC_TO_MIN), None);
1273 inner("60min", Duration::from_secs(60 * SEC_TO_MIN), Some("1h"));
1274 inner("1h", Duration::from_secs(1 * SEC_TO_HOUR), None);
1275 inner("24h", Duration::from_secs(24 * SEC_TO_HOUR), Some("1d"));
1276 inner("1d", Duration::from_secs(1 * SEC_TO_DAY), None);
1277 inner("2d", Duration::from_secs(2 * SEC_TO_DAY), None);
1278 inner(" 1 s ", Duration::from_secs(1), None);
1279 inner("1s ", Duration::from_secs(1), None);
1280 inner(" 1s", Duration::from_secs(1), None);
1281 inner("0d", Duration::from_secs(0), Some("0s"));
1282 inner(
1283 "18446744073709551615",
1284 Duration::from_millis(u64::MAX),
1285 Some("18446744073709551615ms"),
1286 );
1287 inner(
1288 "18446744073709551615 s",
1289 Duration::from_secs(u64::MAX),
1290 Some("0"),
1291 );
1292
1293 fn errs(t: &'static str) {
1294 assert_err!(Duration::parse(VarInput::Flat(t)));
1295 }
1296 errs("1 m");
1297 errs("1 sec");
1298 errs("1 min 1 s");
1299 errs("1m1s");
1300 errs("1.1");
1301 errs("1.1 min");
1302 errs("-1 s");
1303 errs("");
1304 errs(" ");
1305 errs("x");
1306 errs("s");
1307 errs("18446744073709551615 min");
1308 errs("²");
1312 errs("1²ms");
1313 errs("½");
1314 errs("1ms");
1315 }
1316
1317 #[mz_ore::test]
1318 fn test_value_ident_list() {
1319 fn sql_set(values: &[&str]) -> Result<Vec<Ident>, VarParseError> {
1320 let values: Vec<String> = values.iter().map(|v| v.to_string()).collect();
1321 Vec::<Ident>::parse(VarInput::SqlSet(&values))
1322 }
1323 fn flat(value: &str) -> Result<Vec<Ident>, VarParseError> {
1324 Vec::<Ident>::parse(VarInput::Flat(value))
1325 }
1326
1327 for input in [&[""][..], &["a", "b"], &["A b", "c\"d"]] {
1329 let parsed = sql_set(input).expect("valid input");
1330 let formatted = parsed.format();
1331 assert_eq!(
1332 flat(&formatted).ok(),
1333 Some(parsed),
1334 "{input:?} does not round-trip through {formatted:?}"
1335 );
1336 }
1337
1338 assert_eq!(sql_set(&[""]).ok(), Some(vec![]));
1339 assert_eq!(flat("''").ok(), Some(vec![]));
1340 assert_err!(sql_set(&["a", ""]));
1341 assert_err!(flat("a, ''"));
1342 }
1343
1344 #[mz_ore::test]
1345 fn test_should_output_to_client() {
1346 #[rustfmt::skip]
1347 let test_cases = [
1348 (ClientSeverity::Debug1, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1349 (ClientSeverity::Debug2, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1350 (ClientSeverity::Debug3, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1351 (ClientSeverity::Debug4, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1352 (ClientSeverity::Debug5, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1353 (ClientSeverity::Log, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1354 (ClientSeverity::Log, vec![Severity::Debug], false),
1355 (ClientSeverity::Info, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1356 (ClientSeverity::Info, vec![Severity::Debug, Severity::Log], false),
1357 (ClientSeverity::Notice, vec![Severity::Notice, Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1358 (ClientSeverity::Notice, vec![Severity::Debug, Severity::Log], false),
1359 (ClientSeverity::Warning, vec![Severity::Warning, Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1360 (ClientSeverity::Warning, vec![Severity::Debug, Severity::Log, Severity::Notice], false),
1361 (ClientSeverity::Error, vec![Severity::Error, Severity::Fatal, Severity:: Panic, Severity::Info], true),
1362 (ClientSeverity::Error, vec![Severity::Debug, Severity::Log, Severity::Notice, Severity::Warning], false),
1363 ];
1364
1365 for test_case in test_cases {
1366 run_test(test_case)
1367 }
1368
1369 fn run_test(test_case: (ClientSeverity, Vec<Severity>, bool)) {
1370 let client_min_messages_setting = test_case.0;
1371 let expected = test_case.2;
1372 for message_severity in test_case.1 {
1373 assert!(
1374 client_min_messages_setting.should_output_to_client(&message_severity)
1375 == expected
1376 )
1377 }
1378 }
1379 }
1380}
1381
1382#[cfg(test)]
1383mod bounded_staleness_tests {
1384 use super::*;
1385 use std::time::Duration;
1386
1387 fn parse_iso(s: &str) -> Result<IsolationLevel, VarParseError> {
1388 IsolationLevel::parse(VarInput::Flat(s))
1389 }
1390
1391 #[mz_ore::test]
1392 fn parses_bounded_staleness() {
1393 assert_eq!(
1394 parse_iso("bounded staleness 5s").unwrap(),
1395 IsolationLevel::BoundedStaleness(Duration::from_secs(5))
1396 );
1397 assert_eq!(
1398 parse_iso("bounded staleness 500ms").unwrap(),
1399 IsolationLevel::BoundedStaleness(Duration::from_millis(500))
1400 );
1401 assert_eq!(
1403 parse_iso("BOUNDED STALENESS 5s").unwrap(),
1404 IsolationLevel::BoundedStaleness(Duration::from_secs(5))
1405 );
1406 assert_eq!(
1408 parse_iso(" bounded staleness 5s ").unwrap(),
1409 IsolationLevel::BoundedStaleness(Duration::from_secs(5))
1410 );
1411 }
1412
1413 #[mz_ore::test]
1414 fn rejects_zero_duration() {
1415 assert!(parse_iso("bounded staleness 0s").is_err());
1416 assert!(parse_iso("bounded staleness 0ms").is_err());
1417 }
1418
1419 #[mz_ore::test]
1420 fn rejects_sub_millisecond_duration() {
1421 assert!(parse_iso("bounded staleness 1us").is_err());
1424 assert!(parse_iso("bounded staleness 999us").is_err());
1425 assert!(parse_iso("bounded staleness 999ns").is_err());
1426 }
1427
1428 #[mz_ore::test]
1429 fn parses_multi_component_duration() {
1430 let lvl = parse_iso("bounded staleness 1m30s").unwrap();
1433 assert_eq!(
1434 lvl,
1435 IsolationLevel::BoundedStaleness(Duration::from_secs(90))
1436 );
1437 let formatted = lvl.format();
1438 assert_eq!(parse_iso(&formatted).unwrap(), lvl);
1439 }
1440
1441 #[mz_ore::test]
1442 fn rejects_unparseable_duration() {
1443 assert!(parse_iso("bounded staleness banana").is_err());
1444 assert!(parse_iso("bounded staleness").is_err());
1445 assert!(parse_iso("bounded staleness5s").is_err());
1447 }
1448
1449 #[mz_ore::test]
1450 fn round_trips_format() {
1451 let lvl = IsolationLevel::BoundedStaleness(Duration::from_secs(5));
1452 assert_eq!(lvl.format(), "bounded staleness 5s");
1453 let parsed = parse_iso(&lvl.format()).unwrap();
1454 assert_eq!(parsed, lvl);
1455 }
1456
1457 #[mz_ore::test]
1458 fn accepts_long_durations() {
1459 assert!(parse_iso("bounded staleness 1h").is_ok());
1462 assert!(parse_iso("bounded staleness 24h").is_ok());
1463 }
1464}