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