1use std::collections::{BTreeMap, BTreeSet};
11use std::error::Error;
12use std::fmt;
13use std::num::TryFromIntError;
14
15use dec::TryFromDecimalError;
16use itertools::Itertools;
17use mz_catalog::builtin::MZ_CATALOG_SERVER_CLUSTER;
18use mz_compute_client::controller::error as compute_error;
19use mz_compute_client::controller::error::InstanceMissing;
20
21use mz_compute_types::ComputeInstanceId;
22use mz_expr::EvalError;
23use mz_ore::error::ErrorExt;
24use mz_ore::stack::RecursionLimitError;
25use mz_ore::str::StrExt;
26use mz_pgwire_common::{ErrorResponse, Severity};
27use mz_repr::adt::timestamp::TimestampError;
28use mz_repr::explain::ExplainError;
29use mz_repr::{ColumnDiff, ColumnName, KeyDiff, NotNullViolation, RelationDescDiff, Timestamp};
30use mz_sql::plan::PlanError;
31use mz_sql::rbac;
32use mz_sql::session::vars::VarError;
33use mz_storage_types::connections::ConnectionValidationError;
34use mz_storage_types::controller::StorageError;
35use mz_storage_types::errors::CollectionMissing;
36use smallvec::SmallVec;
37use timely::progress::Antichain;
38use tokio::sync::oneshot;
39use tokio_postgres::error::SqlState;
40
41use crate::coord::NetworkPolicyError;
42use crate::optimize::OptimizerError;
43use crate::peek_client::CollectionLookupError;
44
45#[derive(Debug)]
47pub enum AdapterError {
48 AbsurdSubscribeBounds {
50 as_of: mz_repr::Timestamp,
51 up_to: mz_repr::Timestamp,
52 },
53 AmbiguousSystemColumnReference,
57 Catalog(mz_catalog::memory::error::Error),
59 ChangedPlan(String),
64 DuplicateCursor(String),
66 Eval(EvalError),
68 Explain(ExplainError),
70 IdExhaustionError,
72 Internal(String),
74 IntrospectionDisabled {
76 log_names: Vec<String>,
77 },
78 InvalidLogDependency {
81 object_type: String,
82 log_names: Vec<String>,
83 },
84 InvalidClusterReplicaAz {
86 az: String,
87 expected: Vec<String>,
88 },
89 InvalidSetIsolationLevel,
91 InvalidSetCluster,
93 InvalidStorageClusterSize {
95 size: String,
96 expected: Vec<String>,
97 },
98 SourceOrSinkSizeRequired {
100 expected: Vec<String>,
101 },
102 InvalidTableMutationSelection {
104 object_name: String,
106 object_type: String,
108 },
109 ConstraintViolation(NotNullViolation),
111 CopyFormatError(String),
113 ConcurrentClusterDrop,
115 ConcurrentDependencyDrop {
117 dependency_kind: &'static str,
118 dependency_id: String,
119 },
120 CollectionUnreadable {
121 id: String,
122 },
123 NoClusterReplicasAvailable {
125 name: String,
126 is_managed: bool,
127 },
128 OperationProhibitsTransaction(String),
130 OperationRequiresTransaction(String),
132 PlanError(PlanError),
134 PreparedStatementExists(String),
136 ParseError(mz_sql_parser::parser::ParserStatementError),
138 ReadOnlyTransaction,
140 ReadWriteUnavailable,
142 RecursionLimit(RecursionLimitError),
144 RelationOutsideTimeDomain {
147 relations: Vec<String>,
148 names: Vec<String>,
149 },
150 ResourceExhaustion {
152 resource_type: String,
153 limit_name: String,
154 desired: String,
155 limit: String,
156 current: String,
157 },
158 ResultSize(String),
160 SafeModeViolation(String),
162 WrongSetOfLocks,
164 StatementTimeout,
168 Canceled,
170 IdleInTransactionSessionTimeout,
172 SubscribeOnlyTransaction,
174 Optimizer(OptimizerError),
176 UnallowedOnCluster {
178 depends_on: SmallVec<[String; 2]>,
179 cluster: String,
180 },
181 Unauthorized(rbac::UnauthorizedError),
183 UnknownCursor(String),
185 UnknownLoginRole(String),
187 UnknownPreparedStatement(String),
188 UnknownClusterReplica {
190 cluster_name: String,
191 replica_name: String,
192 },
193 UnrecognizedConfigurationParam(String),
195 Unstructured(anyhow::Error),
199 Unsupported(&'static str),
201 UnavailableFeature {
205 feature: String,
206 docs: Option<String>,
207 },
208 UntargetedLogRead {
210 log_names: Vec<String>,
211 },
212 WriteOnlyTransaction,
214 SingleStatementTransaction,
216 DDLOnlyTransaction,
218 DDLTransactionRace,
220 Storage(mz_storage_types::controller::StorageError<mz_repr::Timestamp>),
222 Compute(anyhow::Error),
224 Orchestrator(anyhow::Error),
226 DependentObject(BTreeMap<String, Vec<String>>),
230 InvalidAlter(&'static str, PlanError),
233 ConnectionValidation(ConnectionValidationError),
235 MaterializedViewWouldNeverRefresh(Timestamp, Timestamp),
239 InputNotReadableAtRefreshAtTime(Timestamp, Antichain<Timestamp>),
242 RtrTimeout(String),
244 RtrDropFailure(String),
246 UnreadableSinkCollection,
248 UserSessionsDisallowed,
250 NetworkPolicyDenied(NetworkPolicyError),
252 ReadOnly,
255 AlterClusterTimeout,
256 AlterClusterWhilePendingReplicas,
257 AuthenticationError(AuthenticationError),
258 ReplacementSchemaMismatch(RelationDescDiff),
260 ReplaceMaterializedViewSealed {
262 name: String,
263 },
264 ImpossibleTimestampConstraints {
266 constraints: String,
267 },
268}
269
270#[derive(Debug, thiserror::Error)]
271pub enum AuthenticationError {
272 #[error("invalid credentials")]
273 InvalidCredentials,
274 #[error("role is not allowed to login")]
275 NonLogin,
276 #[error("role does not exist")]
277 RoleNotFound,
278 #[error("password is required")]
279 PasswordRequired,
280}
281
282impl AdapterError {
283 pub fn into_response(self, severity: Severity) -> ErrorResponse {
284 ErrorResponse {
285 severity,
286 code: self.code(),
287 message: self.to_string(),
288 detail: self.detail(),
289 hint: self.hint(),
290 position: self.position(),
291 }
292 }
293
294 pub fn position(&self) -> Option<usize> {
295 match self {
296 AdapterError::ParseError(err) => Some(err.error.pos),
297 _ => None,
298 }
299 }
300
301 pub fn detail(&self) -> Option<String> {
303 match self {
304 AdapterError::AmbiguousSystemColumnReference => {
305 Some("This is a current limitation in Materialize".into())
306 }
307 AdapterError::Catalog(c) => c.detail(),
308 AdapterError::Eval(e) => e.detail(),
309 AdapterError::RelationOutsideTimeDomain { relations, names } => Some(format!(
310 "The following relations in the query are outside the transaction's time domain:\n{}\n{}",
311 relations
312 .iter()
313 .map(|r| r.quoted().to_string())
314 .collect::<Vec<_>>()
315 .join("\n"),
316 match names.is_empty() {
317 true => "No relations are available.".to_string(),
318 false => format!(
319 "Only the following relations are available:\n{}",
320 names
321 .iter()
322 .map(|name| name.quoted().to_string())
323 .collect::<Vec<_>>()
324 .join("\n")
325 ),
326 }
327 )),
328 AdapterError::SourceOrSinkSizeRequired { .. } => Some(
329 "Either specify the cluster that will maintain this object via IN CLUSTER or \
330 specify size via SIZE option."
331 .into(),
332 ),
333 AdapterError::InvalidTableMutationSelection {
334 object_name,
335 object_type,
336 } => Some(format!(
337 "{object_type} '{}' may not be used in this operation; \
338 the selection may refer to views and materialized views, but transitive \
339 dependencies must not include sources or source-export tables",
340 object_name.quoted()
341 )),
342 AdapterError::SafeModeViolation(_) => Some(
343 "The Materialize server you are connected to is running in \
344 safe mode, which limits the features that are available."
345 .into(),
346 ),
347 AdapterError::IntrospectionDisabled { log_names }
348 | AdapterError::UntargetedLogRead { log_names } => Some(format!(
349 "The query references the following log sources:\n {}",
350 log_names.join("\n "),
351 )),
352 AdapterError::InvalidLogDependency { log_names, .. } => Some(format!(
353 "The object depends on the following log sources:\n {}",
354 log_names.join("\n "),
355 )),
356 AdapterError::PlanError(e) => e.detail(),
357 AdapterError::Unauthorized(unauthorized) => unauthorized.detail(),
358 AdapterError::DependentObject(dependent_objects) => Some(
359 dependent_objects
360 .iter()
361 .map(|(role_name, err_msgs)| {
362 err_msgs
363 .iter()
364 .map(|err_msg| format!("{role_name}: {err_msg}"))
365 .join("\n")
366 })
367 .join("\n"),
368 ),
369 AdapterError::Storage(storage_error) => storage_error
370 .source()
371 .map(|source_error| source_error.to_string_with_causes()),
372 AdapterError::ReadOnlyTransaction => Some(
373 "SELECT queries cannot be combined with other query types, including SUBSCRIBE."
374 .into(),
375 ),
376 AdapterError::InvalidAlter(_, e) => e.detail(),
377 AdapterError::Optimizer(e) => e.detail(),
378 AdapterError::ConnectionValidation(e) => e.detail(),
379 AdapterError::MaterializedViewWouldNeverRefresh(last_refresh, earliest_possible) => {
380 Some(format!(
381 "The specified last refresh is at {}, while the earliest possible time to compute the materialized \
382 view is {}.",
383 last_refresh, earliest_possible,
384 ))
385 }
386 AdapterError::UnallowedOnCluster { cluster, .. } => {
387 (cluster == MZ_CATALOG_SERVER_CLUSTER.name).then(|| {
388 format!(
389 "The transaction is executing on the \
390 {cluster} cluster, maybe having been routed \
391 there by the first statement in the transaction."
392 )
393 })
394 }
395 AdapterError::InputNotReadableAtRefreshAtTime(oracle_read_ts, least_valid_read) => {
396 Some(format!(
397 "The requested REFRESH AT time is {}, \
398 but not all input collections are readable earlier than [{}].",
399 oracle_read_ts,
400 if least_valid_read.len() == 1 {
401 format!(
402 "{}",
403 least_valid_read
404 .as_option()
405 .expect("antichain contains exactly 1 timestamp")
406 )
407 } else {
408 format!("{:?}", least_valid_read)
410 }
411 ))
412 }
413 AdapterError::RtrTimeout(name) => Some(format!(
414 "{name} failed to ingest data up to the real-time recency point"
415 )),
416 AdapterError::RtrDropFailure(name) => Some(format!(
417 "{name} dropped before ingesting data to the real-time recency point"
418 )),
419 AdapterError::UserSessionsDisallowed => {
420 Some("Your organization has been blocked. Please contact support.".to_string())
421 }
422 AdapterError::NetworkPolicyDenied(reason) => Some(format!("{reason}.")),
423 AdapterError::ReplacementSchemaMismatch(diff) => {
424 let mut lines: Vec<_> = diff.column_diffs.iter().map(|(idx, diff)| {
425 let pos = idx + 1;
426 match diff {
427 ColumnDiff::Missing { name } => {
428 let name = name.as_str().quoted();
429 format!("missing column {name} at position {pos}")
430 }
431 ColumnDiff::Extra { name } => {
432 let name = name.as_str().quoted();
433 format!("extra column {name} at position {pos}")
434 }
435 ColumnDiff::TypeMismatch { name, left, right } => {
436 let name = name.as_str().quoted();
437 format!("column {name} at position {pos}: type mismatch (target: {left:?}, replacement: {right:?})")
438 }
439 ColumnDiff::NullabilityMismatch { name, left, right } => {
440 let name = name.as_str().quoted();
441 let left = if *left { "NULL" } else { "NOT NULL" };
442 let right = if *right { "NULL" } else { "NOT NULL" };
443 format!("column {name} at position {pos}: nullability mismatch (target: {left}, replacement: {right})")
444 }
445 ColumnDiff::NameMismatch { left, right } => {
446 let left = left.as_str().quoted();
447 let right = right.as_str().quoted();
448 format!("column at position {pos}: name mismatch (target: {left}, replacement: {right})")
449 }
450 }
451 }).collect();
452
453 if let Some(KeyDiff { left, right }) = &diff.key_diff {
454 let format_keys = |keys: &BTreeSet<Vec<ColumnName>>| {
455 if keys.is_empty() {
456 "(none)".to_string()
457 } else {
458 keys.iter()
459 .map(|key| {
460 let cols = key.iter().map(|c| c.as_str()).join(", ");
461 format!("{{{cols}}}")
462 })
463 .join(", ")
464 }
465 };
466 lines.push(format!(
467 "keys differ (target: {}, replacement: {})",
468 format_keys(left),
469 format_keys(right)
470 ));
471 }
472 Some(lines.join("\n"))
473 }
474 AdapterError::ReplaceMaterializedViewSealed { .. } => Some(
475 "The materialized view has already computed its output until the end of time, \
476 so replacing its definition would have no effect."
477 .into(),
478 ),
479 AdapterError::ImpossibleTimestampConstraints { constraints } => {
480 Some(format!("Constraints:\n{}", constraints))
481 }
482 _ => None,
483 }
484 }
485
486 pub fn hint(&self) -> Option<String> {
488 match self {
489 AdapterError::AmbiguousSystemColumnReference => Some(
490 "Rewrite the view to refer to all columns by name. Expand all wildcards and \
491 convert all NATURAL JOINs to USING joins."
492 .to_string(),
493 ),
494 AdapterError::Catalog(c) => c.hint(),
495 AdapterError::Eval(e) => e.hint(),
496 AdapterError::InvalidClusterReplicaAz { expected, az: _ } => {
497 Some(if expected.is_empty() {
498 "No availability zones configured; do not specify AVAILABILITY ZONE".into()
499 } else {
500 format!("Valid availability zones are: {}", expected.join(", "))
501 })
502 }
503 AdapterError::InvalidStorageClusterSize { expected, .. } => {
504 Some(format!("Valid sizes are: {}", expected.join(", ")))
505 }
506 AdapterError::SourceOrSinkSizeRequired { expected } => Some(format!(
507 "Try choosing one of the smaller sizes to start. Available sizes: {}",
508 expected.join(", ")
509 )),
510 AdapterError::NoClusterReplicasAvailable { is_managed, .. } => {
511 Some(if *is_managed {
512 "Use ALTER CLUSTER to adjust the replication factor of the cluster. \
513 Example:`ALTER CLUSTER <cluster-name> SET (REPLICATION FACTOR 1)`".into()
514 } else {
515 "Use CREATE CLUSTER REPLICA to attach cluster replicas to the cluster".into()
516 })
517 }
518 AdapterError::UntargetedLogRead { .. } => Some(
519 "Use `SET cluster_replica = <replica-name>` to target a specific replica in the \
520 active cluster. Note that subsequent queries will only be answered by \
521 the selected replica, which might reduce availability. To undo the replica \
522 selection, use `RESET cluster_replica`."
523 .into(),
524 ),
525 AdapterError::ResourceExhaustion { resource_type, .. } => Some(format!(
526 "Drop an existing {resource_type} or contact support to request a limit increase."
527 )),
528 AdapterError::StatementTimeout => Some(
529 "Consider increasing the maximum allowed statement duration for this session by \
530 setting the statement_timeout session variable. For example, `SET \
531 statement_timeout = '120s'`."
532 .into(),
533 ),
534 AdapterError::PlanError(e) => e.hint(),
535 AdapterError::UnallowedOnCluster { cluster, .. } => {
536 (cluster != MZ_CATALOG_SERVER_CLUSTER.name).then(||
537 "Use `SET CLUSTER = <cluster-name>` to change your cluster and re-run the query."
538 .to_string()
539 )
540 }
541 AdapterError::InvalidAlter(_, e) => e.hint(),
542 AdapterError::Optimizer(e) => e.hint(),
543 AdapterError::ConnectionValidation(e) => e.hint(),
544 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => Some(
545 "You can use `REFRESH AT greatest(mz_now(), <explicit timestamp>)` to refresh \
546 either at the explicitly specified timestamp, or now if the given timestamp would \
547 be in the past.".to_string()
548 ),
549 AdapterError::AlterClusterTimeout => Some(
550 "Consider increasing the timeout duration in the alter cluster statement.".into(),
551 ),
552 AdapterError::DDLTransactionRace => Some(
553 "Currently, DDL transactions fail when any other DDL happens concurrently, \
554 even on unrelated schemas/clusters.".into()
555 ),
556 AdapterError::CollectionUnreadable { .. } => Some(
557 "This could be because the collection has recently been dropped.".into()
558 ),
559 _ => None,
560 }
561 }
562
563 pub fn code(&self) -> SqlState {
564 match self {
569 AdapterError::AbsurdSubscribeBounds { .. } => SqlState::DATA_EXCEPTION,
572 AdapterError::AmbiguousSystemColumnReference => SqlState::FEATURE_NOT_SUPPORTED,
573 AdapterError::Catalog(e) => match &e.kind {
574 mz_catalog::memory::error::ErrorKind::VarError(e) => match e {
575 VarError::ConstrainedParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
576 VarError::FixedValueParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
577 VarError::InvalidParameterType { .. } => SqlState::INVALID_PARAMETER_VALUE,
578 VarError::InvalidParameterValue { .. } => SqlState::INVALID_PARAMETER_VALUE,
579 VarError::ReadOnlyParameter(_) => SqlState::CANT_CHANGE_RUNTIME_PARAM,
580 VarError::UnknownParameter(_) => SqlState::UNDEFINED_OBJECT,
581 VarError::RequiresUnsafeMode { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
582 VarError::RequiresFeatureFlag { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
583 },
584 _ => SqlState::INTERNAL_ERROR,
585 },
586 AdapterError::ChangedPlan(_) => SqlState::FEATURE_NOT_SUPPORTED,
587 AdapterError::DuplicateCursor(_) => SqlState::DUPLICATE_CURSOR,
588 AdapterError::Eval(EvalError::CharacterNotValidForEncoding(_)) => {
589 SqlState::PROGRAM_LIMIT_EXCEEDED
590 }
591 AdapterError::Eval(EvalError::CharacterTooLargeForEncoding(_)) => {
592 SqlState::PROGRAM_LIMIT_EXCEEDED
593 }
594 AdapterError::Eval(EvalError::LengthTooLarge) => SqlState::PROGRAM_LIMIT_EXCEEDED,
595 AdapterError::Eval(EvalError::NullCharacterNotPermitted) => {
596 SqlState::PROGRAM_LIMIT_EXCEEDED
597 }
598 AdapterError::Eval(_) => SqlState::INTERNAL_ERROR,
599 AdapterError::Explain(_) => SqlState::INTERNAL_ERROR,
600 AdapterError::IdExhaustionError => SqlState::INTERNAL_ERROR,
601 AdapterError::Internal(_) => SqlState::INTERNAL_ERROR,
602 AdapterError::IntrospectionDisabled { .. } => SqlState::FEATURE_NOT_SUPPORTED,
603 AdapterError::InvalidLogDependency { .. } => SqlState::FEATURE_NOT_SUPPORTED,
604 AdapterError::InvalidClusterReplicaAz { .. } => SqlState::FEATURE_NOT_SUPPORTED,
605 AdapterError::InvalidSetIsolationLevel => SqlState::ACTIVE_SQL_TRANSACTION,
606 AdapterError::InvalidSetCluster => SqlState::ACTIVE_SQL_TRANSACTION,
607 AdapterError::InvalidStorageClusterSize { .. } => SqlState::FEATURE_NOT_SUPPORTED,
608 AdapterError::SourceOrSinkSizeRequired { .. } => SqlState::FEATURE_NOT_SUPPORTED,
609 AdapterError::InvalidTableMutationSelection { .. } => {
610 SqlState::INVALID_TRANSACTION_STATE
611 }
612 AdapterError::ConstraintViolation(NotNullViolation(_)) => SqlState::NOT_NULL_VIOLATION,
613 AdapterError::CopyFormatError(_) => SqlState::BAD_COPY_FILE_FORMAT,
614 AdapterError::ConcurrentClusterDrop => SqlState::INVALID_TRANSACTION_STATE,
615 AdapterError::ConcurrentDependencyDrop { .. } => SqlState::UNDEFINED_OBJECT,
616 AdapterError::CollectionUnreadable { .. } => SqlState::NO_DATA_FOUND,
617 AdapterError::NoClusterReplicasAvailable { .. } => SqlState::FEATURE_NOT_SUPPORTED,
618 AdapterError::OperationProhibitsTransaction(_) => SqlState::ACTIVE_SQL_TRANSACTION,
619 AdapterError::OperationRequiresTransaction(_) => SqlState::NO_ACTIVE_SQL_TRANSACTION,
620 AdapterError::ParseError(_) => SqlState::SYNTAX_ERROR,
621 AdapterError::PlanError(PlanError::InvalidSchemaName) => SqlState::INVALID_SCHEMA_NAME,
622 AdapterError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
623 SqlState::DUPLICATE_COLUMN
624 }
625 AdapterError::PlanError(PlanError::UnknownParameter(_)) => {
626 SqlState::UNDEFINED_PARAMETER
627 }
628 AdapterError::PlanError(PlanError::ParameterNotAllowed(_)) => {
629 SqlState::UNDEFINED_PARAMETER
630 }
631 AdapterError::PlanError(_) => SqlState::INTERNAL_ERROR,
632 AdapterError::PreparedStatementExists(_) => SqlState::DUPLICATE_PSTATEMENT,
633 AdapterError::ReadOnlyTransaction => SqlState::READ_ONLY_SQL_TRANSACTION,
634 AdapterError::ReadWriteUnavailable => SqlState::INVALID_TRANSACTION_STATE,
635 AdapterError::SingleStatementTransaction => SqlState::INVALID_TRANSACTION_STATE,
636 AdapterError::WrongSetOfLocks => SqlState::LOCK_NOT_AVAILABLE,
637 AdapterError::StatementTimeout => SqlState::QUERY_CANCELED,
638 AdapterError::Canceled => SqlState::QUERY_CANCELED,
639 AdapterError::IdleInTransactionSessionTimeout => {
640 SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT
641 }
642 AdapterError::RecursionLimit(_) => SqlState::INTERNAL_ERROR,
643 AdapterError::RelationOutsideTimeDomain { .. } => SqlState::INVALID_TRANSACTION_STATE,
644 AdapterError::ResourceExhaustion { .. } => SqlState::INSUFFICIENT_RESOURCES,
645 AdapterError::ResultSize(_) => SqlState::OUT_OF_MEMORY,
646 AdapterError::SafeModeViolation(_) => SqlState::INTERNAL_ERROR,
647 AdapterError::SubscribeOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
648 AdapterError::Optimizer(e) => match e {
649 OptimizerError::PlanError(PlanError::InvalidSchemaName) => {
650 SqlState::INVALID_SCHEMA_NAME
651 }
652 OptimizerError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
653 SqlState::DUPLICATE_COLUMN
654 }
655 OptimizerError::PlanError(PlanError::UnknownParameter(_)) => {
656 SqlState::UNDEFINED_PARAMETER
657 }
658 OptimizerError::PlanError(PlanError::ParameterNotAllowed(_)) => {
659 SqlState::UNDEFINED_PARAMETER
660 }
661 OptimizerError::PlanError(_) => SqlState::INTERNAL_ERROR,
662 OptimizerError::RecursionLimitError(e) => {
663 AdapterError::RecursionLimit(e.clone()).code() }
665 OptimizerError::Internal(s) => {
666 AdapterError::Internal(s.clone()).code() }
668 OptimizerError::EvalError(e) => {
669 AdapterError::Eval(e.clone()).code() }
671 OptimizerError::TransformError(_) => SqlState::INTERNAL_ERROR,
672 OptimizerError::UnmaterializableFunction(_) => SqlState::FEATURE_NOT_SUPPORTED,
673 OptimizerError::UncallableFunction { .. } => SqlState::FEATURE_NOT_SUPPORTED,
674 OptimizerError::UnsupportedTemporalExpression(_) => SqlState::FEATURE_NOT_SUPPORTED,
675 OptimizerError::InternalUnsafeMfpPlan(_) => SqlState::INTERNAL_ERROR,
678 },
679 AdapterError::UnallowedOnCluster { .. } => {
680 SqlState::S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED
681 }
682 AdapterError::Unauthorized(_) => SqlState::INSUFFICIENT_PRIVILEGE,
683 AdapterError::UnknownCursor(_) => SqlState::INVALID_CURSOR_NAME,
684 AdapterError::UnknownPreparedStatement(_) => SqlState::UNDEFINED_PSTATEMENT,
685 AdapterError::UnknownLoginRole(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
686 AdapterError::UnknownClusterReplica { .. } => SqlState::UNDEFINED_OBJECT,
687 AdapterError::UnrecognizedConfigurationParam(_) => SqlState::UNDEFINED_OBJECT,
688 AdapterError::Unsupported(..) => SqlState::FEATURE_NOT_SUPPORTED,
689 AdapterError::UnavailableFeature { .. } => SqlState::FEATURE_NOT_SUPPORTED,
690 AdapterError::Unstructured(_) => SqlState::INTERNAL_ERROR,
691 AdapterError::UntargetedLogRead { .. } => SqlState::FEATURE_NOT_SUPPORTED,
692 AdapterError::DDLTransactionRace => SqlState::T_R_SERIALIZATION_FAILURE,
693 AdapterError::WriteOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
698 AdapterError::DDLOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
699 AdapterError::Storage(_) | AdapterError::Compute(_) | AdapterError::Orchestrator(_) => {
700 SqlState::INTERNAL_ERROR
701 }
702 AdapterError::DependentObject(_) => SqlState::DEPENDENT_OBJECTS_STILL_EXIST,
703 AdapterError::InvalidAlter(_, _) => SqlState::FEATURE_NOT_SUPPORTED,
704 AdapterError::ConnectionValidation(_) => SqlState::SYSTEM_ERROR,
705 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => SqlState::DATA_EXCEPTION,
707 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => SqlState::DATA_EXCEPTION,
708 AdapterError::RtrTimeout(_) => SqlState::QUERY_CANCELED,
709 AdapterError::RtrDropFailure(_) => SqlState::UNDEFINED_OBJECT,
710 AdapterError::UnreadableSinkCollection => SqlState::from_code("MZ009"),
711 AdapterError::UserSessionsDisallowed => SqlState::from_code("MZ010"),
712 AdapterError::NetworkPolicyDenied(_) => SqlState::from_code("MZ011"),
713 AdapterError::ReadOnly => SqlState::READ_ONLY_SQL_TRANSACTION,
716 AdapterError::AlterClusterTimeout => SqlState::QUERY_CANCELED,
717 AdapterError::AlterClusterWhilePendingReplicas => SqlState::OBJECT_IN_USE,
718 AdapterError::ReplacementSchemaMismatch(_) => SqlState::FEATURE_NOT_SUPPORTED,
719 AdapterError::AuthenticationError(AuthenticationError::InvalidCredentials) => {
720 SqlState::INVALID_PASSWORD
721 }
722 AdapterError::AuthenticationError(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
723 AdapterError::ReplaceMaterializedViewSealed { .. } => {
724 SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE
725 }
726 AdapterError::ImpossibleTimestampConstraints { .. } => SqlState::DATA_EXCEPTION,
728 }
729 }
730
731 pub fn internal<E: std::fmt::Display>(context: &str, e: E) -> AdapterError {
732 AdapterError::Internal(format!("{context}: {e}"))
733 }
734
735 pub fn concurrent_dependency_drop_from_instance_missing(e: InstanceMissing) -> Self {
742 AdapterError::ConcurrentDependencyDrop {
743 dependency_kind: "cluster",
744 dependency_id: e.0.to_string(),
745 }
746 }
747
748 pub fn concurrent_dependency_drop_from_collection_missing(e: CollectionMissing) -> Self {
749 AdapterError::ConcurrentDependencyDrop {
750 dependency_kind: "collection",
751 dependency_id: e.0.to_string(),
752 }
753 }
754
755 pub fn concurrent_dependency_drop_from_collection_lookup_error(
756 e: CollectionLookupError,
757 compute_instance: ComputeInstanceId,
758 ) -> Self {
759 match e {
760 CollectionLookupError::InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
761 dependency_kind: "cluster",
762 dependency_id: id.to_string(),
763 },
764 CollectionLookupError::CollectionMissing(id) => {
765 AdapterError::ConcurrentDependencyDrop {
766 dependency_kind: "collection",
767 dependency_id: id.to_string(),
768 }
769 }
770 CollectionLookupError::InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
771 dependency_kind: "cluster",
772 dependency_id: compute_instance.to_string(),
773 },
774 }
775 }
776
777 pub fn concurrent_dependency_drop_from_watch_set_install_error(
778 e: compute_error::CollectionLookupError,
779 ) -> Self {
780 match e {
781 compute_error::CollectionLookupError::InstanceMissing(id) => {
782 AdapterError::ConcurrentDependencyDrop {
783 dependency_kind: "cluster",
784 dependency_id: id.to_string(),
785 }
786 }
787 compute_error::CollectionLookupError::CollectionMissing(id) => {
788 AdapterError::ConcurrentDependencyDrop {
789 dependency_kind: "collection",
790 dependency_id: id.to_string(),
791 }
792 }
793 }
794 }
795
796 pub fn concurrent_dependency_drop_from_instance_peek_error(
797 e: mz_compute_client::controller::instance_client::PeekError,
798 compute_instance: ComputeInstanceId,
799 ) -> AdapterError {
800 use mz_compute_client::controller::instance_client::PeekError::*;
801 match e {
802 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
803 dependency_kind: "replica",
804 dependency_id: id.to_string(),
805 },
806 InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
807 dependency_kind: "cluster",
808 dependency_id: compute_instance.to_string(),
809 },
810 e @ ReadHoldIdMismatch(_) => AdapterError::internal("instance peek error", e),
811 e @ ReadHoldInsufficient(_) => AdapterError::internal("instance peek error", e),
812 }
813 }
814
815 pub fn concurrent_dependency_drop_from_peek_error(
816 e: mz_compute_client::controller::error::PeekError,
817 ) -> AdapterError {
818 use mz_compute_client::controller::error::PeekError::*;
819 match e {
820 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
821 dependency_kind: "cluster",
822 dependency_id: id.to_string(),
823 },
824 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
825 dependency_kind: "collection",
826 dependency_id: id.to_string(),
827 },
828 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
829 dependency_kind: "replica",
830 dependency_id: id.to_string(),
831 },
832 e @ SinceViolation(_) => AdapterError::internal("peek error", e),
833 }
834 }
835
836 pub fn concurrent_dependency_drop_from_dataflow_creation_error(
837 e: compute_error::DataflowCreationError,
838 ) -> Self {
839 use compute_error::DataflowCreationError::*;
840 match e {
841 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
842 dependency_kind: "cluster",
843 dependency_id: id.to_string(),
844 },
845 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
846 dependency_kind: "collection",
847 dependency_id: id.to_string(),
848 },
849 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
850 dependency_kind: "replica",
851 dependency_id: id.to_string(),
852 },
853 MissingAsOf | SinceViolation(..) | EmptyAsOfForSubscribe | EmptyAsOfForCopyTo => {
854 AdapterError::internal("dataflow creation error", e)
855 }
856 }
857 }
858}
859
860impl fmt::Display for AdapterError {
861 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
862 match self {
863 AdapterError::AbsurdSubscribeBounds { as_of, up_to } => {
864 write!(
865 f,
866 "subscription lower bound (`AS OF`) is greater than its upper bound (`UP TO`): \
867 {as_of} > {up_to}",
868 )
869 }
870 AdapterError::AmbiguousSystemColumnReference => {
871 write!(
872 f,
873 "cannot use wildcard expansions or NATURAL JOINs in a view that depends on \
874 system objects"
875 )
876 }
877 AdapterError::ChangedPlan(e) => write!(f, "{}", e),
878 AdapterError::Catalog(e) => e.fmt(f),
879 AdapterError::DuplicateCursor(name) => {
880 write!(f, "cursor {} already exists", name.quoted())
881 }
882 AdapterError::Eval(e) => e.fmt(f),
883 AdapterError::Explain(e) => e.fmt(f),
884 AdapterError::IdExhaustionError => f.write_str("ID allocator exhausted all valid IDs"),
885 AdapterError::Internal(e) => write!(f, "internal error: {}", e),
886 AdapterError::IntrospectionDisabled { .. } => write!(
887 f,
888 "cannot read log sources of replica with disabled introspection"
889 ),
890 AdapterError::InvalidLogDependency { object_type, .. } => {
891 write!(f, "{object_type} objects cannot depend on log sources")
892 }
893 AdapterError::InvalidClusterReplicaAz { az, expected: _ } => {
894 write!(f, "unknown cluster replica availability zone {az}",)
895 }
896 AdapterError::InvalidSetIsolationLevel => write!(
897 f,
898 "SET TRANSACTION ISOLATION LEVEL must be called before any query"
899 ),
900 AdapterError::InvalidSetCluster => {
901 write!(f, "SET cluster cannot be called in an active transaction")
902 }
903 AdapterError::InvalidStorageClusterSize { size, .. } => {
904 write!(f, "unknown source size {size}")
905 }
906 AdapterError::SourceOrSinkSizeRequired { .. } => {
907 write!(f, "must specify either cluster or size option")
908 }
909 AdapterError::InvalidTableMutationSelection { .. } => {
910 write!(
911 f,
912 "invalid selection: operation may only (transitively) refer to non-source, non-system tables"
913 )
914 }
915 AdapterError::ReplaceMaterializedViewSealed { name } => {
916 write!(
917 f,
918 "materialized view {name} is sealed and thus cannot be replaced"
919 )
920 }
921 AdapterError::ConstraintViolation(not_null_violation) => {
922 write!(f, "{}", not_null_violation)
923 }
924 AdapterError::CopyFormatError(e) => write!(f, "{e}"),
925 AdapterError::ConcurrentClusterDrop => {
926 write!(f, "the transaction's active cluster has been dropped")
927 }
928 AdapterError::ConcurrentDependencyDrop {
929 dependency_kind,
930 dependency_id,
931 } => {
932 write!(f, "{dependency_kind} '{dependency_id}' was dropped")
933 }
934 AdapterError::CollectionUnreadable { id } => {
935 write!(f, "collection '{id}' is not readable at any timestamp")
936 }
937 AdapterError::NoClusterReplicasAvailable { name, .. } => {
938 write!(
939 f,
940 "CLUSTER {} has no replicas available to service request",
941 name.quoted()
942 )
943 }
944 AdapterError::OperationProhibitsTransaction(op) => {
945 write!(f, "{} cannot be run inside a transaction block", op)
946 }
947 AdapterError::OperationRequiresTransaction(op) => {
948 write!(f, "{} can only be used in transaction blocks", op)
949 }
950 AdapterError::ParseError(e) => e.fmt(f),
951 AdapterError::PlanError(e) => e.fmt(f),
952 AdapterError::PreparedStatementExists(name) => {
953 write!(f, "prepared statement {} already exists", name.quoted())
954 }
955 AdapterError::ReadOnlyTransaction => f.write_str("transaction in read-only mode"),
956 AdapterError::SingleStatementTransaction => {
957 f.write_str("this transaction can only execute a single statement")
958 }
959 AdapterError::ReadWriteUnavailable => {
960 f.write_str("transaction read-write mode must be set before any query")
961 }
962 AdapterError::WrongSetOfLocks => {
963 write!(f, "internal error, wrong set of locks acquired")
964 }
965 AdapterError::StatementTimeout => {
966 write!(f, "canceling statement due to statement timeout")
967 }
968 AdapterError::Canceled => {
969 write!(f, "canceling statement due to user request")
970 }
971 AdapterError::IdleInTransactionSessionTimeout => {
972 write!(
973 f,
974 "terminating connection due to idle-in-transaction timeout"
975 )
976 }
977 AdapterError::RecursionLimit(e) => e.fmt(f),
978 AdapterError::RelationOutsideTimeDomain { .. } => {
979 write!(
980 f,
981 "Transactions can only reference objects in the same timedomain. \
982 See https://materialize.com/docs/sql/begin/#same-timedomain-error",
983 )
984 }
985 AdapterError::ResourceExhaustion {
986 resource_type,
987 limit_name,
988 desired,
989 limit,
990 current,
991 } => {
992 write!(
993 f,
994 "creating {resource_type} would violate {limit_name} limit (desired: {desired}, limit: {limit}, current: {current})"
995 )
996 }
997 AdapterError::ResultSize(e) => write!(f, "{e}"),
998 AdapterError::SafeModeViolation(feature) => {
999 write!(f, "cannot create {} in safe mode", feature)
1000 }
1001 AdapterError::SubscribeOnlyTransaction => {
1002 f.write_str("SUBSCRIBE in transactions must be the only read statement")
1003 }
1004 AdapterError::Optimizer(e) => e.fmt(f),
1005 AdapterError::UnallowedOnCluster {
1006 depends_on,
1007 cluster,
1008 } => {
1009 let items = depends_on.into_iter().map(|item| item.quoted()).join(", ");
1010 write!(
1011 f,
1012 "querying the following items {items} is not allowed from the {} cluster",
1013 cluster.quoted()
1014 )
1015 }
1016 AdapterError::Unauthorized(unauthorized) => {
1017 write!(f, "{unauthorized}")
1018 }
1019 AdapterError::UnknownCursor(name) => {
1020 write!(f, "cursor {} does not exist", name.quoted())
1021 }
1022 AdapterError::UnknownLoginRole(name) => {
1023 write!(f, "role {} does not exist", name.quoted())
1024 }
1025 AdapterError::Unsupported(features) => write!(f, "{} are not supported", features),
1026 AdapterError::Unstructured(e) => write!(f, "{}", e.display_with_causes()),
1027 AdapterError::WriteOnlyTransaction => f.write_str("transaction in write-only mode"),
1028 AdapterError::UnknownPreparedStatement(name) => {
1029 write!(f, "prepared statement {} does not exist", name.quoted())
1030 }
1031 AdapterError::UnknownClusterReplica {
1032 cluster_name,
1033 replica_name,
1034 } => write!(
1035 f,
1036 "cluster replica '{cluster_name}.{replica_name}' does not exist"
1037 ),
1038 AdapterError::UnrecognizedConfigurationParam(setting_name) => write!(
1039 f,
1040 "unrecognized configuration parameter {}",
1041 setting_name.quoted()
1042 ),
1043 AdapterError::UntargetedLogRead { .. } => {
1044 f.write_str("log source reads must target a replica")
1045 }
1046 AdapterError::DDLOnlyTransaction => f.write_str(
1047 "transactions which modify objects are restricted to just modifying objects",
1048 ),
1049 AdapterError::DDLTransactionRace => f.write_str(
1050 "another session modified the catalog while this DDL transaction was open",
1051 ),
1052 AdapterError::Storage(e) => e.fmt(f),
1053 AdapterError::Compute(e) => e.fmt(f),
1054 AdapterError::Orchestrator(e) => e.fmt(f),
1055 AdapterError::DependentObject(dependent_objects) => {
1056 let role_str = if dependent_objects.keys().count() == 1 {
1057 "role"
1058 } else {
1059 "roles"
1060 };
1061 write!(
1062 f,
1063 "{role_str} \"{}\" cannot be dropped because some objects depend on it",
1064 dependent_objects.keys().join(", ")
1065 )
1066 }
1067 AdapterError::InvalidAlter(t, e) => {
1068 write!(f, "invalid ALTER {t}: {e}")
1069 }
1070 AdapterError::ConnectionValidation(e) => e.fmt(f),
1071 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => {
1072 write!(
1073 f,
1074 "all the specified refreshes of the materialized view would be too far in the past, and thus they \
1075 would never happen"
1076 )
1077 }
1078 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => {
1079 write!(
1080 f,
1081 "REFRESH AT requested for a time where not all the inputs are readable"
1082 )
1083 }
1084 AdapterError::RtrTimeout(_) => {
1085 write!(
1086 f,
1087 "timed out before ingesting the source's visible frontier when real-time-recency query issued"
1088 )
1089 }
1090 AdapterError::RtrDropFailure(_) => write!(
1091 f,
1092 "real-time source dropped before ingesting the upstream system's visible frontier"
1093 ),
1094 AdapterError::UnreadableSinkCollection => {
1095 write!(f, "collection is not readable at any time")
1096 }
1097 AdapterError::UserSessionsDisallowed => write!(f, "login blocked"),
1098 AdapterError::NetworkPolicyDenied(_) => write!(f, "session denied"),
1099 AdapterError::ReadOnly => write!(f, "cannot write in read-only mode"),
1100 AdapterError::AlterClusterTimeout => {
1101 write!(f, "canceling statement, provided timeout lapsed")
1102 }
1103 AdapterError::AuthenticationError(e) => {
1104 write!(f, "authentication error {e}")
1105 }
1106 AdapterError::UnavailableFeature { feature, docs } => {
1107 write!(f, "{} is not supported in this environment.", feature)?;
1108 if let Some(docs) = docs {
1109 write!(
1110 f,
1111 " For more information consult the documentation at {docs}"
1112 )?;
1113 }
1114 Ok(())
1115 }
1116 AdapterError::AlterClusterWhilePendingReplicas => {
1117 write!(f, "cannot alter clusters with pending updates")
1118 }
1119 AdapterError::ReplacementSchemaMismatch(_) => {
1120 write!(f, "replacement schema differs from target schema")
1121 }
1122 AdapterError::ImpossibleTimestampConstraints { .. } => {
1123 write!(f, "could not find a valid timestamp for the query")
1124 }
1125 }
1126 }
1127}
1128
1129impl From<anyhow::Error> for AdapterError {
1130 fn from(e: anyhow::Error) -> AdapterError {
1131 match e.downcast::<PlanError>() {
1132 Ok(plan_error) => AdapterError::PlanError(plan_error),
1133 Err(e) => AdapterError::Unstructured(e),
1134 }
1135 }
1136}
1137
1138impl From<TryFromIntError> for AdapterError {
1139 fn from(e: TryFromIntError) -> AdapterError {
1140 AdapterError::Unstructured(e.into())
1141 }
1142}
1143
1144impl From<TryFromDecimalError> for AdapterError {
1145 fn from(e: TryFromDecimalError) -> AdapterError {
1146 AdapterError::Unstructured(e.into())
1147 }
1148}
1149
1150impl From<mz_catalog::memory::error::Error> for AdapterError {
1151 fn from(e: mz_catalog::memory::error::Error) -> AdapterError {
1152 AdapterError::Catalog(e)
1153 }
1154}
1155
1156impl From<mz_catalog::durable::CatalogError> for AdapterError {
1157 fn from(e: mz_catalog::durable::CatalogError) -> Self {
1158 mz_catalog::memory::error::Error::from(e).into()
1159 }
1160}
1161
1162impl From<mz_catalog::durable::DurableCatalogError> for AdapterError {
1163 fn from(e: mz_catalog::durable::DurableCatalogError) -> Self {
1164 mz_catalog::durable::CatalogError::from(e).into()
1165 }
1166}
1167
1168impl From<EvalError> for AdapterError {
1169 fn from(e: EvalError) -> AdapterError {
1170 AdapterError::Eval(e)
1171 }
1172}
1173
1174impl From<ExplainError> for AdapterError {
1175 fn from(e: ExplainError) -> AdapterError {
1176 match e {
1177 ExplainError::RecursionLimitError(e) => AdapterError::RecursionLimit(e),
1178 e => AdapterError::Explain(e),
1179 }
1180 }
1181}
1182
1183impl From<mz_sql::catalog::CatalogError> for AdapterError {
1184 fn from(e: mz_sql::catalog::CatalogError) -> AdapterError {
1185 AdapterError::Catalog(mz_catalog::memory::error::Error::from(e))
1186 }
1187}
1188
1189impl From<PlanError> for AdapterError {
1190 fn from(e: PlanError) -> AdapterError {
1191 match e {
1192 PlanError::UnknownCursor(name) => AdapterError::UnknownCursor(name),
1193 _ => AdapterError::PlanError(e),
1194 }
1195 }
1196}
1197
1198impl From<OptimizerError> for AdapterError {
1199 fn from(e: OptimizerError) -> AdapterError {
1200 use OptimizerError::*;
1201 match e {
1202 PlanError(e) => Self::PlanError(e),
1203 RecursionLimitError(e) => Self::RecursionLimit(e),
1204 EvalError(e) => Self::Eval(e),
1205 InternalUnsafeMfpPlan(e) => Self::Internal(e),
1206 Internal(e) => Self::Internal(e),
1207 e => Self::Optimizer(e),
1208 }
1209 }
1210}
1211
1212impl From<NotNullViolation> for AdapterError {
1213 fn from(e: NotNullViolation) -> AdapterError {
1214 AdapterError::ConstraintViolation(e)
1215 }
1216}
1217
1218impl From<RecursionLimitError> for AdapterError {
1219 fn from(e: RecursionLimitError) -> AdapterError {
1220 AdapterError::RecursionLimit(e)
1221 }
1222}
1223
1224impl From<oneshot::error::RecvError> for AdapterError {
1225 fn from(e: oneshot::error::RecvError) -> AdapterError {
1226 AdapterError::Unstructured(e.into())
1227 }
1228}
1229
1230impl From<StorageError<mz_repr::Timestamp>> for AdapterError {
1231 fn from(e: StorageError<mz_repr::Timestamp>) -> Self {
1232 AdapterError::Storage(e)
1233 }
1234}
1235
1236impl From<compute_error::InstanceExists> for AdapterError {
1237 fn from(e: compute_error::InstanceExists) -> Self {
1238 AdapterError::Compute(e.into())
1239 }
1240}
1241
1242impl From<TimestampError> for AdapterError {
1243 fn from(e: TimestampError) -> Self {
1244 let e: EvalError = e.into();
1245 e.into()
1246 }
1247}
1248
1249impl From<mz_sql_parser::parser::ParserStatementError> for AdapterError {
1250 fn from(e: mz_sql_parser::parser::ParserStatementError) -> Self {
1251 AdapterError::ParseError(e)
1252 }
1253}
1254
1255impl From<VarError> for AdapterError {
1256 fn from(e: VarError) -> Self {
1257 let e: mz_catalog::memory::error::Error = e.into();
1258 e.into()
1259 }
1260}
1261
1262impl From<rbac::UnauthorizedError> for AdapterError {
1263 fn from(e: rbac::UnauthorizedError) -> Self {
1264 AdapterError::Unauthorized(e)
1265 }
1266}
1267
1268impl From<mz_sql_parser::ast::IdentError> for AdapterError {
1269 fn from(value: mz_sql_parser::ast::IdentError) -> Self {
1270 AdapterError::PlanError(PlanError::InvalidIdent(value))
1271 }
1272}
1273
1274impl From<mz_pgwire_common::ConnectionError> for AdapterError {
1275 fn from(value: mz_pgwire_common::ConnectionError) -> Self {
1276 match value {
1277 mz_pgwire_common::ConnectionError::TooManyConnections { current, limit } => {
1278 AdapterError::ResourceExhaustion {
1279 resource_type: "connection".into(),
1280 limit_name: "max_connections".into(),
1281 desired: (current + 1).to_string(),
1282 limit: limit.to_string(),
1283 current: current.to_string(),
1284 }
1285 }
1286 }
1287 }
1288}
1289
1290impl From<NetworkPolicyError> for AdapterError {
1291 fn from(value: NetworkPolicyError) -> Self {
1292 AdapterError::NetworkPolicyDenied(value)
1293 }
1294}
1295
1296impl From<ConnectionValidationError> for AdapterError {
1297 fn from(e: ConnectionValidationError) -> AdapterError {
1298 AdapterError::ConnectionValidation(e)
1299 }
1300}
1301
1302impl Error for AdapterError {}