mz_sql/plan/
with_options.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Provides tooling to handle `WITH` options.
11
12use std::collections::BTreeMap;
13use std::time::Duration;
14
15use mz_repr::adt::interval::Interval;
16use mz_repr::bytes::ByteSize;
17use mz_repr::{CatalogItemId, RelationVersionSelector, strconv};
18use mz_sql_parser::ast::{
19    ClusterAlterOptionValue, ClusterScheduleOptionValue, ConnectionDefaultAwsPrivatelink, Expr,
20    Ident, KafkaBroker, NetworkPolicyRuleDefinition, RefreshOptionValue, ReplicaDefinition,
21};
22use mz_storage_types::connections::string_or_secret::StringOrSecret;
23use serde::{Deserialize, Serialize};
24
25use crate::ast::{AstInfo, UnresolvedItemName, Value, WithOptionValue};
26use crate::catalog::SessionCatalog;
27use crate::names::{ResolvedDataType, ResolvedItemName};
28use crate::plan::{Aug, PlanError, literal};
29
30pub trait TryFromValue<T>: Sized {
31    fn try_from_value(v: T) -> Result<Self, PlanError>;
32
33    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<T>;
34
35    fn name() -> String;
36}
37
38pub trait ImpliedValue: Sized {
39    fn implied_value() -> Result<Self, PlanError>;
40}
41
42#[derive(Copy, Clone, Debug)]
43pub struct Secret(CatalogItemId);
44
45impl From<Secret> for CatalogItemId {
46    fn from(secret: Secret) -> Self {
47        secret.0
48    }
49}
50
51impl TryFromValue<WithOptionValue<Aug>> for Secret {
52    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
53        match StringOrSecret::try_from_value(v)? {
54            StringOrSecret::Secret(id) => Ok(Secret(id)),
55            _ => sql_bail!("must provide a secret value"),
56        }
57    }
58
59    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
60        let secret = catalog.get_item(&self.0);
61        let name = ResolvedItemName::Item {
62            id: self.0,
63            qualifiers: secret.name().qualifiers.clone(),
64            full_name: catalog.resolve_full_name(secret.name()),
65            print_id: false,
66            version: RelationVersionSelector::Latest,
67        };
68        Some(WithOptionValue::Secret(name))
69    }
70
71    fn name() -> String {
72        "secret".to_string()
73    }
74}
75
76impl ImpliedValue for Secret {
77    fn implied_value() -> Result<Self, PlanError> {
78        sql_bail!("must provide a secret value")
79    }
80}
81
82#[derive(Copy, Clone, Debug)]
83pub struct Object(CatalogItemId);
84
85impl From<Object> for CatalogItemId {
86    fn from(obj: Object) -> Self {
87        obj.0
88    }
89}
90
91impl From<&Object> for CatalogItemId {
92    fn from(obj: &Object) -> Self {
93        obj.0
94    }
95}
96
97impl TryFromValue<WithOptionValue<Aug>> for Object {
98    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
99        Ok(match v {
100            WithOptionValue::Item(ResolvedItemName::Item { id, .. }) => Object(id),
101            _ => sql_bail!("must provide an object"),
102        })
103    }
104
105    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
106        let item = catalog.get_item(&self.0);
107        let name = ResolvedItemName::Item {
108            id: self.0,
109            qualifiers: item.name().qualifiers.clone(),
110            full_name: catalog.resolve_full_name(item.name()),
111            print_id: false,
112            // TODO(alter_table): Evaluate if this is correct.
113            version: RelationVersionSelector::Latest,
114        };
115        Some(WithOptionValue::Item(name))
116    }
117
118    fn name() -> String {
119        "object reference".to_string()
120    }
121}
122
123impl ImpliedValue for Object {
124    fn implied_value() -> Result<Self, PlanError> {
125        sql_bail!("must provide an object")
126    }
127}
128
129impl TryFromValue<WithOptionValue<Aug>> for Ident {
130    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
131        Ok(match v {
132            WithOptionValue::UnresolvedItemName(UnresolvedItemName(mut inner))
133                if inner.len() == 1 =>
134            {
135                inner.remove(0)
136            }
137            WithOptionValue::Ident(inner) => inner,
138            _ => sql_bail!("must provide an unqualified identifier"),
139        })
140    }
141
142    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
143        Some(WithOptionValue::Ident(self))
144    }
145
146    fn name() -> String {
147        "identifier".to_string()
148    }
149}
150
151impl ImpliedValue for Ident {
152    fn implied_value() -> Result<Self, PlanError> {
153        sql_bail!("must provide an identifier")
154    }
155}
156
157impl TryFromValue<WithOptionValue<Aug>> for Expr<Aug> {
158    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
159        Ok(match v {
160            WithOptionValue::Expr(e) => e,
161            _ => sql_bail!("must provide an expr"),
162        })
163    }
164
165    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
166        Some(WithOptionValue::Expr(self))
167    }
168
169    fn name() -> String {
170        "expression".to_string()
171    }
172}
173
174impl ImpliedValue for Expr<Aug> {
175    fn implied_value() -> Result<Self, PlanError> {
176        sql_bail!("must provide an expression")
177    }
178}
179
180impl TryFromValue<WithOptionValue<Aug>> for UnresolvedItemName {
181    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
182        Ok(match v {
183            WithOptionValue::UnresolvedItemName(name) => name,
184            WithOptionValue::Ident(inner) => UnresolvedItemName(vec![inner]),
185            _ => sql_bail!("must provide an object name"),
186        })
187    }
188
189    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
190        Some(WithOptionValue::UnresolvedItemName(self))
191    }
192
193    fn name() -> String {
194        "object name".to_string()
195    }
196}
197
198impl ImpliedValue for UnresolvedItemName {
199    fn implied_value() -> Result<Self, PlanError> {
200        sql_bail!("must provide an object name")
201    }
202}
203
204impl TryFromValue<WithOptionValue<Aug>> for ResolvedDataType {
205    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
206        Ok(match v {
207            WithOptionValue::DataType(ty) => ty,
208            _ => sql_bail!("must provide a data type"),
209        })
210    }
211
212    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
213        Some(WithOptionValue::DataType(self))
214    }
215
216    fn name() -> String {
217        "data type".to_string()
218    }
219}
220
221impl ImpliedValue for ResolvedDataType {
222    fn implied_value() -> Result<Self, PlanError> {
223        sql_bail!("must provide a data type")
224    }
225}
226
227impl TryFromValue<WithOptionValue<Aug>> for StringOrSecret {
228    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
229        Ok(match v {
230            WithOptionValue::Secret(ResolvedItemName::Item { id, .. }) => {
231                StringOrSecret::Secret(id)
232            }
233            v => StringOrSecret::String(String::try_from_value(v)?),
234        })
235    }
236
237    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
238        Some(match self {
239            StringOrSecret::Secret(secret) => Secret(secret).try_into_value(catalog)?,
240            StringOrSecret::String(s) => s.try_into_value(catalog)?,
241        })
242    }
243
244    fn name() -> String {
245        "string or secret".to_string()
246    }
247}
248
249impl ImpliedValue for StringOrSecret {
250    fn implied_value() -> Result<Self, PlanError> {
251        sql_bail!("must provide a string or secret value")
252    }
253}
254
255impl TryFromValue<Value> for Duration {
256    fn try_from_value(v: Value) -> Result<Self, PlanError> {
257        let interval = Interval::try_from_value(v)?;
258        Ok(interval.duration()?)
259    }
260
261    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<Value> {
262        let interval = Interval::from_duration(&self)
263            .expect("planning ensured that this is convertible back to Interval");
264        interval.try_into_value(catalog)
265    }
266
267    fn name() -> String {
268        "interval".to_string()
269    }
270}
271
272impl ImpliedValue for Duration {
273    fn implied_value() -> Result<Self, PlanError> {
274        sql_bail!("must provide an interval value")
275    }
276}
277
278impl TryFromValue<Value> for ByteSize {
279    fn try_from_value(v: Value) -> Result<Self, PlanError> {
280        match v {
281            Value::Number(value) | Value::String(value) => Ok(value
282                .parse::<ByteSize>()
283                .map_err(|e| sql_err!("invalid bytes value: {e}"))?),
284            _ => sql_bail!("cannot use value as bytes"),
285        }
286    }
287
288    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
289        Some(Value::String(self.to_string()))
290    }
291
292    fn name() -> String {
293        "bytes".to_string()
294    }
295}
296
297impl ImpliedValue for ByteSize {
298    fn implied_value() -> Result<Self, PlanError> {
299        sql_bail!("must provide a value for bytes")
300    }
301}
302
303impl TryFromValue<Value> for Interval {
304    fn try_from_value(v: Value) -> Result<Self, PlanError> {
305        match v {
306            Value::Interval(value) => literal::plan_interval(&value),
307            Value::Number(value) | Value::String(value) => Ok(strconv::parse_interval(&value)?),
308            _ => sql_bail!("cannot use value as interval"),
309        }
310    }
311
312    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
313        let interval_value = literal::unplan_interval(&self);
314        Some(Value::Interval(interval_value))
315    }
316
317    fn name() -> String {
318        "interval".to_string()
319    }
320}
321
322impl ImpliedValue for Interval {
323    fn implied_value() -> Result<Self, PlanError> {
324        sql_bail!("must provide an interval value")
325    }
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
329pub struct OptionalString(pub Option<String>);
330
331impl TryFromValue<Value> for OptionalString {
332    fn try_from_value(v: Value) -> Result<Self, PlanError> {
333        Ok(match v {
334            Value::Null => Self(None),
335            v => Self(Some(String::try_from_value(v)?)),
336        })
337    }
338
339    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<Value> {
340        Some(match self.0 {
341            None => Value::Null,
342            Some(s) => s.try_into_value(catalog)?,
343        })
344    }
345
346    fn name() -> String {
347        "optional string".to_string()
348    }
349}
350
351impl ImpliedValue for OptionalString {
352    fn implied_value() -> Result<Self, PlanError> {
353        sql_bail!("must provide a string value")
354    }
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Hash, Deserialize)]
358pub struct OptionalDuration(pub Option<Duration>);
359
360impl From<Duration> for OptionalDuration {
361    fn from(i: Duration) -> OptionalDuration {
362        // An interval of 0 disables the setting.
363        let inner = if i == Duration::ZERO { None } else { Some(i) };
364        OptionalDuration(inner)
365    }
366}
367
368impl TryFromValue<Value> for OptionalDuration {
369    fn try_from_value(v: Value) -> Result<Self, PlanError> {
370        Ok(match v {
371            Value::Null => OptionalDuration(None),
372            v => Duration::try_from_value(v)?.into(),
373        })
374    }
375
376    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<Value> {
377        Some(match self.0 {
378            None => Value::Null,
379            Some(duration) => duration.try_into_value(catalog)?,
380        })
381    }
382
383    fn name() -> String {
384        "optional interval".to_string()
385    }
386}
387
388impl ImpliedValue for OptionalDuration {
389    fn implied_value() -> Result<Self, PlanError> {
390        sql_bail!("must provide an interval value")
391    }
392}
393
394impl TryFromValue<Value> for String {
395    fn try_from_value(v: Value) -> Result<Self, PlanError> {
396        match v {
397            Value::String(v) => Ok(v),
398            _ => sql_bail!("cannot use value as string"),
399        }
400    }
401
402    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
403        Some(Value::String(self))
404    }
405
406    fn name() -> String {
407        "text".to_string()
408    }
409}
410
411impl ImpliedValue for String {
412    fn implied_value() -> Result<Self, PlanError> {
413        sql_bail!("must provide a string value")
414    }
415}
416
417impl TryFromValue<Value> for bool {
418    fn try_from_value(v: Value) -> Result<Self, PlanError> {
419        match v {
420            Value::Boolean(v) => Ok(v),
421            _ => sql_bail!("cannot use value as boolean"),
422        }
423    }
424
425    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
426        Some(Value::Boolean(self))
427    }
428
429    fn name() -> String {
430        "bool".to_string()
431    }
432}
433
434impl ImpliedValue for bool {
435    fn implied_value() -> Result<Self, PlanError> {
436        Ok(true)
437    }
438}
439
440impl TryFromValue<Value> for f64 {
441    fn try_from_value(v: Value) -> Result<Self, PlanError> {
442        match v {
443            Value::Number(v) => v
444                .parse::<f64>()
445                .map_err(|e| sql_err!("invalid numeric value: {e}")),
446            _ => sql_bail!("cannot use value as number"),
447        }
448    }
449
450    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
451        Some(Value::Number(self.to_string()))
452    }
453
454    fn name() -> String {
455        "float8".to_string()
456    }
457}
458
459impl ImpliedValue for f64 {
460    fn implied_value() -> Result<Self, PlanError> {
461        sql_bail!("must provide a float value")
462    }
463}
464
465impl TryFromValue<Value> for i32 {
466    fn try_from_value(v: Value) -> Result<Self, PlanError> {
467        match v {
468            Value::Number(v) => v
469                .parse::<i32>()
470                .map_err(|e| sql_err!("invalid numeric value: {e}")),
471            _ => sql_bail!("cannot use value as number"),
472        }
473    }
474
475    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
476        Some(Value::Number(self.to_string()))
477    }
478
479    fn name() -> String {
480        "int".to_string()
481    }
482}
483
484impl ImpliedValue for i32 {
485    fn implied_value() -> Result<Self, PlanError> {
486        sql_bail!("must provide an integer value")
487    }
488}
489
490impl TryFromValue<Value> for i64 {
491    fn try_from_value(v: Value) -> Result<Self, PlanError> {
492        match v {
493            Value::Number(v) => v
494                .parse::<i64>()
495                .map_err(|e| sql_err!("invalid numeric value: {e}")),
496            _ => sql_bail!("cannot use value as number"),
497        }
498    }
499    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
500        Some(Value::Number(self.to_string()))
501    }
502    fn name() -> String {
503        "int8".to_string()
504    }
505}
506
507impl ImpliedValue for i64 {
508    fn implied_value() -> Result<Self, PlanError> {
509        sql_bail!("must provide an integer value")
510    }
511}
512
513impl TryFromValue<Value> for u16 {
514    fn try_from_value(v: Value) -> Result<Self, PlanError> {
515        match v {
516            Value::Number(v) => v
517                .parse::<u16>()
518                .map_err(|e| sql_err!("invalid numeric value: {e}")),
519            _ => sql_bail!("cannot use value as number"),
520        }
521    }
522    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
523        Some(Value::Number(self.to_string()))
524    }
525    fn name() -> String {
526        "uint2".to_string()
527    }
528}
529
530impl ImpliedValue for u16 {
531    fn implied_value() -> Result<Self, PlanError> {
532        sql_bail!("must provide an integer value")
533    }
534}
535
536impl TryFromValue<Value> for u32 {
537    fn try_from_value(v: Value) -> Result<Self, PlanError> {
538        match v {
539            Value::Number(v) => v
540                .parse::<u32>()
541                .map_err(|e| sql_err!("invalid numeric value: {e}")),
542            _ => sql_bail!("cannot use value as number"),
543        }
544    }
545    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
546        Some(Value::Number(self.to_string()))
547    }
548    fn name() -> String {
549        "uint4".to_string()
550    }
551}
552
553impl ImpliedValue for u32 {
554    fn implied_value() -> Result<Self, PlanError> {
555        sql_bail!("must provide an integer value")
556    }
557}
558
559impl TryFromValue<Value> for u64 {
560    fn try_from_value(v: Value) -> Result<Self, PlanError> {
561        match v {
562            Value::Number(v) => v
563                .parse::<u64>()
564                .map_err(|e| sql_err!("invalid unsigned numeric value: {e}")),
565            _ => sql_bail!("cannot use value as number"),
566        }
567    }
568    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<Value> {
569        Some(Value::Number(self.to_string()))
570    }
571    fn name() -> String {
572        "uint8".to_string()
573    }
574}
575
576impl ImpliedValue for u64 {
577    fn implied_value() -> Result<Self, PlanError> {
578        sql_bail!("must provide an unsigned integer value")
579    }
580}
581
582impl<V: TryFromValue<WithOptionValue<Aug>>> TryFromValue<WithOptionValue<Aug>> for Vec<V> {
583    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
584        match v {
585            WithOptionValue::Sequence(a) => {
586                let mut out = Vec::with_capacity(a.len());
587                for i in a {
588                    out.push(
589                        V::try_from_value(i)
590                            .map_err(|_| anyhow::anyhow!("cannot use value in array"))?,
591                    )
592                }
593                Ok(out)
594            }
595            _ => sql_bail!("cannot use value as array"),
596        }
597    }
598
599    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
600        Some(WithOptionValue::Sequence(
601            self.into_iter()
602                .map(|v| v.try_into_value(catalog))
603                .collect::<Option<_>>()?,
604        ))
605    }
606
607    fn name() -> String {
608        format!("array of {}", V::name())
609    }
610}
611
612impl<V: ImpliedValue> ImpliedValue for Vec<V> {
613    fn implied_value() -> Result<Self, PlanError> {
614        sql_bail!("must provide an array value")
615    }
616}
617
618impl<T: AstInfo, V: TryFromValue<WithOptionValue<T>>> TryFromValue<WithOptionValue<T>>
619    for Option<V>
620{
621    fn try_from_value(v: WithOptionValue<T>) -> Result<Self, PlanError> {
622        Ok(Some(V::try_from_value(v)?))
623    }
624
625    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<T>> {
626        match self {
627            Some(v) => v.try_into_value(catalog),
628            None => None,
629        }
630    }
631
632    fn name() -> String {
633        format!("optional {}", V::name())
634    }
635}
636
637impl<V: ImpliedValue> ImpliedValue for Option<V> {
638    fn implied_value() -> Result<Self, PlanError> {
639        Ok(Some(V::implied_value()?))
640    }
641}
642
643impl<V: TryFromValue<Value>, T: AstInfo + std::fmt::Debug> TryFromValue<WithOptionValue<T>> for V {
644    fn try_from_value(v: WithOptionValue<T>) -> Result<Self, PlanError> {
645        match v {
646            WithOptionValue::Value(v) => V::try_from_value(v),
647            WithOptionValue::UnresolvedItemName(UnresolvedItemName(mut inner))
648                if inner.len() == 1 =>
649            {
650                V::try_from_value(Value::String(inner.remove(0).into_string()))
651            }
652            WithOptionValue::Ident(v) => V::try_from_value(Value::String(v.into_string())),
653            WithOptionValue::RetainHistoryFor(v) => V::try_from_value(v),
654            WithOptionValue::Sequence(_)
655            | WithOptionValue::Map(_)
656            | WithOptionValue::Item(_)
657            | WithOptionValue::UnresolvedItemName(_)
658            | WithOptionValue::Secret(_)
659            | WithOptionValue::DataType(_)
660            | WithOptionValue::Expr(_)
661            | WithOptionValue::ClusterReplicas(_)
662            | WithOptionValue::ConnectionKafkaBroker(_)
663            | WithOptionValue::ConnectionAwsPrivatelink(_)
664            | WithOptionValue::ClusterAlterStrategy(_)
665            | WithOptionValue::Refresh(_)
666            | WithOptionValue::ClusterScheduleOptionValue(_)
667            | WithOptionValue::NetworkPolicyRules(_) => sql_bail!(
668                "incompatible value types: cannot convert {} to {}",
669                match v {
670                    // The first few are unreachable because they are handled at the top of the outer match.
671                    WithOptionValue::Value(_) => unreachable!(),
672                    WithOptionValue::RetainHistoryFor(_) => unreachable!(),
673                    WithOptionValue::ClusterAlterStrategy(_) => "cluster alter strategy",
674                    WithOptionValue::Sequence(_) => "sequences",
675                    WithOptionValue::Map(_) => "maps",
676                    WithOptionValue::Item(_) => "object references",
677                    WithOptionValue::UnresolvedItemName(_) => "object names",
678                    WithOptionValue::Ident(_) => "identifiers",
679                    WithOptionValue::Secret(_) => "secrets",
680                    WithOptionValue::DataType(_) => "data types",
681                    WithOptionValue::Expr(_) => "exprs",
682                    WithOptionValue::ClusterReplicas(_) => "cluster replicas",
683                    WithOptionValue::ConnectionKafkaBroker(_) => "connection kafka brokers",
684                    WithOptionValue::ConnectionAwsPrivatelink(_) => "connection kafka brokers",
685                    WithOptionValue::Refresh(_) => "refresh option values",
686                    WithOptionValue::ClusterScheduleOptionValue(_) => "cluster schedule",
687                    WithOptionValue::NetworkPolicyRules(_) => "network policy rules",
688                },
689                V::name()
690            ),
691        }
692    }
693
694    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<T>> {
695        Some(WithOptionValue::Value(self.try_into_value(catalog)?))
696    }
697
698    fn name() -> String {
699        V::name()
700    }
701}
702
703impl<T, V: TryFromValue<T> + ImpliedValue> TryFromValue<Option<T>> for V {
704    fn try_from_value(v: Option<T>) -> Result<Self, PlanError> {
705        match v {
706            Some(v) => V::try_from_value(v),
707            None => V::implied_value(),
708        }
709    }
710
711    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<Option<T>> {
712        Some(Some(self.try_into_value(catalog)?))
713    }
714
715    fn name() -> String {
716        V::name()
717    }
718}
719
720impl TryFromValue<WithOptionValue<Aug>> for Vec<ReplicaDefinition<Aug>> {
721    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
722        match v {
723            WithOptionValue::ClusterReplicas(replicas) => Ok(replicas),
724            _ => sql_bail!("cannot use value as cluster replicas"),
725        }
726    }
727
728    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
729        Some(WithOptionValue::ClusterReplicas(self))
730    }
731
732    fn name() -> String {
733        "cluster replicas".to_string()
734    }
735}
736
737impl ImpliedValue for Vec<ReplicaDefinition<Aug>> {
738    fn implied_value() -> Result<Self, PlanError> {
739        sql_bail!("must provide a set of cluster replicas")
740    }
741}
742
743impl TryFromValue<WithOptionValue<Aug>> for Vec<KafkaBroker<Aug>> {
744    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
745        let mut out = vec![];
746        match v {
747            WithOptionValue::ConnectionKafkaBroker(broker) => {
748                out.push(broker);
749            }
750            WithOptionValue::Sequence(values) => {
751                for value in values {
752                    out.extend(Self::try_from_value(value)?);
753                }
754            }
755            _ => sql_bail!("cannot use value as a kafka broker"),
756        }
757        Ok(out)
758    }
759
760    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
761        Some(WithOptionValue::Sequence(
762            self.into_iter()
763                .map(WithOptionValue::ConnectionKafkaBroker)
764                .collect(),
765        ))
766    }
767
768    fn name() -> String {
769        "kafka broker".to_string()
770    }
771}
772
773impl ImpliedValue for Vec<KafkaBroker<Aug>> {
774    fn implied_value() -> Result<Self, PlanError> {
775        sql_bail!("must provide a kafka broker")
776    }
777}
778
779impl TryFromValue<WithOptionValue<Aug>> for RefreshOptionValue<Aug> {
780    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
781        if let WithOptionValue::Refresh(r) = v {
782            Ok(r)
783        } else {
784            sql_bail!("cannot use value `{}` for a refresh option", v)
785        }
786    }
787
788    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
789        Some(WithOptionValue::Refresh(self))
790    }
791
792    fn name() -> String {
793        "refresh option value".to_string()
794    }
795}
796
797impl ImpliedValue for RefreshOptionValue<Aug> {
798    fn implied_value() -> Result<Self, PlanError> {
799        sql_bail!("must provide a refresh option value")
800    }
801}
802
803impl TryFromValue<WithOptionValue<Aug>> for ConnectionDefaultAwsPrivatelink<Aug> {
804    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
805        if let WithOptionValue::ConnectionAwsPrivatelink(r) = v {
806            Ok(r)
807        } else {
808            sql_bail!("cannot use value `{}` for a privatelink", v)
809        }
810    }
811
812    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
813        Some(WithOptionValue::ConnectionAwsPrivatelink(self))
814    }
815
816    fn name() -> String {
817        "privatelink option value".to_string()
818    }
819}
820
821impl ImpliedValue for ConnectionDefaultAwsPrivatelink<Aug> {
822    fn implied_value() -> Result<Self, PlanError> {
823        sql_bail!("must provide a value")
824    }
825}
826
827impl ImpliedValue for ClusterScheduleOptionValue {
828    fn implied_value() -> Result<Self, PlanError> {
829        sql_bail!("must provide a cluster schedule option value")
830    }
831}
832
833impl ImpliedValue for ClusterAlterOptionValue<Aug> {
834    fn implied_value() -> Result<Self, PlanError> {
835        sql_bail!("must provide a value")
836    }
837}
838
839impl TryFromValue<WithOptionValue<Aug>> for ClusterScheduleOptionValue {
840    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
841        if let WithOptionValue::ClusterScheduleOptionValue(r) = v {
842            Ok(r)
843        } else {
844            sql_bail!("cannot use value `{}` for a cluster schedule", v)
845        }
846    }
847
848    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
849        Some(WithOptionValue::ClusterScheduleOptionValue(self))
850    }
851
852    fn name() -> String {
853        "cluster schedule option value".to_string()
854    }
855}
856
857impl<V: ImpliedValue> ImpliedValue for BTreeMap<String, V> {
858    fn implied_value() -> Result<Self, PlanError> {
859        sql_bail!("must provide a map of key-value pairs")
860    }
861}
862
863impl<V: TryFromValue<WithOptionValue<Aug>>> TryFromValue<WithOptionValue<Aug>>
864    for BTreeMap<String, V>
865{
866    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
867        match v {
868            WithOptionValue::Map(a) => a
869                .into_iter()
870                .map(|(k, v)| Ok((k, V::try_from_value(v)?)))
871                .collect(),
872            _ => sql_bail!("cannot use value as map"),
873        }
874    }
875
876    fn try_into_value(self, catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
877        Some(WithOptionValue::Map(
878            self.into_iter()
879                .map(|(k, v)| {
880                    let v = v.try_into_value(catalog);
881                    v.map(|v| (k, v))
882                })
883                .collect::<Option<_>>()?,
884        ))
885    }
886
887    fn name() -> String {
888        format!("map of string to {}", V::name())
889    }
890}
891
892impl TryFromValue<WithOptionValue<Aug>> for ClusterAlterOptionValue<Aug> {
893    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
894        if let WithOptionValue::ClusterAlterStrategy(r) = v {
895            Ok(r)
896        } else {
897            sql_bail!("cannot use value `{}` for a cluster alter strategy", v)
898        }
899    }
900
901    fn name() -> String {
902        "cluster alter strategyoption value".to_string()
903    }
904
905    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
906        Some(WithOptionValue::ClusterAlterStrategy(self))
907    }
908}
909
910impl TryFromValue<WithOptionValue<Aug>> for Vec<NetworkPolicyRuleDefinition<Aug>> {
911    fn try_from_value(v: WithOptionValue<Aug>) -> Result<Self, PlanError> {
912        match v {
913            WithOptionValue::NetworkPolicyRules(rules) => Ok(rules),
914            _ => sql_bail!("cannot use value as cluster replicas"),
915        }
916    }
917
918    fn try_into_value(self, _catalog: &dyn SessionCatalog) -> Option<WithOptionValue<Aug>> {
919        Some(WithOptionValue::NetworkPolicyRules(self))
920    }
921
922    fn name() -> String {
923        "network policy rules".to_string()
924    }
925}
926
927impl ImpliedValue for Vec<NetworkPolicyRuleDefinition<Aug>> {
928    fn implied_value() -> Result<Self, PlanError> {
929        sql_bail!("must provide a set of network policy rules")
930    }
931}