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_controller_types::ClusterId;
23use mz_expr::EvalError;
24use mz_ore::error::ErrorExt;
25use mz_ore::stack::RecursionLimitError;
26use mz_ore::str::StrExt;
27use mz_pgwire_common::{ErrorResponse, Severity};
28use mz_repr::adt::array::InvalidArrayError;
29use mz_repr::adt::range::InvalidRangeError;
30use mz_repr::adt::timestamp::TimestampError;
31use mz_repr::explain::ExplainError;
32use mz_repr::strconv::{ParseError, ParseErrorKind};
33use mz_repr::{ColumnDiff, ColumnName, KeyDiff, NotNullViolation, RelationDescDiff, Timestamp};
34use mz_sql::plan::PlanError;
35use mz_sql::rbac;
36use mz_sql::session::vars::VarError;
37use mz_storage_types::connections::ConnectionValidationError;
38use mz_storage_types::controller::StorageError;
39use mz_storage_types::errors::CollectionMissing;
40use smallvec::SmallVec;
41use timely::progress::Antichain;
42use tokio::sync::oneshot;
43use tokio_postgres::error::SqlState;
44
45use crate::coord::NetworkPolicyError;
46use crate::optimize::OptimizerError;
47use crate::peek_client::CollectionLookupError;
48
49#[derive(Debug)]
51pub enum AdapterError {
52 AbsurdSubscribeBounds {
54 as_of: mz_repr::Timestamp,
55 up_to: mz_repr::Timestamp,
56 },
57 AmbiguousSystemColumnReference,
61 Catalog(mz_catalog::memory::error::Error),
63 ChangedPlan(String),
68 DuplicateCursor(String),
70 Eval(EvalError),
72 Explain(ExplainError),
74 IdExhaustionError,
76 Internal(String),
78 IntrospectionDisabled {
80 log_names: Vec<String>,
81 },
82 InvalidLogDependency {
85 object_type: String,
86 log_names: Vec<String>,
87 },
88 InvalidClusterReplicaAz {
90 az: String,
91 expected: Vec<String>,
92 },
93 InvalidSetIsolationLevel,
95 InvalidSetCluster,
97 InvalidStorageClusterSize {
99 size: String,
100 expected: Vec<String>,
101 },
102 SourceOrSinkSizeRequired {
104 expected: Vec<String>,
105 },
106 InvalidTableMutationSelection {
108 object_name: String,
110 object_type: String,
112 },
113 ConstraintViolation(NotNullViolation),
115 CopyFormatError(String),
117 ConcurrentClusterDrop,
119 ConcurrentDependencyDrop {
121 dependency_kind: &'static str,
122 dependency_id: String,
123 },
124 ConcurrentDependencyMutation {
128 dependency_id: String,
129 },
130 CollectionUnreadable {
131 id: String,
132 },
133 NoClusterReplicasAvailable {
135 name: String,
136 is_managed: bool,
137 },
138 OperationProhibitsTransaction(String),
140 OperationRequiresTransaction(String),
142 PlanError(PlanError),
144 PreparedStatementExists(String),
146 ParseError(mz_sql_parser::parser::ParserStatementError),
148 ReadOnlyTransaction,
150 ReadWriteUnavailable,
152 RecursionLimit(RecursionLimitError),
154 RelationOutsideTimeDomain {
157 relations: Vec<String>,
158 names: Vec<String>,
159 },
160 ResourceExhaustion {
162 resource_type: String,
163 limit_name: String,
164 desired: String,
165 limit: String,
166 current: String,
167 },
168 ResultSize(String),
170 SafeModeViolation(String),
172 WrongSetOfLocks,
174 StatementTimeout,
178 Canceled,
180 IdleInTransactionSessionTimeout,
182 SubscribeOnlyTransaction,
184 Optimizer(OptimizerError),
186 UnallowedOnCluster {
188 depends_on: SmallVec<[String; 2]>,
189 cluster: String,
190 },
191 Unauthorized(rbac::UnauthorizedError),
193 UnknownCursor(String),
195 UnknownLoginRole(String),
197 UnknownPreparedStatement(String),
198 UnknownClusterReplica {
200 cluster_name: String,
201 replica_name: String,
202 },
203 UnrecognizedConfigurationParam(String),
205 Unstructured(anyhow::Error),
209 Unsupported(&'static str),
211 UnavailableFeature {
215 feature: String,
216 docs: Option<String>,
217 },
218 UntargetedLogRead {
220 log_names: Vec<String>,
221 },
222 WriteOnlyTransaction,
224 SingleStatementTransaction,
226 DDLOnlyTransaction,
228 DDLTransactionRace,
230 ClusterStateChanged {
235 cluster_id: ClusterId,
236 },
237 Storage(mz_storage_types::controller::StorageError),
239 Compute(anyhow::Error),
241 Orchestrator(anyhow::Error),
243 DependentObject(BTreeMap<String, Vec<String>>),
247 InvalidAlter(&'static str, PlanError),
250 ConnectionValidation(ConnectionValidationError),
252 MaterializedViewWouldNeverRefresh(Timestamp, Timestamp),
256 InputNotReadableAtRefreshAtTime(Timestamp, Antichain<Timestamp>),
259 RtrTimeout(String),
261 RtrDropFailure(String),
263 UnreadableSinkCollection,
265 UserSessionsDisallowed,
267 NetworkPolicyDenied(NetworkPolicyError),
269 ReadOnly,
272 AlterClusterTimeout,
273 AlterClusterWhilePendingReplicas,
274 AuthenticationError(AuthenticationError),
275 ReplacementSchemaMismatch(RelationDescDiff),
277 ReplaceMaterializedViewSealed {
279 name: String,
280 },
281 ImpossibleTimestampConstraints {
283 constraints: String,
284 },
285 OidcGroupSyncFailed(String),
287 BoundedStalenessExceeded {
291 bound: std::time::Duration,
292 gap_ms: u64,
293 slowest_input: Option<mz_repr::GlobalId>,
294 },
295 BoundedStalenessReadOnly,
298 BoundedStalenessRealTimeRecencyConflict,
301 BoundedStalenessTimelineUnsupported,
304}
305
306#[derive(Debug, thiserror::Error)]
307pub enum AuthenticationError {
308 #[error("invalid credentials")]
309 InvalidCredentials,
310 #[error("role is not allowed to login")]
311 NonLogin,
312 #[error("role does not exist")]
313 RoleNotFound,
314 #[error("password is required")]
315 PasswordRequired,
316}
317
318fn parse_error_code(err: &ParseError) -> SqlState {
323 let is_datetime = matches!(
324 &*err.type_name,
325 "date" | "time" | "timestamp" | "timestamp with time zone" | "interval"
326 );
327 match (err.kind, is_datetime) {
328 (ParseErrorKind::OutOfRange, true) => SqlState::DATETIME_FIELD_OVERFLOW,
329 (ParseErrorKind::OutOfRange, false) => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
330 (ParseErrorKind::InvalidInputSyntax, true) => SqlState::INVALID_DATETIME_FORMAT,
331 (ParseErrorKind::InvalidInputSyntax, false) => SqlState::INVALID_TEXT_REPRESENTATION,
332 }
333}
334
335fn eval_error_code(err: &EvalError) -> SqlState {
345 match err {
346 EvalError::DivisionByZero => SqlState::DIVISION_BY_ZERO,
348 EvalError::NegSqrt | EvalError::ComplexOutOfRange(_) => {
349 SqlState::INVALID_ARGUMENT_FOR_POWER_FUNCTION
350 }
351 EvalError::InfinityOutOfDomain(_)
352 | EvalError::NegativeOutOfDomain(_)
353 | EvalError::ZeroOutOfDomain(_)
354 | EvalError::OutOfDomain(..)
355 | EvalError::Undefined(_) => SqlState::INVALID_PARAMETER_VALUE,
356
357 EvalError::FloatOverflow
359 | EvalError::FloatUnderflow
360 | EvalError::NumericFieldOverflow
361 | EvalError::Float32OutOfRange(_)
362 | EvalError::Float64OutOfRange(_)
363 | EvalError::Int16OutOfRange(_)
364 | EvalError::Int32OutOfRange(_)
365 | EvalError::Int64OutOfRange(_)
366 | EvalError::UInt16OutOfRange(_)
367 | EvalError::UInt32OutOfRange(_)
368 | EvalError::UInt64OutOfRange(_)
369 | EvalError::OidOutOfRange(_)
370 | EvalError::MzTimestampOutOfRange(_)
371 | EvalError::MzTimestampStepOverflow
372 | EvalError::CharOutOfRange => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
373
374 EvalError::DateOutOfRange
376 | EvalError::TimestampOutOfRange
377 | EvalError::TimestampCannotBeNan
378 | EvalError::DateBinOutOfRange(_)
379 | EvalError::DateDiffOverflow { .. } => SqlState::DATETIME_FIELD_OVERFLOW,
380 EvalError::IntervalOutOfRange(_) => SqlState::INTERVAL_FIELD_OVERFLOW,
381
382 EvalError::Parse(e) => parse_error_code(e),
384 EvalError::ParseHex(_)
385 | EvalError::InvalidBase64Equals
386 | EvalError::InvalidBase64Symbol(_)
387 | EvalError::InvalidBase64EndSequence => SqlState::INVALID_TEXT_REPRESENTATION,
388 EvalError::InvalidByteSequence { .. } => SqlState::CHARACTER_NOT_IN_REPERTOIRE,
389 EvalError::CharacterNotValidForEncoding(_) | EvalError::CharacterTooLargeForEncoding(_) => {
390 SqlState::PROGRAM_LIMIT_EXCEEDED
391 }
392
393 EvalError::InvalidTimezone(_)
395 | EvalError::InvalidTimezoneInterval
396 | EvalError::InvalidTimezoneConversion
397 | EvalError::InvalidIanaTimezoneId(_)
398 | EvalError::InvalidLayer { .. }
399 | EvalError::InvalidEncodingName(_)
400 | EvalError::InvalidHashAlgorithm(_)
401 | EvalError::InvalidDatePart(_)
402 | EvalError::InvalidParameterValue(_)
403 | EvalError::InvalidJsonbCast { .. }
404 | EvalError::UnknownUnits(_)
405 | EvalError::UnsupportedUnits(..)
406 | EvalError::InvalidIdentifier { .. }
407 | EvalError::InvalidRoleId(_)
408 | EvalError::InvalidPrivileges(_)
409 | EvalError::NegLimit => SqlState::INVALID_PARAMETER_VALUE,
410
411 EvalError::InvalidRegex(_) | EvalError::InvalidRegexFlag(_) => {
413 SqlState::INVALID_REGULAR_EXPRESSION
414 }
415
416 EvalError::UnterminatedLikeEscapeSequence | EvalError::LikeEscapeTooLong => {
418 SqlState::INVALID_ESCAPE_SEQUENCE
419 }
420
421 EvalError::KeyCannotBeNull
423 | EvalError::MustNotBeNull(_)
424 | EvalError::AclArrayNullElement
425 | EvalError::MzAclArrayNullElement => SqlState::NULL_VALUE_NOT_ALLOWED,
426
427 EvalError::IndexOutOfRange { .. }
429 | EvalError::ArrayFillWrongArraySubscripts
430 | EvalError::IncompatibleArrayDimensions { .. } => SqlState::ARRAY_SUBSCRIPT_ERROR,
431 EvalError::InvalidArray(e) => match e {
432 InvalidArrayError::TooManyDimensions(_) => SqlState::PROGRAM_LIMIT_EXCEEDED,
433 InvalidArrayError::WrongCardinality { .. } => SqlState::ARRAY_SUBSCRIPT_ERROR,
434 },
435
436 EvalError::InvalidRange(e) => match e {
438 InvalidRangeError::CanonicalizationOverflow(_) => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
439 InvalidRangeError::MisorderedRangeBounds
440 | InvalidRangeError::InvalidRangeBoundFlags
441 | InvalidRangeError::NullRangeBoundFlags
442 | InvalidRangeError::DiscontiguousUnion
443 | InvalidRangeError::DiscontiguousDifference
444 | InvalidRangeError::InvalidRangeData => SqlState::DATA_EXCEPTION,
445 },
446
447 EvalError::MultipleRowsFromSubquery | EvalError::NegativeRowsFromSubquery => {
449 SqlState::CARDINALITY_VIOLATION
450 }
451
452 EvalError::StringValueTooLong { .. } => SqlState::STRING_DATA_RIGHT_TRUNCATION,
454 EvalError::LikePatternTooLong
455 | EvalError::LengthTooLarge
456 | EvalError::NullCharacterNotPermitted
457 | EvalError::MaxArraySizeExceeded(_)
458 | EvalError::LetRecLimitExceeded(_) => SqlState::PROGRAM_LIMIT_EXCEEDED,
459
460 EvalError::Unsupported { .. }
462 | EvalError::MultidimensionalArrayRemovalNotSupported
463 | EvalError::MultiDimensionalArraySearch => SqlState::FEATURE_NOT_SUPPORTED,
464
465 EvalError::IfNullError(_) => SqlState::DATA_EXCEPTION,
467
468 EvalError::Internal(_)
470 | EvalError::InvalidCatalogJson(_)
471 | EvalError::TypeFromOid(_)
472 | EvalError::PrettyError(_)
473 | EvalError::RedactError(_) => SqlState::INTERNAL_ERROR,
474 }
475}
476
477impl AdapterError {
478 pub fn into_response(self, severity: Severity) -> ErrorResponse {
479 ErrorResponse {
480 severity,
481 code: self.code(),
482 message: self.to_string(),
483 detail: self.detail(),
484 hint: self.hint(),
485 position: self.position(),
486 }
487 }
488
489 pub fn position(&self) -> Option<usize> {
490 match self {
491 AdapterError::ParseError(err) => Some(err.error.pos),
492 _ => None,
493 }
494 }
495
496 pub fn detail(&self) -> Option<String> {
498 match self {
499 AdapterError::AmbiguousSystemColumnReference => {
500 Some("This is a current limitation in Materialize".into())
501 }
502 AdapterError::Catalog(c) => c.detail(),
503 AdapterError::Eval(e) => e.detail(),
504 AdapterError::RelationOutsideTimeDomain { relations, names } => Some(format!(
505 "The following relations in the query are outside the transaction's time domain:\n{}\n{}",
506 relations
507 .iter()
508 .map(|r| r.quoted().to_string())
509 .collect::<Vec<_>>()
510 .join("\n"),
511 match names.is_empty() {
512 true => "No relations are available.".to_string(),
513 false => format!(
514 "Only the following relations are available:\n{}",
515 names
516 .iter()
517 .map(|name| name.quoted().to_string())
518 .collect::<Vec<_>>()
519 .join("\n")
520 ),
521 }
522 )),
523 AdapterError::SourceOrSinkSizeRequired { .. } => Some(
524 "Either specify the cluster that will maintain this object via IN CLUSTER or \
525 specify size via SIZE option."
526 .into(),
527 ),
528 AdapterError::InvalidTableMutationSelection {
529 object_name,
530 object_type,
531 } => Some(format!(
532 "{object_type} '{}' may not be used in this operation; \
533 the selection may refer to views and materialized views, but transitive \
534 dependencies must not include sources or source-export tables",
535 object_name.quoted()
536 )),
537 AdapterError::SafeModeViolation(_) => Some(
538 "The Materialize server you are connected to is running in \
539 safe mode, which limits the features that are available."
540 .into(),
541 ),
542 AdapterError::IntrospectionDisabled { log_names }
543 | AdapterError::UntargetedLogRead { log_names } => Some(format!(
544 "The query references the following log sources:\n {}",
545 log_names.join("\n "),
546 )),
547 AdapterError::InvalidLogDependency { log_names, .. } => Some(format!(
548 "The object depends on the following log sources:\n {}",
549 log_names.join("\n "),
550 )),
551 AdapterError::PlanError(e) => e.detail(),
552 AdapterError::Unauthorized(unauthorized) => unauthorized.detail(),
553 AdapterError::DependentObject(dependent_objects) => Some(
554 dependent_objects
555 .iter()
556 .map(|(role_name, err_msgs)| {
557 err_msgs
558 .iter()
559 .map(|err_msg| format!("{role_name}: {err_msg}"))
560 .join("\n")
561 })
562 .join("\n"),
563 ),
564 AdapterError::Storage(storage_error) => storage_error
565 .source()
566 .map(|source_error| source_error.to_string_with_causes()),
567 AdapterError::ReadOnlyTransaction => Some(
568 "SELECT queries cannot be combined with other query types, including SUBSCRIBE."
569 .into(),
570 ),
571 AdapterError::InvalidAlter(_, e) => e.detail(),
572 AdapterError::Optimizer(e) => e.detail(),
573 AdapterError::ConnectionValidation(e) => e.detail(),
574 AdapterError::MaterializedViewWouldNeverRefresh(last_refresh, earliest_possible) => {
575 Some(format!(
576 "The specified last refresh is at {}, while the earliest possible time to compute the materialized \
577 view is {}.",
578 last_refresh, earliest_possible,
579 ))
580 }
581 AdapterError::UnallowedOnCluster { cluster, .. } => {
582 (cluster == MZ_CATALOG_SERVER_CLUSTER.name).then(|| {
583 format!(
584 "The transaction is executing on the \
585 {cluster} cluster, maybe having been routed \
586 there by the first statement in the transaction."
587 )
588 })
589 }
590 AdapterError::InputNotReadableAtRefreshAtTime(oracle_read_ts, least_valid_read) => {
591 Some(format!(
592 "The requested REFRESH AT time is {}, \
593 but not all input collections are readable earlier than [{}].",
594 oracle_read_ts,
595 if least_valid_read.len() == 1 {
596 format!(
597 "{}",
598 least_valid_read
599 .as_option()
600 .expect("antichain contains exactly 1 timestamp")
601 )
602 } else {
603 format!("{:?}", least_valid_read)
605 }
606 ))
607 }
608 AdapterError::RtrTimeout(name) => Some(format!(
609 "{name} failed to ingest data up to the real-time recency point"
610 )),
611 AdapterError::RtrDropFailure(name) => Some(format!(
612 "{name} dropped before ingesting data to the real-time recency point"
613 )),
614 AdapterError::UserSessionsDisallowed => {
615 Some("Your organization has been blocked. Please contact support.".to_string())
616 }
617 AdapterError::NetworkPolicyDenied(reason) => Some(format!("{reason}.")),
618 AdapterError::ReplacementSchemaMismatch(diff) => {
619 let mut lines: Vec<_> = diff.column_diffs.iter().map(|(idx, diff)| {
620 let pos = idx + 1;
621 match diff {
622 ColumnDiff::Missing { name } => {
623 let name = name.as_str().quoted();
624 format!("missing column {name} at position {pos}")
625 }
626 ColumnDiff::Extra { name } => {
627 let name = name.as_str().quoted();
628 format!("extra column {name} at position {pos}")
629 }
630 ColumnDiff::TypeMismatch { name, left, right } => {
631 let name = name.as_str().quoted();
632 format!("column {name} at position {pos}: type mismatch (target: {left:?}, replacement: {right:?})")
633 }
634 ColumnDiff::NullabilityMismatch { name, left, right } => {
635 let name = name.as_str().quoted();
636 let left = if *left { "NULL" } else { "NOT NULL" };
637 let right = if *right { "NULL" } else { "NOT NULL" };
638 format!("column {name} at position {pos}: nullability mismatch (target: {left}, replacement: {right})")
639 }
640 ColumnDiff::NameMismatch { left, right } => {
641 let left = left.as_str().quoted();
642 let right = right.as_str().quoted();
643 format!("column at position {pos}: name mismatch (target: {left}, replacement: {right})")
644 }
645 }
646 }).collect();
647
648 if let Some(KeyDiff { left, right }) = &diff.key_diff {
649 let format_keys = |keys: &BTreeSet<Vec<ColumnName>>| {
650 if keys.is_empty() {
651 "(none)".to_string()
652 } else {
653 keys.iter()
654 .map(|key| {
655 let cols = key.iter().map(|c| c.as_str()).join(", ");
656 format!("{{{cols}}}")
657 })
658 .join(", ")
659 }
660 };
661 lines.push(format!(
662 "keys differ (target: {}, replacement: {})",
663 format_keys(left),
664 format_keys(right)
665 ));
666 }
667 Some(lines.join("\n"))
668 }
669 AdapterError::ReplaceMaterializedViewSealed { .. } => Some(
670 "The materialized view has already computed its output until the end of time, \
671 so replacing its definition would have no effect."
672 .into(),
673 ),
674 AdapterError::ImpossibleTimestampConstraints { constraints } => {
675 Some(format!("Constraints:\n{}", constraints))
676 }
677 AdapterError::BoundedStalenessExceeded {
678 gap_ms,
679 slowest_input,
680 ..
681 } => {
682 let mut detail = format!(
683 "Freshest available timestamp is {}ms older than the bound.",
684 gap_ms,
685 );
686 if let Some(id) = slowest_input {
687 detail.push_str(&format!(" Slowest input: {}.", id));
688 }
689 Some(detail)
690 }
691 AdapterError::BoundedStalenessTimelineUnsupported => Some(
692 "This query touches a timeline other than the EpochMilliseconds wall-clock \
693 timeline."
694 .into(),
695 ),
696 _ => None,
697 }
698 }
699
700 pub fn hint(&self) -> Option<String> {
702 match self {
703 AdapterError::AmbiguousSystemColumnReference => Some(
704 "Rewrite the view to refer to all columns by name. Expand all wildcards and \
705 convert all NATURAL JOINs to USING joins."
706 .to_string(),
707 ),
708 AdapterError::Catalog(c) => c.hint(),
709 AdapterError::Eval(e) => e.hint(),
710 AdapterError::InvalidClusterReplicaAz { expected, az: _ } => {
711 Some(if expected.is_empty() {
712 "No availability zones configured; do not specify AVAILABILITY ZONE".into()
713 } else {
714 format!("Valid availability zones are: {}", expected.join(", "))
715 })
716 }
717 AdapterError::InvalidStorageClusterSize { expected, .. } => {
718 Some(format!("Valid sizes are: {}", expected.join(", ")))
719 }
720 AdapterError::SourceOrSinkSizeRequired { expected } => Some(format!(
721 "Try choosing one of the smaller sizes to start. Available sizes: {}",
722 expected.join(", ")
723 )),
724 AdapterError::NoClusterReplicasAvailable { is_managed, .. } => {
725 Some(if *is_managed {
726 "Use ALTER CLUSTER to adjust the replication factor of the cluster. \
727 Example:`ALTER CLUSTER <cluster-name> SET (REPLICATION FACTOR 1)`".into()
728 } else {
729 "Use CREATE CLUSTER REPLICA to attach cluster replicas to the cluster".into()
730 })
731 }
732 AdapterError::UntargetedLogRead { .. } => Some(
733 "Use `SET cluster_replica = <replica-name>` to target a specific replica in the \
734 active cluster. Note that subsequent queries will only be answered by \
735 the selected replica, which might reduce availability. To undo the replica \
736 selection, use `RESET cluster_replica`."
737 .into(),
738 ),
739 AdapterError::ResourceExhaustion { resource_type, .. } => Some(format!(
740 "Drop an existing {resource_type} or contact support to request a limit increase."
741 )),
742 AdapterError::StatementTimeout => Some(
743 "Consider increasing the maximum allowed statement duration for this session by \
744 setting the statement_timeout session variable. For example, `SET \
745 statement_timeout = '120s'`."
746 .into(),
747 ),
748 AdapterError::PlanError(e) => e.hint(),
749 AdapterError::UnallowedOnCluster { cluster, .. } => {
750 (cluster != MZ_CATALOG_SERVER_CLUSTER.name).then(||
751 "Use `SET CLUSTER = <cluster-name>` to change your cluster and re-run the query."
752 .to_string()
753 )
754 }
755 AdapterError::InvalidAlter(_, e) => e.hint(),
756 AdapterError::Optimizer(e) => e.hint(),
757 AdapterError::ConnectionValidation(e) => e.hint(),
758 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => Some(
759 "You can use `REFRESH AT greatest(mz_now(), <explicit timestamp>)` to refresh \
760 either at the explicitly specified timestamp, or now if the given timestamp would \
761 be in the past.".to_string()
762 ),
763 AdapterError::AlterClusterTimeout => Some(
764 "Consider increasing the timeout duration in the alter cluster statement.".into(),
765 ),
766 AdapterError::DDLTransactionRace => Some(
767 "Currently, DDL transactions fail when any other DDL happens concurrently, \
768 even on unrelated schemas/clusters.".into()
769 ),
770 AdapterError::ConcurrentDependencyMutation { .. } => Some(
771 "Another session modified one of this statement's dependencies before \
772 it could commit. Retry the statement.".into()
773 ),
774 AdapterError::CollectionUnreadable { .. } => Some(
775 "This could be because the collection has recently been dropped.".into()
776 ),
777 _ => None,
778 }
779 }
780
781 pub fn code(&self) -> SqlState {
782 const RECURSION_LIMIT_ERROR_CODE: SqlState = SqlState::INTERNAL_ERROR;
784
785 match self {
790 AdapterError::AbsurdSubscribeBounds { .. } => SqlState::DATA_EXCEPTION,
793 AdapterError::AmbiguousSystemColumnReference => SqlState::FEATURE_NOT_SUPPORTED,
794 AdapterError::Catalog(e) => match &e.kind {
795 mz_catalog::memory::error::ErrorKind::VarError(e) => match e {
796 VarError::ConstrainedParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
797 VarError::FixedValueParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
798 VarError::InvalidParameterType { .. } => SqlState::INVALID_PARAMETER_VALUE,
799 VarError::InvalidParameterValue { .. } => SqlState::INVALID_PARAMETER_VALUE,
800 VarError::ReadOnlyParameter(_) => SqlState::CANT_CHANGE_RUNTIME_PARAM,
801 VarError::UnknownParameter(_) => SqlState::UNDEFINED_OBJECT,
802 VarError::RequiresUnsafeMode { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
803 VarError::RequiresFeatureFlag { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
804 },
805 _ => SqlState::INTERNAL_ERROR,
806 },
807 AdapterError::ChangedPlan(_) => SqlState::FEATURE_NOT_SUPPORTED,
808 AdapterError::DuplicateCursor(_) => SqlState::DUPLICATE_CURSOR,
809 AdapterError::Eval(e) => eval_error_code(e),
814 AdapterError::Explain(_) => SqlState::INTERNAL_ERROR,
815 AdapterError::IdExhaustionError => SqlState::INTERNAL_ERROR,
816 AdapterError::Internal(_) => SqlState::INTERNAL_ERROR,
817 AdapterError::IntrospectionDisabled { .. } => SqlState::FEATURE_NOT_SUPPORTED,
818 AdapterError::InvalidLogDependency { .. } => SqlState::FEATURE_NOT_SUPPORTED,
819 AdapterError::InvalidClusterReplicaAz { .. } => SqlState::FEATURE_NOT_SUPPORTED,
820 AdapterError::InvalidSetIsolationLevel => SqlState::ACTIVE_SQL_TRANSACTION,
821 AdapterError::InvalidSetCluster => SqlState::ACTIVE_SQL_TRANSACTION,
822 AdapterError::InvalidStorageClusterSize { .. } => SqlState::FEATURE_NOT_SUPPORTED,
823 AdapterError::SourceOrSinkSizeRequired { .. } => SqlState::FEATURE_NOT_SUPPORTED,
824 AdapterError::InvalidTableMutationSelection { .. } => {
825 SqlState::INVALID_TRANSACTION_STATE
826 }
827 AdapterError::ConstraintViolation(NotNullViolation(_)) => SqlState::NOT_NULL_VIOLATION,
828 AdapterError::CopyFormatError(_) => SqlState::BAD_COPY_FILE_FORMAT,
829 AdapterError::ConcurrentClusterDrop => SqlState::INVALID_TRANSACTION_STATE,
830 AdapterError::ConcurrentDependencyDrop { .. } => SqlState::UNDEFINED_OBJECT,
831 AdapterError::ConcurrentDependencyMutation { .. } => {
832 SqlState::T_R_SERIALIZATION_FAILURE
833 }
834 AdapterError::CollectionUnreadable { .. } => SqlState::NO_DATA_FOUND,
835 AdapterError::NoClusterReplicasAvailable { .. } => SqlState::FEATURE_NOT_SUPPORTED,
836 AdapterError::OperationProhibitsTransaction(_) => SqlState::ACTIVE_SQL_TRANSACTION,
837 AdapterError::OperationRequiresTransaction(_) => SqlState::NO_ACTIVE_SQL_TRANSACTION,
838 AdapterError::ParseError(_) => SqlState::SYNTAX_ERROR,
839 AdapterError::PlanError(PlanError::InvalidSchemaName) => SqlState::INVALID_SCHEMA_NAME,
840 AdapterError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
841 SqlState::DUPLICATE_COLUMN
842 }
843 AdapterError::PlanError(PlanError::UnknownParameter(_)) => {
844 SqlState::UNDEFINED_PARAMETER
845 }
846 AdapterError::PlanError(PlanError::ParameterNotAllowed(_)) => {
847 SqlState::UNDEFINED_PARAMETER
848 }
849 AdapterError::PlanError(_) => SqlState::INTERNAL_ERROR,
850 AdapterError::PreparedStatementExists(_) => SqlState::DUPLICATE_PSTATEMENT,
851 AdapterError::ReadOnlyTransaction => SqlState::READ_ONLY_SQL_TRANSACTION,
852 AdapterError::ReadWriteUnavailable => SqlState::INVALID_TRANSACTION_STATE,
853 AdapterError::SingleStatementTransaction => SqlState::INVALID_TRANSACTION_STATE,
854 AdapterError::WrongSetOfLocks => SqlState::LOCK_NOT_AVAILABLE,
855 AdapterError::StatementTimeout => SqlState::QUERY_CANCELED,
856 AdapterError::Canceled => SqlState::QUERY_CANCELED,
857 AdapterError::IdleInTransactionSessionTimeout => {
858 SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT
859 }
860 AdapterError::RecursionLimit(_) => RECURSION_LIMIT_ERROR_CODE,
861 AdapterError::RelationOutsideTimeDomain { .. } => SqlState::INVALID_TRANSACTION_STATE,
862 AdapterError::ResourceExhaustion { .. } => SqlState::INSUFFICIENT_RESOURCES,
863 AdapterError::ResultSize(_) => SqlState::OUT_OF_MEMORY,
864 AdapterError::SafeModeViolation(_) => SqlState::INTERNAL_ERROR,
865 AdapterError::SubscribeOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
866 AdapterError::Optimizer(e) => match e {
867 OptimizerError::PlanError(PlanError::InvalidSchemaName) => {
868 SqlState::INVALID_SCHEMA_NAME
869 }
870 OptimizerError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
871 SqlState::DUPLICATE_COLUMN
872 }
873 OptimizerError::PlanError(PlanError::UnknownParameter(_)) => {
874 SqlState::UNDEFINED_PARAMETER
875 }
876 OptimizerError::PlanError(PlanError::ParameterNotAllowed(_)) => {
877 SqlState::UNDEFINED_PARAMETER
878 }
879 OptimizerError::PlanError(_) => SqlState::INTERNAL_ERROR,
880 OptimizerError::RecursionLimitError(_) => RECURSION_LIMIT_ERROR_CODE,
881 OptimizerError::Internal(s) => {
882 AdapterError::Internal(s.clone()).code() }
884 OptimizerError::EvalError(e) => {
885 AdapterError::Eval(e.clone()).code() }
887 OptimizerError::TransformError(_) => SqlState::INTERNAL_ERROR,
888 OptimizerError::UnmaterializableFunction(_) => SqlState::FEATURE_NOT_SUPPORTED,
889 OptimizerError::UncallableFunction { .. } => SqlState::FEATURE_NOT_SUPPORTED,
890 OptimizerError::UnsupportedTemporalExpression(_) => SqlState::FEATURE_NOT_SUPPORTED,
891 OptimizerError::RestrictedFunction(_) => SqlState::INSUFFICIENT_PRIVILEGE,
892 OptimizerError::InternalUnsafeMfpPlan(_) => SqlState::INTERNAL_ERROR,
895 },
896 AdapterError::UnallowedOnCluster { .. } => {
897 SqlState::S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED
898 }
899 AdapterError::Unauthorized(_) => SqlState::INSUFFICIENT_PRIVILEGE,
900 AdapterError::UnknownCursor(_) => SqlState::INVALID_CURSOR_NAME,
901 AdapterError::UnknownPreparedStatement(_) => SqlState::UNDEFINED_PSTATEMENT,
902 AdapterError::UnknownLoginRole(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
903 AdapterError::UnknownClusterReplica { .. } => SqlState::UNDEFINED_OBJECT,
904 AdapterError::UnrecognizedConfigurationParam(_) => SqlState::UNDEFINED_OBJECT,
905 AdapterError::Unsupported(..) => SqlState::FEATURE_NOT_SUPPORTED,
906 AdapterError::UnavailableFeature { .. } => SqlState::FEATURE_NOT_SUPPORTED,
907 AdapterError::Unstructured(_) => SqlState::INTERNAL_ERROR,
908 AdapterError::UntargetedLogRead { .. } => SqlState::FEATURE_NOT_SUPPORTED,
909 AdapterError::DDLTransactionRace => SqlState::T_R_SERIALIZATION_FAILURE,
910 AdapterError::ClusterStateChanged { .. } => SqlState::T_R_SERIALIZATION_FAILURE,
911 AdapterError::WriteOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
916 AdapterError::DDLOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
917 AdapterError::Storage(_) | AdapterError::Compute(_) | AdapterError::Orchestrator(_) => {
918 SqlState::INTERNAL_ERROR
919 }
920 AdapterError::DependentObject(_) => SqlState::DEPENDENT_OBJECTS_STILL_EXIST,
921 AdapterError::InvalidAlter(_, _) => SqlState::FEATURE_NOT_SUPPORTED,
922 AdapterError::ConnectionValidation(_) => SqlState::SYSTEM_ERROR,
923 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => SqlState::DATA_EXCEPTION,
925 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => SqlState::DATA_EXCEPTION,
926 AdapterError::RtrTimeout(_) => SqlState::QUERY_CANCELED,
927 AdapterError::RtrDropFailure(_) => SqlState::UNDEFINED_OBJECT,
928 AdapterError::UnreadableSinkCollection => SqlState::from_code("MZ009"),
929 AdapterError::UserSessionsDisallowed => SqlState::from_code("MZ010"),
930 AdapterError::NetworkPolicyDenied(_) => SqlState::from_code("MZ011"),
931 AdapterError::ReadOnly => SqlState::READ_ONLY_SQL_TRANSACTION,
934 AdapterError::AlterClusterTimeout => SqlState::QUERY_CANCELED,
935 AdapterError::AlterClusterWhilePendingReplicas => SqlState::OBJECT_IN_USE,
936 AdapterError::ReplacementSchemaMismatch(_) => SqlState::FEATURE_NOT_SUPPORTED,
937 AdapterError::AuthenticationError(AuthenticationError::InvalidCredentials) => {
938 SqlState::INVALID_PASSWORD
939 }
940 AdapterError::AuthenticationError(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
941 AdapterError::ReplaceMaterializedViewSealed { .. } => {
942 SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE
943 }
944 AdapterError::ImpossibleTimestampConstraints { .. } => SqlState::DATA_EXCEPTION,
946 AdapterError::OidcGroupSyncFailed(_) => SqlState::INTERNAL_ERROR,
947 AdapterError::BoundedStalenessExceeded { .. } => SqlState::T_R_SERIALIZATION_FAILURE,
948 AdapterError::BoundedStalenessReadOnly => SqlState::READ_ONLY_SQL_TRANSACTION,
951 AdapterError::BoundedStalenessRealTimeRecencyConflict => {
952 SqlState::FEATURE_NOT_SUPPORTED
953 }
954 AdapterError::BoundedStalenessTimelineUnsupported => SqlState::FEATURE_NOT_SUPPORTED,
955 }
956 }
957
958 pub fn internal<E: std::fmt::Display>(context: &str, e: E) -> AdapterError {
959 AdapterError::Internal(format!("{context}: {e}"))
960 }
961
962 pub fn concurrent_dependency_drop_from_instance_missing(e: InstanceMissing) -> Self {
969 AdapterError::ConcurrentDependencyDrop {
970 dependency_kind: "cluster",
971 dependency_id: e.0.to_string(),
972 }
973 }
974
975 pub fn concurrent_dependency_drop_from_collection_missing(e: CollectionMissing) -> Self {
976 AdapterError::ConcurrentDependencyDrop {
977 dependency_kind: "collection",
978 dependency_id: e.0.to_string(),
979 }
980 }
981
982 pub fn concurrent_dependency_drop_from_collection_lookup_error(
983 e: CollectionLookupError,
984 compute_instance: ComputeInstanceId,
985 ) -> Self {
986 match e {
987 CollectionLookupError::InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
988 dependency_kind: "cluster",
989 dependency_id: id.to_string(),
990 },
991 CollectionLookupError::CollectionMissing(id) => {
992 AdapterError::ConcurrentDependencyDrop {
993 dependency_kind: "collection",
994 dependency_id: id.to_string(),
995 }
996 }
997 CollectionLookupError::InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
998 dependency_kind: "cluster",
999 dependency_id: compute_instance.to_string(),
1000 },
1001 }
1002 }
1003
1004 pub fn concurrent_dependency_drop_from_watch_set_install_error(
1005 e: compute_error::CollectionLookupError,
1006 ) -> Self {
1007 match e {
1008 compute_error::CollectionLookupError::InstanceMissing(id) => {
1009 AdapterError::ConcurrentDependencyDrop {
1010 dependency_kind: "cluster",
1011 dependency_id: id.to_string(),
1012 }
1013 }
1014 compute_error::CollectionLookupError::CollectionMissing(id) => {
1015 AdapterError::ConcurrentDependencyDrop {
1016 dependency_kind: "collection",
1017 dependency_id: id.to_string(),
1018 }
1019 }
1020 }
1021 }
1022
1023 pub fn concurrent_dependency_drop_from_instance_peek_error(
1024 e: mz_compute_client::controller::instance_client::PeekError,
1025 compute_instance: ComputeInstanceId,
1026 ) -> AdapterError {
1027 use mz_compute_client::controller::instance_client::PeekError::*;
1028 match e {
1029 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1030 dependency_kind: "replica",
1031 dependency_id: id.to_string(),
1032 },
1033 InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
1034 dependency_kind: "cluster",
1035 dependency_id: compute_instance.to_string(),
1036 },
1037 e @ ReadHoldIdMismatch(_) => AdapterError::internal("instance peek error", e),
1038 e @ ReadHoldInsufficient(_) => AdapterError::internal("instance peek error", e),
1039 }
1040 }
1041
1042 pub fn concurrent_dependency_drop_from_collection_update_error(
1043 e: compute_error::CollectionUpdateError,
1044 ) -> Self {
1045 use compute_error::CollectionUpdateError::*;
1046 match e {
1047 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1048 dependency_kind: "cluster",
1049 dependency_id: id.to_string(),
1050 },
1051 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1052 dependency_kind: "collection",
1053 dependency_id: id.to_string(),
1054 },
1055 }
1056 }
1057
1058 pub fn concurrent_dependency_drop_from_peek_error(
1059 e: mz_compute_client::controller::error::PeekError,
1060 ) -> AdapterError {
1061 use mz_compute_client::controller::error::PeekError::*;
1062 match e {
1063 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1064 dependency_kind: "cluster",
1065 dependency_id: id.to_string(),
1066 },
1067 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1068 dependency_kind: "collection",
1069 dependency_id: id.to_string(),
1070 },
1071 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1072 dependency_kind: "replica",
1073 dependency_id: id.to_string(),
1074 },
1075 e @ (ReadHoldIdMismatch(_) | SinceViolation(_)) => {
1076 AdapterError::internal("peek error", e)
1077 }
1078 }
1079 }
1080
1081 pub fn concurrent_dependency_drop_from_dataflow_creation_error(
1082 e: compute_error::DataflowCreationError,
1083 ) -> Self {
1084 use compute_error::DataflowCreationError::*;
1085 match e {
1086 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1087 dependency_kind: "cluster",
1088 dependency_id: id.to_string(),
1089 },
1090 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1091 dependency_kind: "collection",
1092 dependency_id: id.to_string(),
1093 },
1094 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1095 dependency_kind: "replica",
1096 dependency_id: id.to_string(),
1097 },
1098 MissingAsOf | SinceViolation(..) | EmptyAsOfForSubscribe | EmptyAsOfForCopyTo => {
1099 AdapterError::internal("dataflow creation error", e)
1100 }
1101 }
1102 }
1103}
1104
1105impl fmt::Display for AdapterError {
1106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1107 match self {
1108 AdapterError::AbsurdSubscribeBounds { as_of, up_to } => {
1109 write!(
1110 f,
1111 "subscription lower bound (`AS OF`) is greater than its upper bound (`UP TO`): \
1112 {as_of} > {up_to}",
1113 )
1114 }
1115 AdapterError::AmbiguousSystemColumnReference => {
1116 write!(
1117 f,
1118 "cannot use wildcard expansions or NATURAL JOINs in a view that depends on \
1119 system objects"
1120 )
1121 }
1122 AdapterError::ChangedPlan(e) => write!(f, "{}", e),
1123 AdapterError::Catalog(e) => e.fmt(f),
1124 AdapterError::DuplicateCursor(name) => {
1125 write!(f, "cursor {} already exists", name.quoted())
1126 }
1127 AdapterError::Eval(e) => e.fmt(f),
1128 AdapterError::Explain(e) => e.fmt(f),
1129 AdapterError::IdExhaustionError => f.write_str("ID allocator exhausted all valid IDs"),
1130 AdapterError::Internal(e) => write!(f, "internal error: {}", e),
1131 AdapterError::IntrospectionDisabled { .. } => write!(
1132 f,
1133 "cannot read log sources of replica with disabled introspection"
1134 ),
1135 AdapterError::InvalidLogDependency { object_type, .. } => {
1136 write!(f, "{object_type} objects cannot depend on log sources")
1137 }
1138 AdapterError::InvalidClusterReplicaAz { az, expected: _ } => {
1139 write!(f, "unknown cluster replica availability zone {az}",)
1140 }
1141 AdapterError::InvalidSetIsolationLevel => write!(
1142 f,
1143 "SET TRANSACTION ISOLATION LEVEL must be called before any query"
1144 ),
1145 AdapterError::InvalidSetCluster => {
1146 write!(f, "SET cluster cannot be called in an active transaction")
1147 }
1148 AdapterError::InvalidStorageClusterSize { size, .. } => {
1149 write!(f, "unknown source size {size}")
1150 }
1151 AdapterError::SourceOrSinkSizeRequired { .. } => {
1152 write!(f, "must specify either cluster or size option")
1153 }
1154 AdapterError::InvalidTableMutationSelection { .. } => {
1155 write!(
1156 f,
1157 "invalid selection: operation may only (transitively) refer to non-source, non-system tables"
1158 )
1159 }
1160 AdapterError::ReplaceMaterializedViewSealed { name } => {
1161 write!(
1162 f,
1163 "materialized view {name} is sealed and thus cannot be replaced"
1164 )
1165 }
1166 AdapterError::ConstraintViolation(not_null_violation) => {
1167 write!(f, "{}", not_null_violation)
1168 }
1169 AdapterError::CopyFormatError(e) => write!(f, "{e}"),
1170 AdapterError::ConcurrentClusterDrop => {
1171 write!(f, "the transaction's active cluster has been dropped")
1172 }
1173 AdapterError::ConcurrentDependencyDrop {
1174 dependency_kind,
1175 dependency_id,
1176 } => {
1177 write!(f, "{dependency_kind} '{dependency_id}' was dropped")
1178 }
1179 AdapterError::ConcurrentDependencyMutation { dependency_id } => {
1180 write!(
1181 f,
1182 "catalog item '{dependency_id}' was concurrently modified"
1183 )
1184 }
1185 AdapterError::CollectionUnreadable { id } => {
1186 write!(f, "collection '{id}' is not readable at any timestamp")
1187 }
1188 AdapterError::NoClusterReplicasAvailable { name, .. } => {
1189 write!(
1190 f,
1191 "CLUSTER {} has no replicas available to service request",
1192 name.quoted()
1193 )
1194 }
1195 AdapterError::OperationProhibitsTransaction(op) => {
1196 write!(f, "{} cannot be run inside a transaction block", op)
1197 }
1198 AdapterError::OperationRequiresTransaction(op) => {
1199 write!(f, "{} can only be used in transaction blocks", op)
1200 }
1201 AdapterError::ParseError(e) => e.fmt(f),
1202 AdapterError::PlanError(e) => e.fmt(f),
1203 AdapterError::PreparedStatementExists(name) => {
1204 write!(f, "prepared statement {} already exists", name.quoted())
1205 }
1206 AdapterError::ReadOnlyTransaction => f.write_str("transaction in read-only mode"),
1207 AdapterError::SingleStatementTransaction => {
1208 f.write_str("this transaction can only execute a single statement")
1209 }
1210 AdapterError::ReadWriteUnavailable => {
1211 f.write_str("transaction read-write mode must be set before any query")
1212 }
1213 AdapterError::WrongSetOfLocks => {
1214 write!(f, "internal error, wrong set of locks acquired")
1215 }
1216 AdapterError::StatementTimeout => {
1217 write!(f, "canceling statement due to statement timeout")
1218 }
1219 AdapterError::Canceled => {
1220 write!(f, "canceling statement due to user request")
1221 }
1222 AdapterError::IdleInTransactionSessionTimeout => {
1223 write!(
1224 f,
1225 "terminating connection due to idle-in-transaction timeout"
1226 )
1227 }
1228 AdapterError::RecursionLimit(e) => e.fmt(f),
1229 AdapterError::RelationOutsideTimeDomain { .. } => {
1230 write!(
1231 f,
1232 "Transactions can only reference objects in the same timedomain. \
1233 See https://materialize.com/docs/sql/begin/#same-timedomain-error",
1234 )
1235 }
1236 AdapterError::ResourceExhaustion {
1237 resource_type,
1238 limit_name,
1239 desired,
1240 limit,
1241 current,
1242 } => {
1243 write!(
1244 f,
1245 "creating {resource_type} would violate {limit_name} limit (desired: {desired}, limit: {limit}, current: {current})"
1246 )
1247 }
1248 AdapterError::ResultSize(e) => write!(f, "{e}"),
1249 AdapterError::SafeModeViolation(feature) => {
1250 write!(f, "cannot create {} in safe mode", feature)
1251 }
1252 AdapterError::SubscribeOnlyTransaction => {
1253 f.write_str("SUBSCRIBE in transactions must be the only read statement")
1254 }
1255 AdapterError::Optimizer(e) => e.fmt(f),
1256 AdapterError::UnallowedOnCluster {
1257 depends_on,
1258 cluster,
1259 } => {
1260 let items = depends_on.into_iter().map(|item| item.quoted()).join(", ");
1261 write!(
1262 f,
1263 "querying the following items {items} is not allowed from the {} cluster",
1264 cluster.quoted()
1265 )
1266 }
1267 AdapterError::Unauthorized(unauthorized) => {
1268 write!(f, "{unauthorized}")
1269 }
1270 AdapterError::UnknownCursor(name) => {
1271 write!(f, "cursor {} does not exist", name.quoted())
1272 }
1273 AdapterError::UnknownLoginRole(name) => {
1274 write!(f, "role {} does not exist", name.quoted())
1275 }
1276 AdapterError::Unsupported(features) => write!(f, "{} are not supported", features),
1277 AdapterError::Unstructured(e) => write!(f, "{}", e.display_with_causes()),
1278 AdapterError::WriteOnlyTransaction => f.write_str("transaction in write-only mode"),
1279 AdapterError::UnknownPreparedStatement(name) => {
1280 write!(f, "prepared statement {} does not exist", name.quoted())
1281 }
1282 AdapterError::UnknownClusterReplica {
1283 cluster_name,
1284 replica_name,
1285 } => write!(
1286 f,
1287 "cluster replica '{cluster_name}.{replica_name}' does not exist"
1288 ),
1289 AdapterError::UnrecognizedConfigurationParam(setting_name) => write!(
1290 f,
1291 "unrecognized configuration parameter {}",
1292 setting_name.quoted()
1293 ),
1294 AdapterError::UntargetedLogRead { .. } => {
1295 f.write_str("log source reads must target a replica")
1296 }
1297 AdapterError::DDLOnlyTransaction => f.write_str(
1298 "transactions which modify objects are restricted to just modifying objects",
1299 ),
1300 AdapterError::DDLTransactionRace => f.write_str(
1301 "another session modified the catalog while this DDL transaction was open",
1302 ),
1303 AdapterError::ClusterStateChanged { cluster_id } => {
1304 write!(f, "cluster {cluster_id} was concurrently modified")
1305 }
1306 AdapterError::Storage(e) => e.fmt(f),
1307 AdapterError::Compute(e) => e.fmt(f),
1308 AdapterError::Orchestrator(e) => e.fmt(f),
1309 AdapterError::DependentObject(dependent_objects) => {
1310 let role_str = if dependent_objects.keys().count() == 1 {
1311 "role"
1312 } else {
1313 "roles"
1314 };
1315 write!(
1316 f,
1317 "{role_str} \"{}\" cannot be dropped because some objects depend on it",
1318 dependent_objects.keys().join(", ")
1319 )
1320 }
1321 AdapterError::InvalidAlter(t, e) => {
1322 write!(f, "invalid ALTER {t}: {e}")
1323 }
1324 AdapterError::ConnectionValidation(e) => e.fmt(f),
1325 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => {
1326 write!(
1327 f,
1328 "all the specified refreshes of the materialized view would be too far in the past, and thus they \
1329 would never happen"
1330 )
1331 }
1332 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => {
1333 write!(
1334 f,
1335 "REFRESH AT requested for a time where not all the inputs are readable"
1336 )
1337 }
1338 AdapterError::RtrTimeout(_) => {
1339 write!(
1340 f,
1341 "timed out before ingesting the source's visible frontier when real-time-recency query issued"
1342 )
1343 }
1344 AdapterError::RtrDropFailure(_) => write!(
1345 f,
1346 "real-time source dropped before ingesting the upstream system's visible frontier"
1347 ),
1348 AdapterError::UnreadableSinkCollection => {
1349 write!(f, "collection is not readable at any time")
1350 }
1351 AdapterError::UserSessionsDisallowed => write!(f, "login blocked"),
1352 AdapterError::NetworkPolicyDenied(_) => write!(f, "session denied"),
1353 AdapterError::ReadOnly => write!(f, "cannot write in read-only mode"),
1354 AdapterError::AlterClusterTimeout => {
1355 write!(f, "canceling statement, provided timeout lapsed")
1356 }
1357 AdapterError::AuthenticationError(e) => {
1358 write!(f, "authentication error {e}")
1359 }
1360 AdapterError::UnavailableFeature { feature, docs } => {
1361 write!(f, "{} is not supported in this environment.", feature)?;
1362 if let Some(docs) = docs {
1363 write!(
1364 f,
1365 " For more information consult the documentation at {docs}"
1366 )?;
1367 }
1368 Ok(())
1369 }
1370 AdapterError::AlterClusterWhilePendingReplicas => {
1371 write!(f, "cannot alter clusters with pending updates")
1372 }
1373 AdapterError::ReplacementSchemaMismatch(_) => {
1374 write!(f, "replacement schema differs from target schema")
1375 }
1376 AdapterError::ImpossibleTimestampConstraints { .. } => {
1377 write!(f, "could not find a valid timestamp for the query")
1378 }
1379 AdapterError::OidcGroupSyncFailed(msg) => {
1380 write!(f, "OIDC group-to-role sync failed: {msg}")
1381 }
1382 AdapterError::BoundedStalenessExceeded { bound, .. } => {
1383 write!(
1384 f,
1385 "cannot serve query under bounded staleness {}",
1386 humantime::format_duration(*bound),
1387 )
1388 }
1389 AdapterError::BoundedStalenessReadOnly => {
1390 f.write_str("writes are not permitted under bounded staleness isolation")
1391 }
1392 AdapterError::BoundedStalenessRealTimeRecencyConflict => {
1393 f.write_str("real_time_recency cannot be combined with bounded staleness isolation")
1394 }
1395 AdapterError::BoundedStalenessTimelineUnsupported => {
1396 f.write_str("bounded staleness isolation requires the EpochMilliseconds timeline")
1397 }
1398 }
1399 }
1400}
1401
1402impl From<anyhow::Error> for AdapterError {
1403 fn from(e: anyhow::Error) -> AdapterError {
1404 match e.downcast::<PlanError>() {
1405 Ok(plan_error) => AdapterError::PlanError(plan_error),
1406 Err(e) => AdapterError::Unstructured(e),
1407 }
1408 }
1409}
1410
1411impl From<TryFromIntError> for AdapterError {
1412 fn from(e: TryFromIntError) -> AdapterError {
1413 AdapterError::Unstructured(e.into())
1414 }
1415}
1416
1417impl From<TryFromDecimalError> for AdapterError {
1418 fn from(e: TryFromDecimalError) -> AdapterError {
1419 AdapterError::Unstructured(e.into())
1420 }
1421}
1422
1423impl From<mz_catalog::memory::error::Error> for AdapterError {
1424 fn from(e: mz_catalog::memory::error::Error) -> AdapterError {
1425 AdapterError::Catalog(e)
1426 }
1427}
1428
1429impl From<mz_catalog::durable::CatalogError> for AdapterError {
1430 fn from(e: mz_catalog::durable::CatalogError) -> Self {
1431 mz_catalog::memory::error::Error::from(e).into()
1432 }
1433}
1434
1435impl From<mz_catalog::durable::DurableCatalogError> for AdapterError {
1436 fn from(e: mz_catalog::durable::DurableCatalogError) -> Self {
1437 mz_catalog::durable::CatalogError::from(e).into()
1438 }
1439}
1440
1441impl From<EvalError> for AdapterError {
1442 fn from(e: EvalError) -> AdapterError {
1443 AdapterError::Eval(e)
1444 }
1445}
1446
1447impl From<ExplainError> for AdapterError {
1448 fn from(e: ExplainError) -> AdapterError {
1449 match e {
1450 ExplainError::RecursionLimitError(e) => AdapterError::RecursionLimit(e),
1451 e => AdapterError::Explain(e),
1452 }
1453 }
1454}
1455
1456impl From<mz_sql::catalog::CatalogError> for AdapterError {
1457 fn from(e: mz_sql::catalog::CatalogError) -> AdapterError {
1458 AdapterError::Catalog(mz_catalog::memory::error::Error::from(e))
1459 }
1460}
1461
1462impl From<PlanError> for AdapterError {
1463 fn from(e: PlanError) -> AdapterError {
1464 match e {
1465 PlanError::UnknownCursor(name) => AdapterError::UnknownCursor(name),
1466 _ => AdapterError::PlanError(e),
1467 }
1468 }
1469}
1470
1471impl From<OptimizerError> for AdapterError {
1472 fn from(e: OptimizerError) -> AdapterError {
1473 use OptimizerError::*;
1474 match e {
1475 PlanError(e) => Self::PlanError(e),
1476 RecursionLimitError(e) => Self::RecursionLimit(e),
1477 EvalError(e) => Self::Eval(e),
1478 InternalUnsafeMfpPlan(e) => Self::Internal(e),
1479 Internal(e) => Self::Internal(e),
1480 RestrictedFunction(func) => {
1481 Self::Unauthorized(mz_sql::rbac::UnauthorizedError::RestrictedSystemObject {
1482 object_name: format!("function {func}"),
1483 })
1484 }
1485 e => Self::Optimizer(e),
1486 }
1487 }
1488}
1489
1490impl From<NotNullViolation> for AdapterError {
1491 fn from(e: NotNullViolation) -> AdapterError {
1492 AdapterError::ConstraintViolation(e)
1493 }
1494}
1495
1496impl From<RecursionLimitError> for AdapterError {
1497 fn from(e: RecursionLimitError) -> AdapterError {
1498 AdapterError::RecursionLimit(e)
1499 }
1500}
1501
1502impl From<oneshot::error::RecvError> for AdapterError {
1503 fn from(e: oneshot::error::RecvError) -> AdapterError {
1504 AdapterError::Unstructured(e.into())
1505 }
1506}
1507
1508impl From<StorageError> for AdapterError {
1509 fn from(e: StorageError) -> Self {
1510 AdapterError::Storage(e)
1511 }
1512}
1513
1514impl From<compute_error::InstanceExists> for AdapterError {
1515 fn from(e: compute_error::InstanceExists) -> Self {
1516 AdapterError::Compute(e.into())
1517 }
1518}
1519
1520impl From<TimestampError> for AdapterError {
1521 fn from(e: TimestampError) -> Self {
1522 let e: EvalError = e.into();
1523 e.into()
1524 }
1525}
1526
1527impl From<mz_sql_parser::parser::ParserStatementError> for AdapterError {
1528 fn from(e: mz_sql_parser::parser::ParserStatementError) -> Self {
1529 AdapterError::ParseError(e)
1530 }
1531}
1532
1533impl From<VarError> for AdapterError {
1534 fn from(e: VarError) -> Self {
1535 let e: mz_catalog::memory::error::Error = e.into();
1536 e.into()
1537 }
1538}
1539
1540impl From<rbac::UnauthorizedError> for AdapterError {
1541 fn from(e: rbac::UnauthorizedError) -> Self {
1542 AdapterError::Unauthorized(e)
1543 }
1544}
1545
1546impl From<mz_sql_parser::ast::IdentError> for AdapterError {
1547 fn from(value: mz_sql_parser::ast::IdentError) -> Self {
1548 AdapterError::PlanError(PlanError::InvalidIdent(value))
1549 }
1550}
1551
1552impl From<mz_pgwire_common::ConnectionError> for AdapterError {
1553 fn from(value: mz_pgwire_common::ConnectionError) -> Self {
1554 match value {
1555 mz_pgwire_common::ConnectionError::TooManyConnections { current, limit } => {
1556 AdapterError::ResourceExhaustion {
1557 resource_type: "connection".into(),
1558 limit_name: "max_connections".into(),
1559 desired: (current + 1).to_string(),
1560 limit: limit.to_string(),
1561 current: current.to_string(),
1562 }
1563 }
1564 }
1565 }
1566}
1567
1568impl From<NetworkPolicyError> for AdapterError {
1569 fn from(value: NetworkPolicyError) -> Self {
1570 AdapterError::NetworkPolicyDenied(value)
1571 }
1572}
1573
1574impl From<ConnectionValidationError> for AdapterError {
1575 fn from(e: ConnectionValidationError) -> AdapterError {
1576 AdapterError::ConnectionValidation(e)
1577 }
1578}
1579
1580impl Error for AdapterError {}