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