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::cast::CastFrom;
25use mz_ore::error::ErrorExt;
26use mz_ore::stack::RecursionLimitError;
27use mz_ore::str::StrExt;
28use mz_pgwire_common::{ErrorResponse, Severity};
29use mz_repr::adt::array::InvalidArrayError;
30use mz_repr::adt::range::InvalidRangeError;
31use mz_repr::adt::timestamp::TimestampError;
32use mz_repr::explain::ExplainError;
33use mz_repr::strconv::{ParseError, ParseErrorKind};
34use mz_repr::{ColumnDiff, ColumnName, KeyDiff, NotNullViolation, RelationDescDiff, Timestamp};
35use mz_sql::plan::PlanError;
36use mz_sql::rbac;
37use mz_sql::session::vars::VarError;
38use mz_storage_types::connections::ConnectionValidationError;
39use mz_storage_types::controller::StorageError;
40use mz_storage_types::errors::CollectionMissing;
41use smallvec::SmallVec;
42use timely::progress::Antichain;
43use tokio::sync::oneshot;
44use tokio_postgres::error::SqlState;
45
46use crate::coord::NetworkPolicyError;
47use crate::optimize::OptimizerError;
48use crate::peek_client::CollectionLookupError;
49
50#[derive(Debug)]
52pub enum AdapterError {
53 AbsurdSubscribeBounds {
55 as_of: mz_repr::Timestamp,
56 up_to: mz_repr::Timestamp,
57 },
58 AmbiguousSystemColumnReference,
62 Catalog(mz_catalog::memory::error::Error),
64 ChangedPlan(String),
69 DuplicateCursor(String),
71 Eval(EvalError),
73 Explain(ExplainError),
75 IdExhaustionError,
77 Internal(String),
79 IntrospectionDisabled {
81 log_names: Vec<String>,
82 },
83 InvalidLogDependency {
86 object_type: String,
87 log_names: Vec<String>,
88 },
89 InvalidClusterReplicaAz {
91 az: String,
92 expected: Vec<String>,
93 },
94 InvalidSetIsolationLevel,
96 InvalidSetCluster,
98 InvalidStorageClusterSize {
100 size: String,
101 expected: Vec<String>,
102 },
103 SourceOrSinkSizeRequired {
105 expected: Vec<String>,
106 },
107 InvalidTableMutationSelection {
109 object_name: String,
111 object_type: String,
113 },
114 ReadThenWriteDependencyLimitExceeded {
117 max_rw_dependencies: usize,
119 },
120 ConstraintViolation(NotNullViolation),
122 CopyFormatError(String),
124 ConcurrentClusterDrop,
126 ConcurrentDependencyDrop {
128 dependency_kind: &'static str,
129 dependency_id: String,
130 },
131 ConcurrentDependencyMutation {
137 dependency_id: String,
138 },
139 ReadThenWriteContention,
144 ReadThenWriteTimestampTooFarAhead {
152 target_timestamp: mz_repr::Timestamp,
153 limit: mz_repr::Timestamp,
154 },
155 CollectionUnreadable {
156 id: String,
157 },
158 NoClusterReplicasAvailable {
160 name: String,
161 is_managed: bool,
162 },
163 OperationProhibitsTransaction(String),
165 OperationRequiresTransaction(String),
167 PlanError(PlanError),
169 PreparedStatementExists(String),
171 ParseError(mz_sql_parser::parser::ParserStatementError),
173 ReadOnlyTransaction,
175 ReadWriteUnavailable,
177 RecursionLimit(RecursionLimitError),
179 RelationOutsideTimeDomain {
182 relations: Vec<String>,
183 names: Vec<String>,
184 },
185 ResourceExhaustion {
187 resource_type: String,
188 limit_name: String,
189 desired: String,
190 limit: String,
191 current: String,
192 },
193 ResultSize(String),
195 SubscribeFellBehind {
200 buffered_bytes: usize,
202 max_buffered_bytes: usize,
204 },
205 SafeModeViolation(String),
207 WrongSetOfLocks,
209 StatementTimeout,
213 Canceled,
215 IdleInTransactionSessionTimeout,
217 SubscribeOnlyTransaction,
219 Optimizer(OptimizerError),
221 UnallowedOnCluster {
223 depends_on: SmallVec<[String; 2]>,
224 cluster: String,
225 },
226 Unauthorized(rbac::UnauthorizedError),
228 UnknownCursor(String),
230 UnknownLoginRole(String),
232 UnknownPreparedStatement(String),
233 UnknownClusterReplica {
235 cluster_name: String,
236 replica_name: String,
237 },
238 UnrecognizedConfigurationParam(String),
240 Unstructured(anyhow::Error),
244 Unsupported(&'static str),
246 UnavailableFeature {
250 feature: String,
251 docs: Option<String>,
252 },
253 UntargetedLogRead {
255 log_names: Vec<String>,
256 },
257 WriteOnlyTransaction,
259 SingleStatementTransaction,
261 DDLOnlyTransaction,
263 DDLTransactionRace,
265 ClusterStateChanged {
270 cluster_id: ClusterId,
271 },
272 Storage(mz_storage_types::controller::StorageError),
274 Compute(anyhow::Error),
276 Orchestrator(anyhow::Error),
278 DependentObject(BTreeMap<String, Vec<String>>),
282 InvalidAlter(&'static str, PlanError),
285 ConnectionValidation(ConnectionValidationError),
287 MaterializedViewWouldNeverRefresh(Timestamp, Timestamp),
291 InputNotReadableAtRefreshAtTime(Timestamp, Antichain<Timestamp>),
294 RtrTimeout(String),
296 RtrDropFailure(String),
298 UnreadableSinkCollection,
300 UserSessionsDisallowed,
302 NetworkPolicyDenied(NetworkPolicyError),
304 ReadOnly,
307 AlterClusterTimeout,
308 AlterClusterWhilePendingReplicas,
309 AlterClusterUnmanagedWhileReconfiguring,
312 AlterClusterUnmanagedWhileBursting,
315 AlterClusterReplicationFactorWhileReconfiguring,
318 AlterClusterScheduleWhileReconfiguring,
321 AlterClusterWaitOnScheduledCluster,
324 AuthenticationError(AuthenticationError),
325 ReplacementSchemaMismatch(RelationDescDiff),
327 ReplaceMaterializedViewSealed {
329 name: String,
330 },
331 ImpossibleTimestampConstraints {
333 constraints: String,
334 },
335 OidcGroupSyncFailed(String),
337 BoundedStalenessExceeded {
341 bound: std::time::Duration,
342 gap_ms: u64,
343 slowest_input: Option<mz_repr::GlobalId>,
344 },
345 BoundedStalenessReadOnly,
348 BoundedStalenessRealTimeRecencyConflict,
351 BoundedStalenessTimelineUnsupported,
354}
355
356#[derive(Debug, thiserror::Error)]
357pub enum AuthenticationError {
358 #[error("invalid credentials")]
359 InvalidCredentials,
360 #[error("role is not allowed to login")]
361 NonLogin,
362 #[error("role does not exist")]
363 RoleNotFound,
364 #[error("password is required")]
365 PasswordRequired,
366}
367
368fn parse_error_code(err: &ParseError) -> SqlState {
373 let is_datetime = matches!(
374 &*err.type_name,
375 "date" | "time" | "timestamp" | "timestamp with time zone" | "interval"
376 );
377 match (err.kind, is_datetime) {
378 (ParseErrorKind::OutOfRange, true) => SqlState::DATETIME_FIELD_OVERFLOW,
379 (ParseErrorKind::OutOfRange, false) => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
380 (ParseErrorKind::InvalidInputSyntax, true) => SqlState::INVALID_DATETIME_FORMAT,
381 (ParseErrorKind::InvalidInputSyntax, false) => SqlState::INVALID_TEXT_REPRESENTATION,
382 }
383}
384
385fn eval_error_code(err: &EvalError) -> SqlState {
395 match err {
396 EvalError::DivisionByZero => SqlState::DIVISION_BY_ZERO,
398 EvalError::NegSqrt | EvalError::ComplexOutOfRange(_) => {
399 SqlState::INVALID_ARGUMENT_FOR_POWER_FUNCTION
400 }
401 EvalError::InfinityOutOfDomain(_)
402 | EvalError::NegativeOutOfDomain(_)
403 | EvalError::ZeroOutOfDomain(_)
404 | EvalError::OutOfDomain(..)
405 | EvalError::Undefined(_) => SqlState::INVALID_PARAMETER_VALUE,
406
407 EvalError::FloatOverflow
409 | EvalError::FloatUnderflow
410 | EvalError::NumericFieldOverflow
411 | EvalError::Float32OutOfRange(_)
412 | EvalError::Float64OutOfRange(_)
413 | EvalError::Int16OutOfRange(_)
414 | EvalError::Int32OutOfRange(_)
415 | EvalError::Int64OutOfRange(_)
416 | EvalError::UInt16OutOfRange(_)
417 | EvalError::UInt32OutOfRange(_)
418 | EvalError::UInt64OutOfRange(_)
419 | EvalError::OidOutOfRange(_)
420 | EvalError::MzTimestampOutOfRange(_)
421 | EvalError::MzTimestampStepOverflow
422 | EvalError::CharOutOfRange => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
423
424 EvalError::DateOutOfRange
426 | EvalError::TimestampOutOfRange
427 | EvalError::TimestampCannotBeNan
428 | EvalError::DateBinOutOfRange(_)
429 | EvalError::DateDiffOverflow { .. } => SqlState::DATETIME_FIELD_OVERFLOW,
430 EvalError::IntervalOutOfRange(_) => SqlState::INTERVAL_FIELD_OVERFLOW,
431
432 EvalError::Parse(e) => parse_error_code(e),
434 EvalError::ParseHex(_)
435 | EvalError::InvalidBase64Equals
436 | EvalError::InvalidBase64Symbol(_)
437 | EvalError::InvalidBase64EndSequence => SqlState::INVALID_TEXT_REPRESENTATION,
438 EvalError::InvalidByteSequence { .. } => SqlState::CHARACTER_NOT_IN_REPERTOIRE,
439 EvalError::CharacterNotValidForEncoding(_) | EvalError::CharacterTooLargeForEncoding(_) => {
440 SqlState::PROGRAM_LIMIT_EXCEEDED
441 }
442
443 EvalError::InvalidTimezone(_)
445 | EvalError::InvalidTimezoneInterval
446 | EvalError::InvalidTimezoneConversion
447 | EvalError::InvalidIanaTimezoneId(_)
448 | EvalError::InvalidLayer { .. }
449 | EvalError::InvalidEncodingName(_)
450 | EvalError::InvalidHashAlgorithm(_)
451 | EvalError::InvalidDatePart(_)
452 | EvalError::InvalidParameterValue(_)
453 | EvalError::InvalidJsonbCast { .. }
454 | EvalError::UnknownUnits(_)
455 | EvalError::UnsupportedUnits(..)
456 | EvalError::InvalidIdentifier { .. }
457 | EvalError::InvalidRoleId(_)
458 | EvalError::InvalidPrivileges(_)
459 | EvalError::NegLimit => SqlState::INVALID_PARAMETER_VALUE,
460
461 EvalError::InvalidRegex(_) | EvalError::InvalidRegexFlag(_) => {
463 SqlState::INVALID_REGULAR_EXPRESSION
464 }
465
466 EvalError::UnterminatedLikeEscapeSequence | EvalError::LikeEscapeTooLong => {
468 SqlState::INVALID_ESCAPE_SEQUENCE
469 }
470
471 EvalError::KeyCannotBeNull
473 | EvalError::MustNotBeNull(_)
474 | EvalError::AclArrayNullElement
475 | EvalError::MzAclArrayNullElement => SqlState::NULL_VALUE_NOT_ALLOWED,
476
477 EvalError::IndexOutOfRange { .. }
479 | EvalError::ArrayFillWrongArraySubscripts
480 | EvalError::IncompatibleArrayDimensions { .. } => SqlState::ARRAY_SUBSCRIPT_ERROR,
481 EvalError::InvalidArray(e) => match e {
482 InvalidArrayError::TooManyDimensions(_) => SqlState::PROGRAM_LIMIT_EXCEEDED,
483 InvalidArrayError::WrongCardinality { .. } => SqlState::ARRAY_SUBSCRIPT_ERROR,
484 },
485
486 EvalError::InvalidRange(e) => match e {
488 InvalidRangeError::CanonicalizationOverflow(_) => SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
489 InvalidRangeError::MisorderedRangeBounds
490 | InvalidRangeError::InvalidRangeBoundFlags
491 | InvalidRangeError::NullRangeBoundFlags
492 | InvalidRangeError::DiscontiguousUnion
493 | InvalidRangeError::DiscontiguousDifference
494 | InvalidRangeError::InvalidRangeData => SqlState::DATA_EXCEPTION,
495 },
496
497 EvalError::MultipleRowsFromSubquery | EvalError::NegativeRowsFromSubquery => {
499 SqlState::CARDINALITY_VIOLATION
500 }
501
502 EvalError::StringValueTooLong { .. } => SqlState::STRING_DATA_RIGHT_TRUNCATION,
504 EvalError::LikePatternTooLong
505 | EvalError::LengthTooLarge
506 | EvalError::TempStorageBudgetExceeded
507 | EvalError::NullCharacterNotPermitted
508 | EvalError::MaxArraySizeExceeded(_)
509 | EvalError::LetRecLimitExceeded(_) => SqlState::PROGRAM_LIMIT_EXCEEDED,
510
511 EvalError::Unsupported { .. }
513 | EvalError::MultidimensionalArrayRemovalNotSupported
514 | EvalError::MultiDimensionalArraySearch => SqlState::FEATURE_NOT_SUPPORTED,
515
516 EvalError::IfNullError(_) => SqlState::DATA_EXCEPTION,
518
519 EvalError::Internal(_)
521 | EvalError::InvalidCatalogJson(_)
522 | EvalError::TypeFromOid(_)
523 | EvalError::PrettyError(_)
524 | EvalError::RedactError(_) => SqlState::INTERNAL_ERROR,
525 }
526}
527
528impl AdapterError {
529 pub fn into_response(self, severity: Severity) -> ErrorResponse {
530 ErrorResponse {
531 severity,
532 code: self.code(),
533 message: self.to_string(),
534 detail: self.detail(),
535 hint: self.hint(),
536 position: self.position(),
537 }
538 }
539
540 pub fn position(&self) -> Option<usize> {
541 match self {
542 AdapterError::ParseError(err) => Some(err.error.pos),
543 _ => None,
544 }
545 }
546
547 pub fn detail(&self) -> Option<String> {
549 match self {
550 AdapterError::AmbiguousSystemColumnReference => {
551 Some("This is a current limitation in Materialize".into())
552 }
553 AdapterError::Catalog(c) => c.detail(),
554 AdapterError::Eval(e) => e.detail(),
555 AdapterError::RelationOutsideTimeDomain { relations, names } => Some(format!(
556 "The following relations in the query are outside the transaction's time domain:\n{}\n{}",
557 relations
558 .iter()
559 .map(|r| r.quoted().to_string())
560 .collect::<Vec<_>>()
561 .join("\n"),
562 match names.is_empty() {
563 true => "No relations are available.".to_string(),
564 false => format!(
565 "Only the following relations are available:\n{}",
566 names
567 .iter()
568 .map(|name| name.quoted().to_string())
569 .collect::<Vec<_>>()
570 .join("\n")
571 ),
572 }
573 )),
574 AdapterError::SourceOrSinkSizeRequired { .. } => Some(
575 "Either specify the cluster that will maintain this object via IN CLUSTER or \
576 specify size via SIZE option."
577 .into(),
578 ),
579 AdapterError::InvalidTableMutationSelection {
580 object_name,
581 object_type,
582 } => Some(format!(
583 "{object_type} '{}' may not be used in this operation; \
584 the selection may refer to views and materialized views, but transitive \
585 dependencies must not include sources or source-export tables",
586 object_name.quoted()
587 )),
588 AdapterError::ReadThenWriteDependencyLimitExceeded {
589 max_rw_dependencies,
590 } => Some(format!(
591 "The read set transitively depends on more than {max_rw_dependencies} \
592 objects. Reduce the number of dependencies, or raise the \
593 read_then_write_max_dependencies system parameter."
594 )),
595 AdapterError::SafeModeViolation(_) => Some(
596 "The Materialize server you are connected to is running in \
597 safe mode, which limits the features that are available."
598 .into(),
599 ),
600 AdapterError::IntrospectionDisabled { log_names }
601 | AdapterError::UntargetedLogRead { log_names } => Some(format!(
602 "The query references the following log sources:\n {}",
603 log_names.join("\n "),
604 )),
605 AdapterError::InvalidLogDependency { log_names, .. } => Some(format!(
606 "The object depends on the following log sources:\n {}",
607 log_names.join("\n "),
608 )),
609 AdapterError::PlanError(e) => e.detail(),
610 AdapterError::Unauthorized(unauthorized) => unauthorized.detail(),
611 AdapterError::DependentObject(dependent_objects) => Some(
612 dependent_objects
613 .iter()
614 .map(|(role_name, err_msgs)| {
615 err_msgs
616 .iter()
617 .map(|err_msg| format!("{role_name}: {err_msg}"))
618 .join("\n")
619 })
620 .join("\n"),
621 ),
622 AdapterError::Storage(storage_error) => storage_error
623 .source()
624 .map(|source_error| source_error.to_string_with_causes()),
625 AdapterError::ReadOnlyTransaction => Some(
626 "SELECT queries cannot be combined with other query types, including SUBSCRIBE."
627 .into(),
628 ),
629 AdapterError::InvalidAlter(_, e) => e.detail(),
630 AdapterError::Optimizer(e) => e.detail(),
631 AdapterError::ConnectionValidation(e) => e.detail(),
632 AdapterError::MaterializedViewWouldNeverRefresh(last_refresh, earliest_possible) => {
633 Some(format!(
634 "The specified last refresh is at {}, while the earliest possible time to compute the materialized \
635 view is {}.",
636 last_refresh, earliest_possible,
637 ))
638 }
639 AdapterError::UnallowedOnCluster { cluster, .. } => {
640 (cluster == MZ_CATALOG_SERVER_CLUSTER.name).then(|| {
641 format!(
642 "The transaction is executing on the \
643 {cluster} cluster, maybe having been routed \
644 there by the first statement in the transaction."
645 )
646 })
647 }
648 AdapterError::InputNotReadableAtRefreshAtTime(oracle_read_ts, least_valid_read) => {
649 Some(format!(
650 "The requested REFRESH AT time is {}, \
651 but not all input collections are readable earlier than [{}].",
652 oracle_read_ts,
653 if least_valid_read.len() == 1 {
654 format!(
655 "{}",
656 least_valid_read
657 .as_option()
658 .expect("antichain contains exactly 1 timestamp")
659 )
660 } else {
661 format!("{:?}", least_valid_read)
663 }
664 ))
665 }
666 AdapterError::RtrTimeout(name) => Some(format!(
667 "{name} failed to ingest data up to the real-time recency point"
668 )),
669 AdapterError::RtrDropFailure(name) => Some(format!(
670 "{name} dropped before ingesting data to the real-time recency point"
671 )),
672 AdapterError::UserSessionsDisallowed => {
673 Some("Your organization has been blocked. Please contact support.".to_string())
674 }
675 AdapterError::NetworkPolicyDenied(reason) => Some(format!("{reason}.")),
676 AdapterError::ReplacementSchemaMismatch(diff) => {
677 let mut lines: Vec<_> = diff.column_diffs.iter().map(|(idx, diff)| {
678 let pos = idx + 1;
679 match diff {
680 ColumnDiff::Missing { name } => {
681 let name = name.as_str().quoted();
682 format!("missing column {name} at position {pos}")
683 }
684 ColumnDiff::Extra { name } => {
685 let name = name.as_str().quoted();
686 format!("extra column {name} at position {pos}")
687 }
688 ColumnDiff::TypeMismatch { name, left, right } => {
689 let name = name.as_str().quoted();
690 format!("column {name} at position {pos}: type mismatch (target: {left:?}, replacement: {right:?})")
691 }
692 ColumnDiff::NullabilityMismatch { name, left, right } => {
693 let name = name.as_str().quoted();
694 let left = if *left { "NULL" } else { "NOT NULL" };
695 let right = if *right { "NULL" } else { "NOT NULL" };
696 format!("column {name} at position {pos}: nullability mismatch (target: {left}, replacement: {right})")
697 }
698 ColumnDiff::NameMismatch { left, right } => {
699 let left = left.as_str().quoted();
700 let right = right.as_str().quoted();
701 format!("column at position {pos}: name mismatch (target: {left}, replacement: {right})")
702 }
703 }
704 }).collect();
705
706 if let Some(KeyDiff { left, right }) = &diff.key_diff {
707 let format_keys = |keys: &BTreeSet<Vec<ColumnName>>| {
708 if keys.is_empty() {
709 "(none)".to_string()
710 } else {
711 keys.iter()
712 .map(|key| {
713 let cols = key.iter().map(|c| c.as_str()).join(", ");
714 format!("{{{cols}}}")
715 })
716 .join(", ")
717 }
718 };
719 lines.push(format!(
720 "keys differ (target: {}, replacement: {})",
721 format_keys(left),
722 format_keys(right)
723 ));
724 }
725 Some(lines.join("\n"))
726 }
727 AdapterError::ReplaceMaterializedViewSealed { .. } => Some(
728 "The materialized view has already computed its output until the end of time, \
729 so replacing its definition would have no effect."
730 .into(),
731 ),
732 AdapterError::ImpossibleTimestampConstraints { constraints } => {
733 Some(format!("Constraints:\n{}", constraints))
734 }
735 AdapterError::BoundedStalenessExceeded {
736 gap_ms,
737 slowest_input,
738 ..
739 } => {
740 let mut detail = format!(
741 "Freshest available timestamp is {}ms older than the bound.",
742 gap_ms,
743 );
744 if let Some(id) = slowest_input {
745 detail.push_str(&format!(" Slowest input: {}.", id));
746 }
747 Some(detail)
748 }
749 AdapterError::BoundedStalenessTimelineUnsupported => Some(
750 "This query touches a timeline other than the EpochMilliseconds wall-clock \
751 timeline."
752 .into(),
753 ),
754 _ => None,
755 }
756 }
757
758 pub fn hint(&self) -> Option<String> {
760 match self {
761 AdapterError::AmbiguousSystemColumnReference => Some(
762 "Rewrite the view to refer to all columns by name. Expand all wildcards and \
763 convert all NATURAL JOINs to USING joins."
764 .to_string(),
765 ),
766 AdapterError::Catalog(c) => c.hint(),
767 AdapterError::Eval(e) => e.hint(),
768 AdapterError::SubscribeFellBehind { .. } => Some(
769 "The client is not reading results fast enough. Use a client that reads output \
770 without buffering, or raise the subscribe_max_buffered_bytes system variable."
771 .to_string(),
772 ),
773 AdapterError::AlterClusterUnmanagedWhileReconfiguring => Some(
774 "Cancel the reconfiguration by altering the cluster back to its current \
775 configuration, or wait for it to settle, then convert."
776 .to_string(),
777 ),
778 AdapterError::AlterClusterUnmanagedWhileBursting => Some(
779 "Remove the strategy with ALTER CLUSTER ... RESET (AUTO SCALING STRATEGY), \
780 or wait for the burst to wind down, then convert."
781 .to_string(),
782 ),
783 AdapterError::AlterClusterReplicationFactorWhileReconfiguring => Some(
784 "Cancel the reconfiguration by altering the cluster back to its current \
785 configuration, or wait for it to settle, then change the replication factor."
786 .to_string(),
787 ),
788 AdapterError::AlterClusterScheduleWhileReconfiguring => Some(
789 "Cancel the reconfiguration by altering the cluster back to its current \
790 configuration, or wait for it to settle, then change the schedule."
791 .to_string(),
792 ),
793 AdapterError::AlterClusterWaitOnScheduledCluster => Some(
794 "The scheduler reshapes a scheduled cluster's replicas in the background. \
795 Run the ALTER without a WAIT option, or set SCHEDULE = MANUAL first."
796 .to_string(),
797 ),
798 AdapterError::InvalidClusterReplicaAz { expected, az: _ } => {
799 Some(if expected.is_empty() {
800 "No availability zones configured; do not specify AVAILABILITY ZONE".into()
801 } else {
802 format!("Valid availability zones are: {}", expected.join(", "))
803 })
804 }
805 AdapterError::InvalidStorageClusterSize { expected, .. } => {
806 Some(format!("Valid sizes are: {}", expected.join(", ")))
807 }
808 AdapterError::SourceOrSinkSizeRequired { expected } => Some(format!(
809 "Try choosing one of the smaller sizes to start. Available sizes: {}",
810 expected.join(", ")
811 )),
812 AdapterError::NoClusterReplicasAvailable { is_managed, .. } => {
813 Some(if *is_managed {
814 "Use ALTER CLUSTER to adjust the replication factor of the cluster. \
815 Example:`ALTER CLUSTER <cluster-name> SET (REPLICATION FACTOR 1)`".into()
816 } else {
817 "Use CREATE CLUSTER REPLICA to attach cluster replicas to the cluster".into()
818 })
819 }
820 AdapterError::UntargetedLogRead { .. } => Some(
821 "Use `SET cluster_replica = <replica-name>` to target a specific replica in the \
822 active cluster. Note that subsequent queries will only be answered by \
823 the selected replica, which might reduce availability. To undo the replica \
824 selection, use `RESET cluster_replica`."
825 .into(),
826 ),
827 AdapterError::ResourceExhaustion { resource_type, .. } => Some(format!(
828 "Drop an existing {resource_type} or contact support to request a limit increase."
829 )),
830 AdapterError::StatementTimeout => Some(
831 "Consider increasing the maximum allowed statement duration for this session by \
832 setting the statement_timeout session variable. For example, `SET \
833 statement_timeout = '120s'`."
834 .into(),
835 ),
836 AdapterError::PlanError(e) => e.hint(),
837 AdapterError::UnallowedOnCluster { cluster, .. } => {
838 (cluster != MZ_CATALOG_SERVER_CLUSTER.name).then(||
839 "Use `SET CLUSTER = <cluster-name>` to change your cluster and re-run the query."
840 .to_string()
841 )
842 }
843 AdapterError::InvalidAlter(_, e) => e.hint(),
844 AdapterError::Optimizer(e) => e.hint(),
845 AdapterError::ConnectionValidation(e) => e.hint(),
846 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => Some(
847 "You can use `REFRESH AT greatest(mz_now(), <explicit timestamp>)` to refresh \
848 either at the explicitly specified timestamp, or now if the given timestamp would \
849 be in the past.".to_string()
850 ),
851 AdapterError::AlterClusterTimeout => Some(
852 "Consider increasing the timeout duration in the alter cluster statement.".into(),
853 ),
854 AdapterError::DDLTransactionRace => Some(
855 "Currently, DDL transactions fail when any other DDL happens concurrently, \
856 even on unrelated schemas/clusters.".into()
857 ),
858 AdapterError::ConcurrentDependencyMutation { .. } => Some(
859 "Another session modified one of this statement's dependencies before \
860 it could commit. Retry the statement.".into()
861 ),
862 AdapterError::ReadThenWriteContention => Some(
863 "Concurrent writes to the target table kept this statement from \
864 committing. Retry the statement, or lower the write concurrency.".into()
865 ),
866 AdapterError::ReadThenWriteTimestampTooFarAhead { .. } => Some(
867 "The write timestamp this statement would have committed at is far ahead of \
868 the wall clock. Committing there would keep writes and strict-serializable \
869 reads on this timeline from proceeding until the clock caught up.".into()
870 ),
871 AdapterError::CollectionUnreadable { .. } => Some(
872 "This could be because the collection has recently been dropped.".into()
873 ),
874 _ => None,
875 }
876 }
877
878 pub fn code(&self) -> SqlState {
879 const RECURSION_LIMIT_ERROR_CODE: SqlState = SqlState::INTERNAL_ERROR;
881
882 match self {
887 AdapterError::AbsurdSubscribeBounds { .. } => SqlState::DATA_EXCEPTION,
890 AdapterError::AmbiguousSystemColumnReference => SqlState::FEATURE_NOT_SUPPORTED,
891 AdapterError::Catalog(e) => match &e.kind {
892 mz_catalog::memory::error::ErrorKind::VarError(e) => match e {
893 VarError::ConstrainedParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
894 VarError::FixedValueParameter { .. } => SqlState::INVALID_PARAMETER_VALUE,
895 VarError::InvalidParameterType { .. } => SqlState::INVALID_PARAMETER_VALUE,
896 VarError::InvalidParameterValue { .. } => SqlState::INVALID_PARAMETER_VALUE,
897 VarError::ReadOnlyParameter(_) => SqlState::CANT_CHANGE_RUNTIME_PARAM,
898 VarError::UnknownParameter(_) => SqlState::UNDEFINED_OBJECT,
899 VarError::RequiresUnsafeMode { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
900 VarError::RequiresFeatureFlag { .. } => SqlState::CANT_CHANGE_RUNTIME_PARAM,
901 },
902 _ => SqlState::INTERNAL_ERROR,
903 },
904 AdapterError::ChangedPlan(_) => SqlState::FEATURE_NOT_SUPPORTED,
905 AdapterError::DuplicateCursor(_) => SqlState::DUPLICATE_CURSOR,
906 AdapterError::Eval(e) => eval_error_code(e),
911 AdapterError::Explain(_) => SqlState::INTERNAL_ERROR,
912 AdapterError::IdExhaustionError => SqlState::INTERNAL_ERROR,
913 AdapterError::Internal(_) => SqlState::INTERNAL_ERROR,
914 AdapterError::IntrospectionDisabled { .. } => SqlState::FEATURE_NOT_SUPPORTED,
915 AdapterError::InvalidLogDependency { .. } => SqlState::FEATURE_NOT_SUPPORTED,
916 AdapterError::InvalidClusterReplicaAz { .. } => SqlState::FEATURE_NOT_SUPPORTED,
917 AdapterError::InvalidSetIsolationLevel => SqlState::ACTIVE_SQL_TRANSACTION,
918 AdapterError::InvalidSetCluster => SqlState::ACTIVE_SQL_TRANSACTION,
919 AdapterError::InvalidStorageClusterSize { .. } => SqlState::FEATURE_NOT_SUPPORTED,
920 AdapterError::SourceOrSinkSizeRequired { .. } => SqlState::FEATURE_NOT_SUPPORTED,
921 AdapterError::InvalidTableMutationSelection { .. } => {
922 SqlState::INVALID_TRANSACTION_STATE
923 }
924 AdapterError::ReadThenWriteDependencyLimitExceeded { .. } => {
925 SqlState::PROGRAM_LIMIT_EXCEEDED
926 }
927 AdapterError::ConstraintViolation(NotNullViolation(_)) => SqlState::NOT_NULL_VIOLATION,
928 AdapterError::CopyFormatError(_) => SqlState::BAD_COPY_FILE_FORMAT,
929 AdapterError::ConcurrentClusterDrop => SqlState::INVALID_TRANSACTION_STATE,
930 AdapterError::ConcurrentDependencyDrop { .. } => SqlState::UNDEFINED_OBJECT,
931 AdapterError::ConcurrentDependencyMutation { .. } => {
932 SqlState::T_R_SERIALIZATION_FAILURE
933 }
934 AdapterError::ReadThenWriteContention => SqlState::T_R_SERIALIZATION_FAILURE,
935 AdapterError::ReadThenWriteTimestampTooFarAhead { .. } => {
936 SqlState::INTERNAL_ERROR
940 }
941 AdapterError::CollectionUnreadable { .. } => SqlState::NO_DATA_FOUND,
942 AdapterError::NoClusterReplicasAvailable { .. } => SqlState::FEATURE_NOT_SUPPORTED,
943 AdapterError::OperationProhibitsTransaction(_) => SqlState::ACTIVE_SQL_TRANSACTION,
944 AdapterError::OperationRequiresTransaction(_) => SqlState::NO_ACTIVE_SQL_TRANSACTION,
945 AdapterError::ParseError(_) => SqlState::SYNTAX_ERROR,
946 AdapterError::PlanError(PlanError::InvalidSchemaName) => SqlState::INVALID_SCHEMA_NAME,
947 AdapterError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
948 SqlState::DUPLICATE_COLUMN
949 }
950 AdapterError::PlanError(PlanError::UnknownParameter(_)) => {
951 SqlState::UNDEFINED_PARAMETER
952 }
953 AdapterError::PlanError(PlanError::ParameterNotAllowed(_)) => {
954 SqlState::UNDEFINED_PARAMETER
955 }
956 AdapterError::PlanError(PlanError::Unsupported { .. }) => {
960 SqlState::FEATURE_NOT_SUPPORTED
961 }
962 AdapterError::PlanError(_) => SqlState::INTERNAL_ERROR,
963 AdapterError::PreparedStatementExists(_) => SqlState::DUPLICATE_PSTATEMENT,
964 AdapterError::ReadOnlyTransaction => SqlState::READ_ONLY_SQL_TRANSACTION,
965 AdapterError::ReadWriteUnavailable => SqlState::INVALID_TRANSACTION_STATE,
966 AdapterError::SingleStatementTransaction => SqlState::INVALID_TRANSACTION_STATE,
967 AdapterError::WrongSetOfLocks => SqlState::LOCK_NOT_AVAILABLE,
968 AdapterError::StatementTimeout => SqlState::QUERY_CANCELED,
969 AdapterError::Canceled => SqlState::QUERY_CANCELED,
970 AdapterError::IdleInTransactionSessionTimeout => {
971 SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT
972 }
973 AdapterError::RecursionLimit(_) => RECURSION_LIMIT_ERROR_CODE,
974 AdapterError::RelationOutsideTimeDomain { .. } => SqlState::INVALID_TRANSACTION_STATE,
975 AdapterError::ResourceExhaustion { .. } => SqlState::INSUFFICIENT_RESOURCES,
976 AdapterError::ResultSize(_) => SqlState::OUT_OF_MEMORY,
977 AdapterError::SubscribeFellBehind { .. } => SqlState::OUT_OF_MEMORY,
978 AdapterError::SafeModeViolation(_) => SqlState::INTERNAL_ERROR,
979 AdapterError::SubscribeOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
980 AdapterError::Optimizer(e) => match e {
981 OptimizerError::PlanError(PlanError::InvalidSchemaName) => {
982 SqlState::INVALID_SCHEMA_NAME
983 }
984 OptimizerError::PlanError(PlanError::ColumnAlreadyExists { .. }) => {
985 SqlState::DUPLICATE_COLUMN
986 }
987 OptimizerError::PlanError(PlanError::UnknownParameter(_)) => {
988 SqlState::UNDEFINED_PARAMETER
989 }
990 OptimizerError::PlanError(PlanError::ParameterNotAllowed(_)) => {
991 SqlState::UNDEFINED_PARAMETER
992 }
993 OptimizerError::PlanError(PlanError::Unsupported { .. }) => {
994 SqlState::FEATURE_NOT_SUPPORTED
995 }
996 OptimizerError::PlanError(_) => SqlState::INTERNAL_ERROR,
997 OptimizerError::RecursionLimitError(_) => RECURSION_LIMIT_ERROR_CODE,
998 OptimizerError::Internal(s) => {
999 AdapterError::Internal(s.clone()).code() }
1001 OptimizerError::EvalError(e) => {
1002 AdapterError::Eval(e.clone()).code() }
1004 OptimizerError::TransformError(_) => SqlState::INTERNAL_ERROR,
1005 OptimizerError::UnmaterializableFunction(_) => SqlState::FEATURE_NOT_SUPPORTED,
1006 OptimizerError::UncallableFunction { .. } => SqlState::FEATURE_NOT_SUPPORTED,
1007 OptimizerError::UnsupportedTemporalExpression(_) => SqlState::FEATURE_NOT_SUPPORTED,
1008 OptimizerError::RestrictedFunction(_) => SqlState::INSUFFICIENT_PRIVILEGE,
1009 OptimizerError::InternalUnsafeMfpPlan(_) => SqlState::INTERNAL_ERROR,
1012 },
1013 AdapterError::UnallowedOnCluster { .. } => {
1014 SqlState::S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED
1015 }
1016 AdapterError::Unauthorized(_) => SqlState::INSUFFICIENT_PRIVILEGE,
1017 AdapterError::UnknownCursor(_) => SqlState::INVALID_CURSOR_NAME,
1018 AdapterError::UnknownPreparedStatement(_) => SqlState::UNDEFINED_PSTATEMENT,
1019 AdapterError::UnknownLoginRole(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
1020 AdapterError::UnknownClusterReplica { .. } => SqlState::UNDEFINED_OBJECT,
1021 AdapterError::UnrecognizedConfigurationParam(_) => SqlState::UNDEFINED_OBJECT,
1022 AdapterError::Unsupported(..) => SqlState::FEATURE_NOT_SUPPORTED,
1023 AdapterError::UnavailableFeature { .. } => SqlState::FEATURE_NOT_SUPPORTED,
1024 AdapterError::Unstructured(_) => SqlState::INTERNAL_ERROR,
1025 AdapterError::UntargetedLogRead { .. } => SqlState::FEATURE_NOT_SUPPORTED,
1026 AdapterError::DDLTransactionRace => SqlState::T_R_SERIALIZATION_FAILURE,
1027 AdapterError::ClusterStateChanged { .. } => SqlState::T_R_SERIALIZATION_FAILURE,
1028 AdapterError::WriteOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
1033 AdapterError::DDLOnlyTransaction => SqlState::INVALID_TRANSACTION_STATE,
1034 AdapterError::Storage(_) | AdapterError::Compute(_) | AdapterError::Orchestrator(_) => {
1035 SqlState::INTERNAL_ERROR
1036 }
1037 AdapterError::DependentObject(_) => SqlState::DEPENDENT_OBJECTS_STILL_EXIST,
1038 AdapterError::InvalidAlter(_, _) => SqlState::FEATURE_NOT_SUPPORTED,
1039 AdapterError::ConnectionValidation(_) => SqlState::SYSTEM_ERROR,
1040 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => SqlState::DATA_EXCEPTION,
1042 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => SqlState::DATA_EXCEPTION,
1043 AdapterError::RtrTimeout(_) => SqlState::QUERY_CANCELED,
1044 AdapterError::RtrDropFailure(_) => SqlState::UNDEFINED_OBJECT,
1045 AdapterError::UnreadableSinkCollection => SqlState::from_code("MZ009"),
1046 AdapterError::UserSessionsDisallowed => SqlState::from_code("MZ010"),
1047 AdapterError::NetworkPolicyDenied(_) => SqlState::from_code("MZ011"),
1048 AdapterError::ReadOnly => SqlState::READ_ONLY_SQL_TRANSACTION,
1051 AdapterError::AlterClusterTimeout => SqlState::QUERY_CANCELED,
1052 AdapterError::AlterClusterWhilePendingReplicas => SqlState::OBJECT_IN_USE,
1053 AdapterError::AlterClusterUnmanagedWhileReconfiguring => SqlState::OBJECT_IN_USE,
1054 AdapterError::AlterClusterUnmanagedWhileBursting => SqlState::OBJECT_IN_USE,
1055 AdapterError::AlterClusterReplicationFactorWhileReconfiguring => {
1056 SqlState::OBJECT_IN_USE
1057 }
1058 AdapterError::AlterClusterScheduleWhileReconfiguring => SqlState::OBJECT_IN_USE,
1059 AdapterError::AlterClusterWaitOnScheduledCluster => SqlState::FEATURE_NOT_SUPPORTED,
1060 AdapterError::ReplacementSchemaMismatch(_) => SqlState::FEATURE_NOT_SUPPORTED,
1061 AdapterError::AuthenticationError(AuthenticationError::InvalidCredentials) => {
1062 SqlState::INVALID_PASSWORD
1063 }
1064 AdapterError::AuthenticationError(_) => SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
1065 AdapterError::ReplaceMaterializedViewSealed { .. } => {
1066 SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE
1067 }
1068 AdapterError::ImpossibleTimestampConstraints { .. } => SqlState::DATA_EXCEPTION,
1070 AdapterError::OidcGroupSyncFailed(_) => SqlState::INTERNAL_ERROR,
1071 AdapterError::BoundedStalenessExceeded { .. } => SqlState::T_R_SERIALIZATION_FAILURE,
1072 AdapterError::BoundedStalenessReadOnly => SqlState::READ_ONLY_SQL_TRANSACTION,
1075 AdapterError::BoundedStalenessRealTimeRecencyConflict => {
1076 SqlState::FEATURE_NOT_SUPPORTED
1077 }
1078 AdapterError::BoundedStalenessTimelineUnsupported => SqlState::FEATURE_NOT_SUPPORTED,
1079 }
1080 }
1081
1082 pub fn internal<E: std::fmt::Display>(context: &str, e: E) -> AdapterError {
1083 AdapterError::Internal(format!("{context}: {e}"))
1084 }
1085
1086 pub fn concurrent_dependency_drop_from_instance_missing(e: InstanceMissing) -> Self {
1093 AdapterError::ConcurrentDependencyDrop {
1094 dependency_kind: "cluster",
1095 dependency_id: e.0.to_string(),
1096 }
1097 }
1098
1099 pub fn concurrent_dependency_drop_from_collection_missing(e: CollectionMissing) -> Self {
1100 AdapterError::ConcurrentDependencyDrop {
1101 dependency_kind: "collection",
1102 dependency_id: e.0.to_string(),
1103 }
1104 }
1105
1106 pub fn concurrent_dependency_drop_from_collection_lookup_error(
1107 e: CollectionLookupError,
1108 compute_instance: ComputeInstanceId,
1109 ) -> Self {
1110 match e {
1111 CollectionLookupError::InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1112 dependency_kind: "cluster",
1113 dependency_id: id.to_string(),
1114 },
1115 CollectionLookupError::CollectionMissing(id) => {
1116 AdapterError::ConcurrentDependencyDrop {
1117 dependency_kind: "collection",
1118 dependency_id: id.to_string(),
1119 }
1120 }
1121 CollectionLookupError::InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
1122 dependency_kind: "cluster",
1123 dependency_id: compute_instance.to_string(),
1124 },
1125 }
1126 }
1127
1128 pub fn concurrent_dependency_drop_from_watch_set_install_error(
1129 e: compute_error::CollectionLookupError,
1130 ) -> Self {
1131 match e {
1132 compute_error::CollectionLookupError::InstanceMissing(id) => {
1133 AdapterError::ConcurrentDependencyDrop {
1134 dependency_kind: "cluster",
1135 dependency_id: id.to_string(),
1136 }
1137 }
1138 compute_error::CollectionLookupError::CollectionMissing(id) => {
1139 AdapterError::ConcurrentDependencyDrop {
1140 dependency_kind: "collection",
1141 dependency_id: id.to_string(),
1142 }
1143 }
1144 }
1145 }
1146
1147 pub fn concurrent_dependency_drop_from_instance_peek_error(
1148 e: mz_compute_client::controller::instance_client::PeekError,
1149 compute_instance: ComputeInstanceId,
1150 ) -> AdapterError {
1151 use mz_compute_client::controller::instance_client::PeekError::*;
1152 match e {
1153 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1154 dependency_kind: "replica",
1155 dependency_id: id.to_string(),
1156 },
1157 InstanceShutDown => AdapterError::ConcurrentDependencyDrop {
1158 dependency_kind: "cluster",
1159 dependency_id: compute_instance.to_string(),
1160 },
1161 e @ ReadHoldIdMismatch(_) => AdapterError::internal("instance peek error", e),
1162 e @ ReadHoldInsufficient(_) => AdapterError::internal("instance peek error", e),
1163 }
1164 }
1165
1166 pub fn concurrent_dependency_drop_from_collection_update_error(
1167 e: compute_error::CollectionUpdateError,
1168 ) -> Self {
1169 use compute_error::CollectionUpdateError::*;
1170 match e {
1171 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1172 dependency_kind: "cluster",
1173 dependency_id: id.to_string(),
1174 },
1175 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1176 dependency_kind: "collection",
1177 dependency_id: id.to_string(),
1178 },
1179 }
1180 }
1181
1182 pub fn concurrent_dependency_drop_from_peek_error(
1183 e: mz_compute_client::controller::error::PeekError,
1184 ) -> AdapterError {
1185 use mz_compute_client::controller::error::PeekError::*;
1186 match e {
1187 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1188 dependency_kind: "cluster",
1189 dependency_id: id.to_string(),
1190 },
1191 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1192 dependency_kind: "collection",
1193 dependency_id: id.to_string(),
1194 },
1195 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1196 dependency_kind: "replica",
1197 dependency_id: id.to_string(),
1198 },
1199 e @ (ReadHoldIdMismatch(_) | SinceViolation(_)) => {
1200 AdapterError::internal("peek error", e)
1201 }
1202 }
1203 }
1204
1205 pub fn concurrent_dependency_drop_from_dataflow_creation_error(
1206 e: compute_error::DataflowCreationError,
1207 ) -> Self {
1208 use compute_error::DataflowCreationError::*;
1209 match e {
1210 InstanceMissing(id) => AdapterError::ConcurrentDependencyDrop {
1211 dependency_kind: "cluster",
1212 dependency_id: id.to_string(),
1213 },
1214 CollectionMissing(id) => AdapterError::ConcurrentDependencyDrop {
1215 dependency_kind: "collection",
1216 dependency_id: id.to_string(),
1217 },
1218 ReplicaMissing(id) => AdapterError::ConcurrentDependencyDrop {
1219 dependency_kind: "replica",
1220 dependency_id: id.to_string(),
1221 },
1222 MissingAsOf | SinceViolation(..) | EmptyAsOfForSubscribe | EmptyAsOfForCopyTo => {
1223 AdapterError::internal("dataflow creation error", e)
1224 }
1225 }
1226 }
1227}
1228
1229impl fmt::Display for AdapterError {
1230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1231 match self {
1232 AdapterError::AbsurdSubscribeBounds { as_of, up_to } => {
1233 write!(
1234 f,
1235 "subscription lower bound (`AS OF`) is greater than its upper bound (`UP TO`): \
1236 {as_of} > {up_to}",
1237 )
1238 }
1239 AdapterError::AmbiguousSystemColumnReference => {
1240 write!(
1241 f,
1242 "cannot use wildcard expansions or NATURAL JOINs in a view that depends on \
1243 system objects"
1244 )
1245 }
1246 AdapterError::ChangedPlan(e) => write!(f, "{}", e),
1247 AdapterError::Catalog(e) => e.fmt(f),
1248 AdapterError::DuplicateCursor(name) => {
1249 write!(f, "cursor {} already exists", name.quoted())
1250 }
1251 AdapterError::Eval(e) => e.fmt(f),
1252 AdapterError::Explain(e) => e.fmt(f),
1253 AdapterError::IdExhaustionError => f.write_str("ID allocator exhausted all valid IDs"),
1254 AdapterError::Internal(e) => write!(f, "internal error: {}", e),
1255 AdapterError::IntrospectionDisabled { .. } => write!(
1256 f,
1257 "cannot read log sources of replica with disabled introspection"
1258 ),
1259 AdapterError::InvalidLogDependency { object_type, .. } => {
1260 write!(f, "{object_type} objects cannot depend on log sources")
1261 }
1262 AdapterError::InvalidClusterReplicaAz { az, expected: _ } => {
1263 write!(f, "unknown cluster replica availability zone {az}",)
1264 }
1265 AdapterError::InvalidSetIsolationLevel => write!(
1266 f,
1267 "SET TRANSACTION ISOLATION LEVEL must be called before any query"
1268 ),
1269 AdapterError::InvalidSetCluster => {
1270 write!(f, "SET cluster cannot be called in an active transaction")
1271 }
1272 AdapterError::InvalidStorageClusterSize { size, .. } => {
1273 write!(f, "unknown source size {size}")
1274 }
1275 AdapterError::SourceOrSinkSizeRequired { .. } => {
1276 write!(f, "must specify either cluster or size option")
1277 }
1278 AdapterError::InvalidTableMutationSelection { .. } => {
1279 write!(
1280 f,
1281 "invalid selection: operation may only (transitively) refer to non-source, non-system tables"
1282 )
1283 }
1284 AdapterError::ReadThenWriteDependencyLimitExceeded {
1285 max_rw_dependencies,
1286 } => {
1287 write!(
1288 f,
1289 "selection has too many transitive dependencies to validate (limit {max_rw_dependencies})"
1290 )
1291 }
1292 AdapterError::ReplaceMaterializedViewSealed { name } => {
1293 write!(
1294 f,
1295 "materialized view {name} is sealed and thus cannot be replaced"
1296 )
1297 }
1298 AdapterError::ConstraintViolation(not_null_violation) => {
1299 write!(f, "{}", not_null_violation)
1300 }
1301 AdapterError::CopyFormatError(e) => write!(f, "{e}"),
1302 AdapterError::ConcurrentClusterDrop => {
1303 write!(f, "the transaction's active cluster has been dropped")
1304 }
1305 AdapterError::ConcurrentDependencyDrop {
1306 dependency_kind,
1307 dependency_id,
1308 } => {
1309 write!(f, "{dependency_kind} '{dependency_id}' was dropped")
1310 }
1311 AdapterError::ConcurrentDependencyMutation { dependency_id } => {
1312 write!(
1313 f,
1314 "catalog item '{dependency_id}' was concurrently modified"
1315 )
1316 }
1317 AdapterError::ReadThenWriteContention => {
1318 write!(
1319 f,
1320 "read-then-write exceeded maximum retry attempts under contention"
1321 )
1322 }
1323 AdapterError::ReadThenWriteTimestampTooFarAhead {
1324 target_timestamp,
1325 limit,
1326 } => {
1327 write!(
1328 f,
1329 "read-then-write would have to commit at {target_timestamp}, past the \
1330 highest timestamp the write timeline may be advanced to ({limit})"
1331 )
1332 }
1333 AdapterError::CollectionUnreadable { id } => {
1334 write!(f, "collection '{id}' is not readable at any timestamp")
1335 }
1336 AdapterError::NoClusterReplicasAvailable { name, .. } => {
1337 write!(
1338 f,
1339 "CLUSTER {} has no replicas available to service request",
1340 name.quoted()
1341 )
1342 }
1343 AdapterError::OperationProhibitsTransaction(op) => {
1344 write!(f, "{} cannot be run inside a transaction block", op)
1345 }
1346 AdapterError::OperationRequiresTransaction(op) => {
1347 write!(f, "{} can only be used in transaction blocks", op)
1348 }
1349 AdapterError::ParseError(e) => e.fmt(f),
1350 AdapterError::PlanError(e) => e.fmt(f),
1351 AdapterError::PreparedStatementExists(name) => {
1352 write!(f, "prepared statement {} already exists", name.quoted())
1353 }
1354 AdapterError::ReadOnlyTransaction => f.write_str("transaction in read-only mode"),
1355 AdapterError::SingleStatementTransaction => {
1356 f.write_str("this transaction can only execute a single statement")
1357 }
1358 AdapterError::ReadWriteUnavailable => {
1359 f.write_str("transaction read-write mode must be set before any query")
1360 }
1361 AdapterError::WrongSetOfLocks => {
1362 write!(f, "internal error, wrong set of locks acquired")
1363 }
1364 AdapterError::StatementTimeout => {
1365 write!(f, "canceling statement due to statement timeout")
1366 }
1367 AdapterError::Canceled => {
1368 write!(f, "canceling statement due to user request")
1369 }
1370 AdapterError::IdleInTransactionSessionTimeout => {
1371 write!(
1372 f,
1373 "terminating connection due to idle-in-transaction timeout"
1374 )
1375 }
1376 AdapterError::RecursionLimit(e) => e.fmt(f),
1377 AdapterError::RelationOutsideTimeDomain { .. } => {
1378 write!(
1379 f,
1380 "Transactions can only reference objects in the same timedomain. \
1381 See https://materialize.com/docs/sql/begin/#same-timedomain-error",
1382 )
1383 }
1384 AdapterError::ResourceExhaustion {
1385 resource_type,
1386 limit_name,
1387 desired,
1388 limit,
1389 current,
1390 } => {
1391 write!(
1392 f,
1393 "creating {resource_type} would violate {limit_name} limit (desired: {desired}, limit: {limit}, current: {current})"
1394 )
1395 }
1396 AdapterError::ResultSize(e) => write!(f, "{e}"),
1397 AdapterError::SubscribeFellBehind {
1398 buffered_bytes,
1399 max_buffered_bytes,
1400 } => {
1401 use bytesize::ByteSize;
1402 let buffered = u64::cast_from(*buffered_bytes);
1403 let max = u64::cast_from(*max_buffered_bytes);
1404 write!(
1405 f,
1406 "SUBSCRIBE fell behind: the client did not read results fast enough, so its backlog reached {}, exceeding the {} budget",
1407 ByteSize::b(buffered),
1408 ByteSize::b(max),
1409 )
1410 }
1411 AdapterError::SafeModeViolation(feature) => {
1412 write!(f, "cannot create {} in safe mode", feature)
1413 }
1414 AdapterError::SubscribeOnlyTransaction => {
1415 f.write_str("SUBSCRIBE in transactions must be the only read statement")
1416 }
1417 AdapterError::Optimizer(e) => e.fmt(f),
1418 AdapterError::UnallowedOnCluster {
1419 depends_on,
1420 cluster,
1421 } => {
1422 let items = depends_on.into_iter().map(|item| item.quoted()).join(", ");
1423 write!(
1424 f,
1425 "querying the following items {items} is not allowed from the {} cluster",
1426 cluster.quoted()
1427 )
1428 }
1429 AdapterError::Unauthorized(unauthorized) => {
1430 write!(f, "{unauthorized}")
1431 }
1432 AdapterError::UnknownCursor(name) => {
1433 write!(f, "cursor {} does not exist", name.quoted())
1434 }
1435 AdapterError::UnknownLoginRole(name) => {
1436 write!(f, "role {} does not exist", name.quoted())
1437 }
1438 AdapterError::Unsupported(features) => write!(f, "{} are not supported", features),
1439 AdapterError::Unstructured(e) => write!(f, "{}", e.display_with_causes()),
1440 AdapterError::WriteOnlyTransaction => f.write_str("transaction in write-only mode"),
1441 AdapterError::UnknownPreparedStatement(name) => {
1442 write!(f, "prepared statement {} does not exist", name.quoted())
1443 }
1444 AdapterError::UnknownClusterReplica {
1445 cluster_name,
1446 replica_name,
1447 } => write!(
1448 f,
1449 "cluster replica '{cluster_name}.{replica_name}' does not exist"
1450 ),
1451 AdapterError::UnrecognizedConfigurationParam(setting_name) => write!(
1452 f,
1453 "unrecognized configuration parameter {}",
1454 setting_name.quoted()
1455 ),
1456 AdapterError::UntargetedLogRead { .. } => {
1457 f.write_str("log source reads must target a replica")
1458 }
1459 AdapterError::DDLOnlyTransaction => f.write_str(
1460 "transactions which modify objects are restricted to just modifying objects",
1461 ),
1462 AdapterError::DDLTransactionRace => f.write_str(
1463 "another session modified the catalog while this DDL transaction was open",
1464 ),
1465 AdapterError::ClusterStateChanged { cluster_id } => {
1466 write!(f, "cluster {cluster_id} was concurrently modified")
1467 }
1468 AdapterError::Storage(e) => e.fmt(f),
1469 AdapterError::Compute(e) => e.fmt(f),
1470 AdapterError::Orchestrator(e) => e.fmt(f),
1471 AdapterError::DependentObject(dependent_objects) => {
1472 let role_str = if dependent_objects.keys().count() == 1 {
1473 "role"
1474 } else {
1475 "roles"
1476 };
1477 write!(
1478 f,
1479 "{role_str} \"{}\" cannot be dropped because some objects depend on it",
1480 dependent_objects.keys().join(", ")
1481 )
1482 }
1483 AdapterError::InvalidAlter(t, e) => {
1484 write!(f, "invalid ALTER {t}: {e}")
1485 }
1486 AdapterError::ConnectionValidation(e) => e.fmt(f),
1487 AdapterError::MaterializedViewWouldNeverRefresh(_, _) => {
1488 write!(
1489 f,
1490 "all the specified refreshes of the materialized view would be too far in the past, and thus they \
1491 would never happen"
1492 )
1493 }
1494 AdapterError::InputNotReadableAtRefreshAtTime(_, _) => {
1495 write!(
1496 f,
1497 "REFRESH AT requested for a time where not all the inputs are readable"
1498 )
1499 }
1500 AdapterError::RtrTimeout(_) => {
1501 write!(
1502 f,
1503 "timed out before ingesting the source's visible frontier when real-time-recency query issued"
1504 )
1505 }
1506 AdapterError::RtrDropFailure(_) => write!(
1507 f,
1508 "real-time source dropped before ingesting the upstream system's visible frontier"
1509 ),
1510 AdapterError::UnreadableSinkCollection => {
1511 write!(f, "collection is not readable at any time")
1512 }
1513 AdapterError::UserSessionsDisallowed => write!(f, "login blocked"),
1514 AdapterError::NetworkPolicyDenied(_) => write!(f, "session denied"),
1515 AdapterError::ReadOnly => write!(f, "cannot write in read-only mode"),
1516 AdapterError::AlterClusterTimeout => {
1517 write!(f, "canceling statement, provided timeout lapsed")
1518 }
1519 AdapterError::AuthenticationError(e) => {
1520 write!(f, "authentication error {e}")
1521 }
1522 AdapterError::UnavailableFeature { feature, docs } => {
1523 write!(f, "{} is not supported in this environment.", feature)?;
1524 if let Some(docs) = docs {
1525 write!(
1526 f,
1527 " For more information consult the documentation at {docs}"
1528 )?;
1529 }
1530 Ok(())
1531 }
1532 AdapterError::AlterClusterWhilePendingReplicas => {
1533 write!(f, "cannot alter clusters with pending updates")
1534 }
1535 AdapterError::AlterClusterUnmanagedWhileReconfiguring => {
1536 write!(
1537 f,
1538 "cannot convert cluster to unmanaged while a reconfiguration is in progress"
1539 )
1540 }
1541 AdapterError::AlterClusterUnmanagedWhileBursting => {
1542 write!(
1543 f,
1544 "cannot convert cluster to unmanaged while a hydration burst is in progress"
1545 )
1546 }
1547 AdapterError::AlterClusterReplicationFactorWhileReconfiguring => {
1548 write!(
1549 f,
1550 "cannot change replication factor while a reconfiguration is in progress"
1551 )
1552 }
1553 AdapterError::AlterClusterScheduleWhileReconfiguring => {
1554 write!(
1555 f,
1556 "cannot change the cluster schedule while a reconfiguration is in progress"
1557 )
1558 }
1559 AdapterError::AlterClusterWaitOnScheduledCluster => {
1560 write!(
1561 f,
1562 "WAIT is not supported when the cluster SCHEDULE is not MANUAL"
1563 )
1564 }
1565 AdapterError::ReplacementSchemaMismatch(_) => {
1566 write!(f, "replacement schema differs from target schema")
1567 }
1568 AdapterError::ImpossibleTimestampConstraints { .. } => {
1569 write!(f, "could not find a valid timestamp for the query")
1570 }
1571 AdapterError::OidcGroupSyncFailed(msg) => {
1572 write!(f, "OIDC group-to-role sync failed: {msg}")
1573 }
1574 AdapterError::BoundedStalenessExceeded { bound, .. } => {
1575 write!(
1576 f,
1577 "cannot serve query under bounded staleness {}",
1578 humantime::format_duration(*bound),
1579 )
1580 }
1581 AdapterError::BoundedStalenessReadOnly => {
1582 f.write_str("writes are not permitted under bounded staleness isolation")
1583 }
1584 AdapterError::BoundedStalenessRealTimeRecencyConflict => {
1585 f.write_str("real_time_recency cannot be combined with bounded staleness isolation")
1586 }
1587 AdapterError::BoundedStalenessTimelineUnsupported => {
1588 f.write_str("bounded staleness isolation requires the EpochMilliseconds timeline")
1589 }
1590 }
1591 }
1592}
1593
1594impl From<anyhow::Error> for AdapterError {
1595 fn from(e: anyhow::Error) -> AdapterError {
1596 match e.downcast::<PlanError>() {
1597 Ok(plan_error) => AdapterError::PlanError(plan_error),
1598 Err(e) => AdapterError::Unstructured(e),
1599 }
1600 }
1601}
1602
1603impl From<TryFromIntError> for AdapterError {
1604 fn from(e: TryFromIntError) -> AdapterError {
1605 AdapterError::Unstructured(e.into())
1606 }
1607}
1608
1609impl From<TryFromDecimalError> for AdapterError {
1610 fn from(e: TryFromDecimalError) -> AdapterError {
1611 AdapterError::Unstructured(e.into())
1612 }
1613}
1614
1615impl From<mz_catalog::memory::error::Error> for AdapterError {
1616 fn from(e: mz_catalog::memory::error::Error) -> AdapterError {
1617 AdapterError::Catalog(e)
1618 }
1619}
1620
1621impl From<mz_catalog::durable::CatalogError> for AdapterError {
1622 fn from(e: mz_catalog::durable::CatalogError) -> Self {
1623 mz_catalog::memory::error::Error::from(e).into()
1624 }
1625}
1626
1627impl From<mz_catalog::durable::DurableCatalogError> for AdapterError {
1628 fn from(e: mz_catalog::durable::DurableCatalogError) -> Self {
1629 mz_catalog::durable::CatalogError::from(e).into()
1630 }
1631}
1632
1633impl From<EvalError> for AdapterError {
1634 fn from(e: EvalError) -> AdapterError {
1635 AdapterError::Eval(e)
1636 }
1637}
1638
1639impl From<ExplainError> for AdapterError {
1640 fn from(e: ExplainError) -> AdapterError {
1641 match e {
1642 ExplainError::RecursionLimitError(e) => AdapterError::RecursionLimit(e),
1643 e => AdapterError::Explain(e),
1644 }
1645 }
1646}
1647
1648impl From<mz_sql::catalog::CatalogError> for AdapterError {
1649 fn from(e: mz_sql::catalog::CatalogError) -> AdapterError {
1650 AdapterError::Catalog(mz_catalog::memory::error::Error::from(e))
1651 }
1652}
1653
1654impl From<PlanError> for AdapterError {
1655 fn from(e: PlanError) -> AdapterError {
1656 match e {
1657 PlanError::UnknownCursor(name) => AdapterError::UnknownCursor(name),
1658 _ => AdapterError::PlanError(e),
1659 }
1660 }
1661}
1662
1663impl From<OptimizerError> for AdapterError {
1664 fn from(e: OptimizerError) -> AdapterError {
1665 use OptimizerError::*;
1666 match e {
1667 PlanError(e) => Self::PlanError(e),
1668 RecursionLimitError(e) => Self::RecursionLimit(e),
1669 EvalError(e) => Self::Eval(e),
1670 InternalUnsafeMfpPlan(e) => Self::Internal(e),
1671 Internal(e) => Self::Internal(e),
1672 RestrictedFunction(func) => {
1673 Self::Unauthorized(mz_sql::rbac::UnauthorizedError::RestrictedSystemObject {
1674 object_name: format!("function {func}"),
1675 })
1676 }
1677 e => Self::Optimizer(e),
1678 }
1679 }
1680}
1681
1682impl From<NotNullViolation> for AdapterError {
1683 fn from(e: NotNullViolation) -> AdapterError {
1684 AdapterError::ConstraintViolation(e)
1685 }
1686}
1687
1688impl From<RecursionLimitError> for AdapterError {
1689 fn from(e: RecursionLimitError) -> AdapterError {
1690 AdapterError::RecursionLimit(e)
1691 }
1692}
1693
1694impl From<oneshot::error::RecvError> for AdapterError {
1695 fn from(e: oneshot::error::RecvError) -> AdapterError {
1696 AdapterError::Unstructured(e.into())
1697 }
1698}
1699
1700impl From<StorageError> for AdapterError {
1701 fn from(e: StorageError) -> Self {
1702 AdapterError::Storage(e)
1703 }
1704}
1705
1706impl From<compute_error::InstanceExists> for AdapterError {
1707 fn from(e: compute_error::InstanceExists) -> Self {
1708 AdapterError::Compute(e.into())
1709 }
1710}
1711
1712impl From<TimestampError> for AdapterError {
1713 fn from(e: TimestampError) -> Self {
1714 let e: EvalError = e.into();
1715 e.into()
1716 }
1717}
1718
1719impl From<mz_sql_parser::parser::ParserStatementError> for AdapterError {
1720 fn from(e: mz_sql_parser::parser::ParserStatementError) -> Self {
1721 AdapterError::ParseError(e)
1722 }
1723}
1724
1725impl From<VarError> for AdapterError {
1726 fn from(e: VarError) -> Self {
1727 let e: mz_catalog::memory::error::Error = e.into();
1728 e.into()
1729 }
1730}
1731
1732impl From<rbac::UnauthorizedError> for AdapterError {
1733 fn from(e: rbac::UnauthorizedError) -> Self {
1734 AdapterError::Unauthorized(e)
1735 }
1736}
1737
1738impl From<mz_sql_parser::ast::IdentError> for AdapterError {
1739 fn from(value: mz_sql_parser::ast::IdentError) -> Self {
1740 AdapterError::PlanError(PlanError::InvalidIdent(value))
1741 }
1742}
1743
1744impl From<mz_pgwire_common::ConnectionError> for AdapterError {
1745 fn from(value: mz_pgwire_common::ConnectionError) -> Self {
1746 match value {
1747 mz_pgwire_common::ConnectionError::TooManyConnections { current, limit } => {
1748 AdapterError::ResourceExhaustion {
1749 resource_type: "connection".into(),
1750 limit_name: "max_connections".into(),
1751 desired: (current + 1).to_string(),
1752 limit: limit.to_string(),
1753 current: current.to_string(),
1754 }
1755 }
1756 }
1757 }
1758}
1759
1760impl From<NetworkPolicyError> for AdapterError {
1761 fn from(value: NetworkPolicyError) -> Self {
1762 AdapterError::NetworkPolicyDenied(value)
1763 }
1764}
1765
1766impl From<ConnectionValidationError> for AdapterError {
1767 fn from(e: ConnectionValidationError) -> AdapterError {
1768 AdapterError::ConnectionValidation(e)
1769 }
1770}
1771
1772impl Error for AdapterError {}