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