1use std::collections::BTreeSet;
11use std::error::Error;
12use std::num::{ParseIntError, TryFromIntError};
13use std::sync::Arc;
14use std::time::Duration;
15use std::{fmt, io};
16
17use itertools::Itertools;
18use mz_expr::EvalError;
19use mz_mysql_util::MySqlError;
20use mz_ore::error::ErrorExt;
21use mz_ore::stack::RecursionLimitError;
22use mz_ore::str::{StrExt, separated};
23use mz_postgres_util::PostgresError;
24use mz_repr::adt::char::InvalidCharLengthError;
25use mz_repr::adt::mz_acl_item::AclMode;
26use mz_repr::adt::numeric::InvalidNumericMaxScaleError;
27use mz_repr::adt::timestamp::InvalidTimestampPrecisionError;
28use mz_repr::adt::varchar::InvalidVarCharMaxLengthError;
29use mz_repr::{CatalogItemId, ColumnName, strconv};
30use mz_sql_parser::ast::display::AstDisplay;
31use mz_sql_parser::ast::{IdentError, UnresolvedItemName};
32use mz_sql_parser::parser::{ParserError, ParserStatementError};
33use mz_sql_server_util::SqlServerError;
34use mz_storage_types::sources::ExternalReferenceResolutionError;
35
36use crate::catalog::{
37 CatalogError, CatalogItemType, ErrorMessageObjectDescription, SystemObjectType,
38};
39use crate::names::{PartialItemName, ResolvedItemName};
40use crate::plan::ObjectType;
41use crate::plan::plan_utils::JoinSide;
42use crate::plan::scope::ScopeItem;
43use crate::plan::typeconv::CastContext;
44use crate::pure::error::{
45 CsrPurificationError, GluePurificationError, IcebergSinkPurificationError,
46 KafkaSinkPurificationError, KafkaSourcePurificationError, LoadGeneratorSourcePurificationError,
47 MySqlSourcePurificationError, PgSourcePurificationError, SqlServerSourcePurificationError,
48};
49use crate::session::vars::VarError;
50
51#[derive(Debug)]
52pub enum PlanError {
53 Unsupported {
55 feature: String,
56 discussion_no: Option<usize>,
57 },
58 HydrationSizeEqualsClusterSize {
60 size: String,
61 },
62 NeverSupported {
64 feature: String,
65 documentation_link: Option<String>,
66 details: Option<String>,
67 },
68 UnknownColumn {
69 table: Option<PartialItemName>,
70 column: ColumnName,
71 similar: Box<[ColumnName]>,
72 },
73 UngroupedColumn {
74 table: Option<PartialItemName>,
75 column: ColumnName,
76 },
77 ItemWithoutColumns {
78 name: String,
79 item_type: CatalogItemType,
80 },
81 WrongJoinTypeForLateralColumn {
82 table: Option<PartialItemName>,
83 column: ColumnName,
84 },
85 AmbiguousColumn(ColumnName),
86 TooManyColumns {
87 max_num_columns: usize,
88 req_num_columns: usize,
89 },
90 ColumnAlreadyExists {
91 column_name: ColumnName,
92 object_name: String,
93 },
94 AmbiguousTable(PartialItemName),
95 UnknownColumnInUsingClause {
96 column: ColumnName,
97 join_side: JoinSide,
98 },
99 AmbiguousColumnInUsingClause {
100 column: ColumnName,
101 join_side: JoinSide,
102 },
103 MisqualifiedName(String),
104 OverqualifiedDatabaseName(String),
105 OverqualifiedSchemaName(String),
106 UnderqualifiedColumnName(String),
107 SubqueriesDisallowed {
108 context: String,
109 },
110 UnknownParameter(usize),
111 ParameterNotAllowed(String),
112 WrongParameterType(usize, String, String),
113 RecursionLimit(RecursionLimitError),
114 StrconvParse(strconv::ParseError),
115 Catalog(CatalogError),
116 UpsertSinkWithoutKey,
117 UpsertSinkWithInvalidKey {
118 name: String,
119 desired_key: Vec<String>,
120 valid_keys: Vec<Vec<String>>,
121 },
122 IcebergSinkUnsupportedKeyType {
123 column: String,
124 column_type: String,
125 },
126 InvalidWmrRecursionLimit(String),
127 InvalidNumericMaxScale(InvalidNumericMaxScaleError),
128 InvalidCharLength(InvalidCharLengthError),
129 InvalidId(CatalogItemId),
130 InvalidIdent(IdentError),
131 InvalidObject(Box<ResolvedItemName>),
132 InvalidObjectType {
133 expected_type: SystemObjectType,
134 actual_type: SystemObjectType,
135 object_name: String,
136 },
137 InvalidPrivilegeTypes {
138 invalid_privileges: AclMode,
139 object_description: ErrorMessageObjectDescription,
140 },
141 InvalidVarCharMaxLength(InvalidVarCharMaxLengthError),
142 InvalidTimestampPrecision(InvalidTimestampPrecisionError),
143 InvalidSecret(Box<ResolvedItemName>),
144 InvalidTemporarySchema,
145 InvalidCast {
146 name: String,
147 ccx: CastContext,
148 from: String,
149 to: String,
150 },
151 UnsupportedRangeElementType {
153 element_type_name: String,
154 },
155 InvalidTable {
156 name: String,
157 },
158 InvalidVersion {
159 name: String,
160 version: String,
161 },
162 InvalidSinkFrom {
163 name: String,
164 item_type: String,
165 },
166 InvalidDependency {
167 name: String,
168 item_type: String,
169 },
170 MangedReplicaName(String),
171 ParserStatement(ParserStatementError),
172 Parser(ParserError),
173 DropViewOnMaterializedView(String),
174 DependentObjectsStillExist {
175 object_type: String,
176 object_name: String,
177 dependents: Vec<(String, String)>,
179 },
180 AlterViewOnMaterializedView(String),
181 ShowCreateViewOnMaterializedView(String),
182 ExplainViewOnMaterializedView(String),
183 UnacceptableTimelineName(String),
184 FetchingCsrSchemaFailed {
185 schema_lookup: String,
186 cause: Arc<dyn Error + Send + Sync>,
187 },
188 PostgresConnectionErr {
189 cause: Arc<mz_postgres_util::PostgresError>,
190 },
191 MySqlConnectionErr {
192 cause: Arc<MySqlError>,
193 },
194 SqlServerConnectionErr {
195 cause: Arc<SqlServerError>,
196 },
197 SubsourceNameConflict {
198 name: UnresolvedItemName,
199 upstream_references: Vec<UnresolvedItemName>,
200 },
201 SubsourceDuplicateReference {
202 name: UnresolvedItemName,
203 target_names: Vec<UnresolvedItemName>,
204 },
205 NoTablesFoundForSchemas(Vec<String>),
206 InvalidProtobufSchema {
207 cause: protobuf_native::OperationFailedError,
208 },
209 InvalidOptionValue {
210 option_name: String,
213 err: Box<PlanError>,
214 },
215 UnexpectedDuplicateReference {
216 name: UnresolvedItemName,
217 },
218 RecursiveTypeMismatch(String, Vec<String>, Vec<String>),
220 UnknownFunction {
221 name: String,
222 arg_types: Vec<String>,
223 },
224 IndistinctFunction {
225 name: String,
226 arg_types: Vec<String>,
227 },
228 UnknownOperator {
229 name: String,
230 arg_types: Vec<String>,
231 },
232 IndistinctOperator {
233 name: String,
234 arg_types: Vec<String>,
235 },
236 InvalidPrivatelinkAvailabilityZone {
237 name: String,
238 supported_azs: BTreeSet<String>,
239 },
240 DuplicatePrivatelinkAvailabilityZone {
241 duplicate_azs: BTreeSet<String>,
242 },
243 InvalidSchemaName,
244 ItemAlreadyExists {
245 name: String,
246 item_type: CatalogItemType,
247 },
248 ManagedCluster {
249 cluster_name: String,
250 },
251 InvalidKeysInSubscribeEnvelopeUpsert,
252 InvalidKeysInSubscribeEnvelopeDebezium,
253 DuplicateKeyColumnInSubscribeEnvelope {
254 column_name: String,
255 },
256 InvalidPartitionByEnvelopeDebezium {
257 column_name: String,
258 },
259 InvalidOrderByInSubscribeWithinTimestampOrderBy,
260 FromValueRequiresParen,
261 VarError(VarError),
262 UnsolvablePolymorphicFunctionInput,
263 ShowCommandInView,
264 WebhookValidationDoesNotUseColumns,
265 WebhookValidationNonDeterministic,
266 InternalFunctionCall,
267 CommentTooLong {
268 length: usize,
269 max_size: usize,
270 },
271 InvalidTimestampInterval {
272 min: Duration,
273 max: Duration,
274 requested: Duration,
275 },
276 InvalidGroupSizeHints,
277 PgSourcePurification(PgSourcePurificationError),
278 KafkaSourcePurification(KafkaSourcePurificationError),
279 KafkaSinkPurification(KafkaSinkPurificationError),
280 IcebergSinkPurification(IcebergSinkPurificationError),
281 LoadGeneratorSourcePurification(LoadGeneratorSourcePurificationError),
282 CsrPurification(CsrPurificationError),
283 GluePurification(GluePurificationError),
284 MySqlSourcePurification(MySqlSourcePurificationError),
285 SqlServerSourcePurificationError(SqlServerSourcePurificationError),
286 UseTablesForSources(String),
287 MissingName(CatalogItemType),
288 InvalidRefreshAt,
289 InvalidRefreshEveryAlignedTo,
290 MismatchedObjectType {
291 name: PartialItemName,
292 is_type: ObjectType,
293 expected_type: ObjectType,
294 },
295 TableContainsUningestableTypes {
297 name: String,
298 type_: String,
299 column: String,
300 },
301 RetainHistoryLow {
302 limit: Duration,
303 },
304 RetainHistoryRequired,
305 UntilReadyTimeoutRequired,
306 SubsourceResolutionError(ExternalReferenceResolutionError),
307 Replan(String),
308 Internal(String),
309 NetworkPolicyLockoutError,
310 NetworkPolicyInUse,
311 ConstantExpressionSimplificationFailed(String),
313 InvalidLimit(String),
314 InvalidOffset(String),
315 UnknownCursor(String),
317 CopyFromTargetTableDropped {
318 target_name: String,
319 },
320 InvalidAsOfUpTo,
322 InvalidReplacement {
323 item_type: CatalogItemType,
324 item_name: PartialItemName,
325 replacement_type: CatalogItemType,
326 replacement_name: PartialItemName,
327 },
328 Unstructured(String),
330}
331
332impl PlanError {
333 pub(crate) fn ungrouped_column(item: &ScopeItem) -> PlanError {
334 PlanError::UngroupedColumn {
335 table: item.table_name.clone(),
336 column: item.column_name.clone(),
337 }
338 }
339
340 pub fn detail(&self) -> Option<String> {
341 match self {
342 Self::HydrationSizeEqualsClusterSize { .. } => Some(
343 "A burst replica at the same size as the steady replicas would not \
344 accelerate hydration."
345 .into(),
346 ),
347 Self::NeverSupported { details, .. } => details.clone(),
348 Self::FetchingCsrSchemaFailed { cause, .. } => Some(cause.to_string_with_causes()),
349 Self::PostgresConnectionErr { cause } => Some(cause.to_string_with_causes()),
350 Self::InvalidProtobufSchema { cause } => Some(cause.to_string_with_causes()),
351 Self::InvalidOptionValue { err, .. } => err.detail(),
352 Self::UpsertSinkWithInvalidKey {
353 name,
354 desired_key,
355 valid_keys,
356 } => {
357 let valid_keys = if valid_keys.is_empty() {
358 "There are no known valid unique keys for the underlying relation.".into()
359 } else {
360 format!(
361 "The following keys are known to be unique for the underlying relation:\n{}",
362 valid_keys
363 .iter()
364 .map(|k|
365 format!(" ({})", k.iter().map(|c| c.as_str().quoted()).join(", "))
366 )
367 .join("\n"),
368 )
369 };
370 Some(format!(
371 "Materialize could not prove that the specified upsert envelope key ({}) \
372 was a unique key of the underlying relation {}. {valid_keys}",
373 separated(", ", desired_key.iter().map(|c| c.as_str().quoted())),
374 name.quoted()
375 ))
376 }
377 Self::VarError(e) => e.detail(),
378 Self::InternalFunctionCall => Some("This function is for the internal use of the database system and cannot be called directly.".into()),
379 Self::PgSourcePurification(e) => e.detail(),
380 Self::MySqlSourcePurification(e) => e.detail(),
381 Self::SqlServerSourcePurificationError(e) => e.detail(),
382 Self::KafkaSourcePurification(e) => e.detail(),
383 Self::LoadGeneratorSourcePurification(e) => e.detail(),
384 Self::CsrPurification(e) => e.detail(),
385 Self::GluePurification(e) => e.detail(),
386 Self::KafkaSinkPurification(e) => e.detail(),
387 Self::IcebergSinkPurification(e) => e.detail(),
388 Self::SubsourceNameConflict {
389 name: _,
390 upstream_references,
391 } => Some(format!(
392 "referenced tables with duplicate name: {}",
393 itertools::join(upstream_references, ", ")
394 )),
395 Self::SubsourceDuplicateReference {
396 name: _,
397 target_names,
398 } => Some(format!(
399 "subsources referencing table: {}",
400 itertools::join(target_names, ", ")
401 )),
402 Self::InvalidPartitionByEnvelopeDebezium { .. } => Some(
403 "When using ENVELOPE DEBEZIUM, only columns in the key can be referenced in the PARTITION BY expression.".to_string()
404 ),
405 Self::NoTablesFoundForSchemas(schemas) => Some(format!(
406 "missing schemas: {}",
407 separated(", ", schemas.iter().map(|c| c.quoted()))
408 )),
409 _ => None,
410 }
411 }
412
413 pub fn hint(&self) -> Option<String> {
414 match self {
415 Self::DropViewOnMaterializedView(_) => {
416 Some("Use DROP MATERIALIZED VIEW to remove a materialized view.".into())
417 }
418 Self::DependentObjectsStillExist {..} => Some("Use DROP ... CASCADE to drop the dependent objects too.".into()),
419 Self::AlterViewOnMaterializedView(_) => {
420 Some("Use ALTER MATERIALIZED VIEW to rename a materialized view.".into())
421 }
422 Self::ShowCreateViewOnMaterializedView(_) => {
423 Some("Use SHOW CREATE MATERIALIZED VIEW to show a materialized view.".into())
424 }
425 Self::ExplainViewOnMaterializedView(_) => {
426 Some("Use EXPLAIN [...] MATERIALIZED VIEW to explain a materialized view.".into())
427 }
428 Self::UnacceptableTimelineName(_) => {
429 Some("The prefix \"mz_\" is reserved for system timelines.".into())
430 }
431 Self::PostgresConnectionErr { cause } => {
432 if let Some(cause) = cause.source() {
433 if let Some(cause) = cause.downcast_ref::<io::Error>() {
434 if cause.kind() == io::ErrorKind::TimedOut {
435 return Some(
436 "Do you have a firewall or security group that is \
437 preventing Materialize from connecting to your PostgreSQL server?"
438 .into(),
439 );
440 }
441 }
442 }
443 None
444 }
445 Self::InvalidOptionValue { err, .. } => err.hint(),
446 Self::UnknownFunction { ..} => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
447 Self::IndistinctFunction {..} => {
448 Some("Could not choose a best candidate function. You might need to add explicit type casts.".into())
449 }
450 Self::UnknownOperator {..} => {
451 Some("No operator matches the given name and argument types. You might need to add explicit type casts.".into())
452 }
453 Self::IndistinctOperator {..} => {
454 Some("Could not choose a best candidate operator. You might need to add explicit type casts.".into())
455 },
456 Self::InvalidPrivatelinkAvailabilityZone { supported_azs, ..} => {
457 let supported_azs_str = supported_azs.iter().join("\n ");
458 Some(format!("Did you supply an availability zone name instead of an ID? Known availability zone IDs:\n {}", supported_azs_str))
459 }
460 Self::DuplicatePrivatelinkAvailabilityZone { duplicate_azs, ..} => {
461 let duplicate_azs = duplicate_azs.iter().join("\n ");
462 Some(format!("Duplicated availability zones:\n {}", duplicate_azs))
463 }
464 Self::InvalidKeysInSubscribeEnvelopeUpsert => {
465 Some("All keys must be columns on the underlying relation.".into())
466 }
467 Self::InvalidKeysInSubscribeEnvelopeDebezium => {
468 Some("All keys must be columns on the underlying relation.".into())
469 }
470 Self::DuplicateKeyColumnInSubscribeEnvelope { .. } => {
471 Some("Each KEY column must be listed at most once.".into())
472 }
473 Self::InvalidOrderByInSubscribeWithinTimestampOrderBy => {
474 Some("All order bys must be output columns.".into())
475 }
476 Self::UpsertSinkWithInvalidKey { .. } | Self::UpsertSinkWithoutKey => {
477 Some("See: https://materialize.com/s/sink-key-selection".into())
478 }
479 Self::IcebergSinkUnsupportedKeyType { .. } => {
480 Some("Iceberg equality delete keys must be primitive, non-floating-point columns.".into())
481 }
482 Self::Catalog(e) => e.hint(),
483 Self::VarError(e) => e.hint(),
484 Self::PgSourcePurification(e) => e.hint(),
485 Self::MySqlSourcePurification(e) => e.hint(),
486 Self::SqlServerSourcePurificationError(e) => e.hint(),
487 Self::KafkaSourcePurification(e) => e.hint(),
488 Self::LoadGeneratorSourcePurification(e) => e.hint(),
489 Self::CsrPurification(e) => e.hint(),
490 Self::GluePurification(e) => e.hint(),
491 Self::KafkaSinkPurification(e) => e.hint(),
492 Self::UnknownColumn { table, similar, .. } => {
493 let suffix = "Make sure to surround case sensitive names in double quotes.";
494 match &similar[..] {
495 [] => None,
496 [column] => Some(format!("The similarly named column {} does exist. {suffix}", ColumnDisplay { table, column })),
497 names => {
498 let similar = names.into_iter().map(|column| ColumnDisplay { table, column }).join(", ");
499 Some(format!("There are similarly named columns that do exist: {similar}. {suffix}"))
500 }
501 }
502 }
503 Self::RecursiveTypeMismatch(..) => {
504 Some("You will need to rewrite or cast the query's expressions.".into())
505 },
506 Self::InvalidRefreshAt
507 | Self::InvalidRefreshEveryAlignedTo => {
508 Some("Calling `mz_now()` is allowed.".into())
509 },
510 Self::TableContainsUningestableTypes { column,.. } => {
511 Some(format!("Remove the table or use TEXT COLUMNS ({column}, ..) to ingest this column as text"))
512 }
513 Self::RetainHistoryLow { .. } | Self::RetainHistoryRequired => {
514 Some("Use ALTER ... RESET (RETAIN HISTORY) to set the retain history to its default and lowest value.".into())
515 }
516 Self::NetworkPolicyInUse => {
517 Some("Use ALTER SYSTEM SET 'network_policy' to change the default network policy.".into())
518 }
519 Self::WrongParameterType(_, _, _) => {
520 Some("EXECUTE automatically inserts only such casts that are allowed in an assignment cast context. Try adding an explicit cast.".into())
521 }
522 Self::InvalidSchemaName => {
523 Some("Use SET schema = name to select a schema. Use SHOW SCHEMAS to list available schemas. Use SHOW search_path to show the schema names that we looked for, but none of them existed.".into())
524 }
525 _ => None,
526 }
527 }
528}
529
530impl fmt::Display for PlanError {
531 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
532 match self {
533 Self::Unsupported { feature, discussion_no } => {
534 write!(f, "{} not yet supported", feature)?;
535 if let Some(discussion_no) = discussion_no {
536 write!(f, ", see https://github.com/MaterializeInc/materialize/discussions/{} for more details", discussion_no)?;
537 }
538 Ok(())
539 }
540 Self::HydrationSizeEqualsClusterSize { size } => {
541 write!(f, "HYDRATION SIZE must differ from the cluster SIZE ('{size}')")
542 }
543 Self::NeverSupported { feature, documentation_link: documentation_path,.. } => {
544 write!(f, "{feature} is not supported",)?;
545 if let Some(documentation_path) = documentation_path {
546 write!(f, ", for more information consult the documentation at https://materialize.com/docs/{documentation_path}")?;
547 }
548 Ok(())
549 }
550 Self::UnknownColumn { table, column, similar: _ } => write!(
551 f,
552 "column {} does not exist",
553 ColumnDisplay { table, column }
554 ),
555 Self::UngroupedColumn { table, column } => write!(
556 f,
557 "column {} must appear in the GROUP BY clause or be used in an aggregate function",
558 ColumnDisplay { table, column },
559 ),
560 Self::ItemWithoutColumns { name, item_type } => {
561 let name = name.quoted();
562 write!(f, "{item_type} {name} does not have columns")
563 }
564 Self::WrongJoinTypeForLateralColumn { table, column } => write!(
565 f,
566 "column {} cannot be referenced from this part of the query: \
567 the combining JOIN type must be INNER or LEFT for a LATERAL reference",
568 ColumnDisplay { table, column },
569 ),
570 Self::AmbiguousColumn(column) => write!(
571 f,
572 "column reference {} is ambiguous",
573 column.quoted()
574 ),
575 Self::TooManyColumns { max_num_columns, req_num_columns } => write!(
576 f,
577 "attempt to create relation with too many columns, {} max: {}",
578 req_num_columns, max_num_columns
579 ),
580 Self::ColumnAlreadyExists { column_name, object_name } => write!(
581 f,
582 "column {} of relation {} already exists",
583 column_name.quoted(), object_name.quoted(),
584 ),
585 Self::AmbiguousTable(table) => write!(
586 f,
587 "table reference {} is ambiguous",
588 table.item.as_str().quoted()
589 ),
590 Self::UnknownColumnInUsingClause { column, join_side } => write!(
591 f,
592 "column {} specified in USING clause does not exist in {} table",
593 column.quoted(),
594 join_side,
595 ),
596 Self::AmbiguousColumnInUsingClause { column, join_side } => write!(
597 f,
598 "common column name {} appears more than once in {} table",
599 column.quoted(),
600 join_side,
601 ),
602 Self::MisqualifiedName(name) => write!(
603 f,
604 "qualified name did not have between 1 and 3 components: {}",
605 name
606 ),
607 Self::OverqualifiedDatabaseName(name) => write!(
608 f,
609 "database name '{}' does not have exactly one component",
610 name
611 ),
612 Self::OverqualifiedSchemaName(name) => write!(
613 f,
614 "schema name '{}' cannot have more than two components",
615 name
616 ),
617 Self::UnderqualifiedColumnName(name) => write!(
618 f,
619 "column name '{}' must have at least a table qualification",
620 name
621 ),
622 Self::UnacceptableTimelineName(name) => {
623 write!(f, "unacceptable timeline name {}", name.quoted(),)
624 }
625 Self::SubqueriesDisallowed { context } => {
626 write!(f, "{} does not allow subqueries", context)
627 }
628 Self::UnknownParameter(n) => write!(f, "there is no parameter ${}", n),
629 Self::ParameterNotAllowed(object_type) => write!(f, "{} cannot have parameters", object_type),
630 Self::WrongParameterType(i, expected_ty, actual_ty) => write!(f, "unable to cast given parameter ${}: expected {}, got {}", i, expected_ty, actual_ty),
631 Self::RecursionLimit(e) => write!(f, "{}", e),
632 Self::StrconvParse(e) => write!(f, "{}", e),
633 Self::Catalog(e) => write!(f, "{}", e),
634 Self::UpsertSinkWithoutKey => write!(f, "upsert sinks must specify a key"),
635 Self::UpsertSinkWithInvalidKey { .. } => {
636 write!(f, "upsert key could not be validated as unique")
637 }
638 Self::IcebergSinkUnsupportedKeyType { column, column_type } => {
639 write!(f, "column {column} has type {column_type} which cannot be used as an Iceberg equality delete key")
640 }
641 Self::InvalidWmrRecursionLimit(msg) => write!(f, "Invalid WITH MUTUALLY RECURSIVE recursion limit. {}", msg),
642 Self::InvalidNumericMaxScale(e) => e.fmt(f),
643 Self::InvalidCharLength(e) => e.fmt(f),
644 Self::InvalidVarCharMaxLength(e) => e.fmt(f),
645 Self::InvalidTimestampPrecision(e) => e.fmt(f),
646 Self::Parser(e) => e.fmt(f),
647 Self::ParserStatement(e) => e.fmt(f),
648 Self::Unstructured(e) => write!(f, "{}", e),
649 Self::InvalidId(id) => write!(f, "invalid id {}", id),
650 Self::InvalidIdent(err) => write!(f, "invalid identifier, {err}"),
651 Self::InvalidObject(i) => write!(f, "{} is not a database object", i.full_name_str()),
652 Self::InvalidObjectType{expected_type, actual_type, object_name} => write!(f, "{actual_type} {object_name} is not a {expected_type}"),
653 Self::InvalidPrivilegeTypes{ invalid_privileges, object_description, } => {
654 write!(f, "invalid privilege types {} for {}", invalid_privileges.to_error_string(), object_description)
655 },
656 Self::InvalidSecret(i) => write!(f, "{} is not a secret", i.full_name_str()),
657 Self::InvalidTemporarySchema => {
658 write!(f, "cannot create temporary item in non-temporary schema")
659 }
660 Self::InvalidCast { name, ccx, from, to } =>{
661 write!(
662 f,
663 "{name} does not support {ccx}casting from {from} to {to}",
664 ccx = if matches!(ccx, CastContext::Implicit) {
665 "implicitly "
666 } else {
667 ""
668 },
669 )
670 }
671 Self::UnsupportedRangeElementType { element_type_name } => {
672 write!(f, "range type over {} is not supported", element_type_name)
673 }
674 Self::InvalidTable { name } => {
675 write!(f, "invalid table definition for {}", name.quoted())
676 },
677 Self::InvalidVersion { name, version } => {
678 write!(f, "invalid version {} for {}", version.quoted(), name.quoted())
679 },
680 Self::InvalidSinkFrom { name, item_type } => {
681 write!(f, "{item_type} {name} cannot be exported as a sink")
682 },
683 Self::InvalidDependency { name, item_type } => {
684 write!(f, "{item_type} {name} cannot be depended upon")
685 },
686 Self::DropViewOnMaterializedView(name)
687 | Self::AlterViewOnMaterializedView(name)
688 | Self::ShowCreateViewOnMaterializedView(name)
689 | Self::ExplainViewOnMaterializedView(name) => write!(f, "{name} is not a view"),
690 Self::FetchingCsrSchemaFailed { schema_lookup, .. } => {
691 write!(f, "failed to fetch schema {schema_lookup} from schema registry")
692 }
693 Self::PostgresConnectionErr { .. } => {
694 write!(f, "failed to connect to PostgreSQL database")
695 }
696 Self::MySqlConnectionErr { cause } => {
697 write!(f, "failed to connect to MySQL database: {}", cause)
698 }
699 Self::SqlServerConnectionErr { cause } => {
700 write!(f, "failed to connect to SQL Server database: {}", cause)
701 }
702 Self::SubsourceNameConflict {
703 name , upstream_references: _,
704 } => {
705 write!(f, "multiple subsources would be named {}", name)
706 },
707 Self::SubsourceDuplicateReference {
708 name,
709 target_names: _,
710 } => {
711 write!(f, "multiple subsources refer to table {}", name)
712 },
713 Self::NoTablesFoundForSchemas(schemas) => {
714 write!(f, "no tables found in referenced schemas: {}",
715 separated(", ", schemas.iter().map(|c| c.quoted()))
716 )
717 },
718 Self::InvalidProtobufSchema { .. } => {
719 write!(f, "invalid protobuf schema")
720 }
721 Self::DependentObjectsStillExist {object_type, object_name, dependents} => {
722 let reason = match &dependents[..] {
723 [] => " because other objects depend on it".to_string(),
724 dependents => {
725 let dependents = dependents.iter().map(|(dependent_type, dependent_name)| format!("{} {}", dependent_type, dependent_name.quoted())).join(", ");
726 format!(": still depended upon by {dependents}")
727 },
728 };
729 let object_name = object_name.quoted();
730 write!(f, "cannot drop {object_type} {object_name}{reason}")
731 }
732 Self::InvalidOptionValue { option_name, err } => write!(f, "invalid {} option value: {}", option_name, err),
733 Self::UnexpectedDuplicateReference { name } => write!(f, "unexpected multiple references to {}", name.to_ast_string_simple()),
734 Self::RecursiveTypeMismatch(name, declared, inferred) => {
735 let declared = separated(", ", declared);
736 let inferred = separated(", ", inferred);
737 let name = name.quoted();
738 write!(f, "WITH MUTUALLY RECURSIVE query {name} declared types ({declared}), but query returns types ({inferred})")
739 },
740 Self::UnknownFunction {name, arg_types, ..} => {
741 write!(f, "function {}({}) does not exist", name, arg_types.join(", "))
742 },
743 Self::IndistinctFunction {name, arg_types, ..} => {
744 write!(f, "function {}({}) is not unique", name, arg_types.join(", "))
745 },
746 Self::UnknownOperator {name, arg_types, ..} => {
747 write!(f, "operator does not exist: {}", match arg_types.as_slice(){
748 [typ] => format!("{} {}", name, typ),
749 [ltyp, rtyp] => {
750 format!("{} {} {}", ltyp, name, rtyp)
751 }
752 _ => unreachable!("non-unary non-binary operator"),
753 })
754 },
755 Self::IndistinctOperator {name, arg_types, ..} => {
756 write!(f, "operator is not unique: {}", match arg_types.as_slice(){
757 [typ] => format!("{} {}", name, typ),
758 [ltyp, rtyp] => {
759 format!("{} {} {}", ltyp, name, rtyp)
760 }
761 _ => unreachable!("non-unary non-binary operator"),
762 })
763 },
764 Self::InvalidPrivatelinkAvailabilityZone { name, ..} => write!(f, "invalid AWS PrivateLink availability zone {}", name.quoted()),
765 Self::DuplicatePrivatelinkAvailabilityZone {..} => write!(f, "connection cannot contain duplicate availability zones"),
766 Self::InvalidSchemaName => write!(f, "no valid schema selected"),
767 Self::ItemAlreadyExists { name, item_type } => write!(f, "{item_type} {} already exists", name.quoted()),
768 Self::ManagedCluster {cluster_name} => write!(f, "cannot modify managed cluster {cluster_name}"),
769 Self::InvalidKeysInSubscribeEnvelopeUpsert => {
770 write!(f, "invalid keys in SUBSCRIBE ENVELOPE UPSERT (KEY (..))")
771 }
772 Self::InvalidKeysInSubscribeEnvelopeDebezium => {
773 write!(f, "invalid keys in SUBSCRIBE ENVELOPE DEBEZIUM (KEY (..))")
774 }
775 Self::DuplicateKeyColumnInSubscribeEnvelope { column_name } => {
776 write!(
777 f,
778 "column {} appears more than once in SUBSCRIBE ENVELOPE KEY clause",
779 column_name.quoted(),
780 )
781 }
782 Self::InvalidPartitionByEnvelopeDebezium { column_name } => {
783 write!(
784 f,
785 "PARTITION BY expression cannot refer to non-key column {}",
786 column_name.quoted(),
787 )
788 }
789 Self::InvalidOrderByInSubscribeWithinTimestampOrderBy => {
790 write!(f, "invalid ORDER BY in SUBSCRIBE WITHIN TIMESTAMP ORDER BY")
791 }
792 Self::FromValueRequiresParen => f.write_str(
793 "VALUES expression in FROM clause must be surrounded by parentheses"
794 ),
795 Self::VarError(e) => e.fmt(f),
796 Self::UnsolvablePolymorphicFunctionInput => f.write_str(
797 "could not determine polymorphic type because input has type unknown"
798 ),
799 Self::ShowCommandInView => f.write_str("SHOW commands are not allowed in views"),
800 Self::WebhookValidationDoesNotUseColumns => f.write_str(
801 "expression provided in CHECK does not reference any columns"
802 ),
803 Self::WebhookValidationNonDeterministic => f.write_str(
804 "expression provided in CHECK is not deterministic"
805 ),
806 Self::InternalFunctionCall => f.write_str("cannot call function with arguments of type internal"),
807 Self::CommentTooLong { length, max_size } => {
808 write!(f, "provided comment was {length} bytes long, max size is {max_size} bytes")
809 }
810 Self::InvalidTimestampInterval { min, max, requested } => {
811 write!(f, "invalid timestamp interval of {}ms, must be in the range [{}ms, {}ms]", requested.as_millis(), min.as_millis(), max.as_millis())
812 }
813 Self::InvalidGroupSizeHints => f.write_str("EXPECTED GROUP SIZE cannot be provided \
814 simultaneously with any of AGGREGATE INPUT GROUP SIZE, DISTINCT ON INPUT GROUP SIZE, \
815 or LIMIT INPUT GROUP SIZE"),
816 Self::PgSourcePurification(e) => write!(f, "POSTGRES source validation: {}", e),
817 Self::KafkaSourcePurification(e) => write!(f, "KAFKA source validation: {}", e),
818 Self::LoadGeneratorSourcePurification(e) => write!(f, "LOAD GENERATOR source validation: {}", e),
819 Self::KafkaSinkPurification(e) => write!(f, "KAFKA sink validation: {}", e),
820 Self::IcebergSinkPurification(e) => write!(f, "ICEBERG sink validation: {}", e),
821 Self::CsrPurification(e) => write!(f, "CONFLUENT SCHEMA REGISTRY validation: {}", e),
822 Self::GluePurification(e) => write!(f, "AWS GLUE SCHEMA REGISTRY validation: {}", e),
823 Self::MySqlSourcePurification(e) => write!(f, "MYSQL source validation: {}", e),
824 Self::SqlServerSourcePurificationError(e) => write!(f, "SQL SERVER source validation: {}", e),
825 Self::UseTablesForSources(command) => write!(f, "{command} not supported; use CREATE TABLE .. FROM SOURCE instead"),
826 Self::MangedReplicaName(name) => {
827 write!(f, "{name} is reserved for replicas of managed clusters")
828 }
829 Self::MissingName(item_type) => {
830 write!(f, "unspecified name for {item_type}")
831 }
832 Self::InvalidRefreshAt => {
833 write!(f, "REFRESH AT argument must be an expression that can be simplified \
834 and/or cast to a constant whose type is mz_timestamp")
835 }
836 Self::InvalidRefreshEveryAlignedTo => {
837 write!(f, "REFRESH EVERY ... ALIGNED TO argument must be an expression that can be simplified \
838 and/or cast to a constant whose type is mz_timestamp")
839 }
840 Self::MismatchedObjectType {
841 name,
842 is_type,
843 expected_type,
844 } => {
845 write!(
846 f,
847 "{name} is {} {} not {} {}",
848 if *is_type == ObjectType::Index {
849 "an"
850 } else {
851 "a"
852 },
853 is_type.to_string().to_lowercase(),
854 if *expected_type == ObjectType::Index {
855 "an"
856 } else {
857 "a"
858 },
859 expected_type.to_string().to_lowercase()
860 )
861 }
862 Self::TableContainsUningestableTypes { name, type_, column } => {
863 write!(f, "table {name} contains column {column} of type {type_} which Materialize cannot currently ingest")
864 },
865 Self::RetainHistoryLow { limit } => {
866 write!(f, "RETAIN HISTORY cannot be set lower than {}ms", limit.as_millis())
867 },
868 Self::RetainHistoryRequired => {
869 write!(f, "RETAIN HISTORY cannot be disabled or set to 0")
870 },
871 Self::SubsourceResolutionError(e) => write!(f, "{}", e),
872 Self::Replan(msg) => write!(f, "internal error while replanning, please contact support: {msg}"),
873 Self::Internal(msg) => write!(f, "internal error: {msg}"),
874 Self::NetworkPolicyLockoutError => write!(f, "policy would block current session IP"),
875 Self::NetworkPolicyInUse => write!(f, "network policy is currently in use"),
876 Self::UntilReadyTimeoutRequired => {
877 write!(f, "TIMEOUT=<duration> option is required for ALTER CLUSTER ... WITH (WAIT UNTIL READY ( ... ))")
878 },
879 Self::ConstantExpressionSimplificationFailed(e) => write!(f, "{}", e),
880 Self::InvalidLimit(e) => write!(f, "Invalid LIMIT clause: {}", e),
881 Self::InvalidOffset(e) => write!(f, "Invalid OFFSET clause: {}", e),
882 Self::UnknownCursor(name) => {
883 write!(f, "cursor {} does not exist", name.quoted())
884 }
885 Self::CopyFromTargetTableDropped { target_name: name } => {
886 write!(f, "COPY FROM's target table {} was dropped", name.quoted())
887 }
888 Self::InvalidAsOfUpTo => write!(
889 f,
890 "AS OF or UP TO should be castable to a (non-null) mz_timestamp value",
891 ),
892 Self::InvalidReplacement {
893 item_type, item_name, replacement_type, replacement_name,
894 } => {
895 write!(
896 f,
897 "cannot replace {item_type} {item_name} \
898 with {replacement_type} {replacement_name}",
899 )
900 }
901 }
902 }
903}
904
905impl Error for PlanError {}
906
907impl From<CatalogError> for PlanError {
908 fn from(e: CatalogError) -> PlanError {
909 PlanError::Catalog(e)
910 }
911}
912
913impl From<strconv::ParseError> for PlanError {
914 fn from(e: strconv::ParseError) -> PlanError {
915 PlanError::StrconvParse(e)
916 }
917}
918
919impl From<RecursionLimitError> for PlanError {
920 fn from(e: RecursionLimitError) -> PlanError {
921 PlanError::RecursionLimit(e)
922 }
923}
924
925impl From<InvalidNumericMaxScaleError> for PlanError {
926 fn from(e: InvalidNumericMaxScaleError) -> PlanError {
927 PlanError::InvalidNumericMaxScale(e)
928 }
929}
930
931impl From<InvalidCharLengthError> for PlanError {
932 fn from(e: InvalidCharLengthError) -> PlanError {
933 PlanError::InvalidCharLength(e)
934 }
935}
936
937impl From<InvalidVarCharMaxLengthError> for PlanError {
938 fn from(e: InvalidVarCharMaxLengthError) -> PlanError {
939 PlanError::InvalidVarCharMaxLength(e)
940 }
941}
942
943impl From<InvalidTimestampPrecisionError> for PlanError {
944 fn from(e: InvalidTimestampPrecisionError) -> PlanError {
945 PlanError::InvalidTimestampPrecision(e)
946 }
947}
948
949impl From<anyhow::Error> for PlanError {
950 fn from(e: anyhow::Error) -> PlanError {
951 sql_err!("{}", e.display_with_causes())
953 }
954}
955
956impl From<TryFromIntError> for PlanError {
957 fn from(e: TryFromIntError) -> PlanError {
958 sql_err!("{}", e.display_with_causes())
959 }
960}
961
962impl From<ParseIntError> for PlanError {
963 fn from(e: ParseIntError) -> PlanError {
964 sql_err!("{}", e.display_with_causes())
965 }
966}
967
968impl From<EvalError> for PlanError {
969 fn from(e: EvalError) -> PlanError {
970 sql_err!("{}", e.display_with_causes())
971 }
972}
973
974impl From<ParserError> for PlanError {
975 fn from(e: ParserError) -> PlanError {
976 PlanError::Parser(e)
977 }
978}
979
980impl From<ParserStatementError> for PlanError {
981 fn from(e: ParserStatementError) -> PlanError {
982 PlanError::ParserStatement(e)
983 }
984}
985
986impl From<PostgresError> for PlanError {
987 fn from(e: PostgresError) -> PlanError {
988 PlanError::PostgresConnectionErr { cause: Arc::new(e) }
989 }
990}
991
992impl From<MySqlError> for PlanError {
993 fn from(e: MySqlError) -> PlanError {
994 PlanError::MySqlConnectionErr { cause: Arc::new(e) }
995 }
996}
997
998impl From<SqlServerError> for PlanError {
999 fn from(e: SqlServerError) -> PlanError {
1000 PlanError::SqlServerConnectionErr { cause: Arc::new(e) }
1001 }
1002}
1003
1004impl From<VarError> for PlanError {
1005 fn from(e: VarError) -> Self {
1006 PlanError::VarError(e)
1007 }
1008}
1009
1010impl From<PgSourcePurificationError> for PlanError {
1011 fn from(e: PgSourcePurificationError) -> Self {
1012 PlanError::PgSourcePurification(e)
1013 }
1014}
1015
1016impl From<KafkaSourcePurificationError> for PlanError {
1017 fn from(e: KafkaSourcePurificationError) -> Self {
1018 PlanError::KafkaSourcePurification(e)
1019 }
1020}
1021
1022impl From<KafkaSinkPurificationError> for PlanError {
1023 fn from(e: KafkaSinkPurificationError) -> Self {
1024 PlanError::KafkaSinkPurification(e)
1025 }
1026}
1027
1028impl From<IcebergSinkPurificationError> for PlanError {
1029 fn from(e: IcebergSinkPurificationError) -> Self {
1030 PlanError::IcebergSinkPurification(e)
1031 }
1032}
1033
1034impl From<GluePurificationError> for PlanError {
1035 fn from(e: GluePurificationError) -> Self {
1036 PlanError::GluePurification(e)
1037 }
1038}
1039
1040impl From<CsrPurificationError> for PlanError {
1041 fn from(e: CsrPurificationError) -> Self {
1042 PlanError::CsrPurification(e)
1043 }
1044}
1045
1046impl From<LoadGeneratorSourcePurificationError> for PlanError {
1047 fn from(e: LoadGeneratorSourcePurificationError) -> Self {
1048 PlanError::LoadGeneratorSourcePurification(e)
1049 }
1050}
1051
1052impl From<MySqlSourcePurificationError> for PlanError {
1053 fn from(e: MySqlSourcePurificationError) -> Self {
1054 PlanError::MySqlSourcePurification(e)
1055 }
1056}
1057
1058impl From<SqlServerSourcePurificationError> for PlanError {
1059 fn from(e: SqlServerSourcePurificationError) -> Self {
1060 PlanError::SqlServerSourcePurificationError(e)
1061 }
1062}
1063
1064impl From<IdentError> for PlanError {
1065 fn from(e: IdentError) -> Self {
1066 PlanError::InvalidIdent(e)
1067 }
1068}
1069
1070impl From<ExternalReferenceResolutionError> for PlanError {
1071 fn from(e: ExternalReferenceResolutionError) -> Self {
1072 PlanError::SubsourceResolutionError(e)
1073 }
1074}
1075
1076struct ColumnDisplay<'a> {
1077 table: &'a Option<PartialItemName>,
1078 column: &'a ColumnName,
1079}
1080
1081impl<'a> fmt::Display for ColumnDisplay<'a> {
1082 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1083 if let Some(table) = &self.table {
1084 format!("{}.{}", table.item, self.column).quoted().fmt(f)
1085 } else {
1086 self.column.quoted().fmt(f)
1087 }
1088 }
1089}