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