mz_sql/plan/
notice.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10// Copyright Materialize, Inc. and contributors. All rights reserved.
11//
12// Use of this software is governed by the Business Source License
13// included in the LICENSE file.
14//
15// As of the Change Date specified in that file, in accordance with
16// the Business Source License, use of this software will be governed
17// by the Apache License, Version 2.0.
18
19use std::fmt;
20
21use mz_ore::str::{StrExt, separated};
22use mz_repr::ColumnName;
23
24use crate::catalog::ObjectType;
25
26/// Notices that can occur in the adapter layer.
27///
28/// These are diagnostic warnings or informational messages that are not
29/// severe enough to warrant failing a query entirely.
30#[derive(Clone, Debug, Eq, PartialEq)]
31pub enum PlanNotice {
32    ObjectDoesNotExist {
33        name: String,
34        object_type: ObjectType,
35    },
36    ColumnAlreadyExists {
37        column_name: String,
38        object_name: String,
39    },
40    UpsertSinkKeyNotEnforced {
41        key: Vec<ColumnName>,
42        name: String,
43    },
44}
45
46impl PlanNotice {
47    /// Reports additional details about the notice, if any are available.
48    pub fn detail(&self) -> Option<String> {
49        match self {
50            PlanNotice::UpsertSinkKeyNotEnforced { key, name } => {
51                let details = format!(
52                    "Materialize did not validate that the specified upsert envelope key ({}) \
53                    was a unique key of the underlying relation {}. If this key is not unique, \
54                    the sink might produce multiple updates for the same key at the same time, \
55                    which may confuse downstream consumers.",
56                    separated(", ", key.iter().map(|c| c.as_str().quoted())),
57                    name.quoted()
58                );
59                Some(details)
60            }
61            _ => None,
62        }
63    }
64
65    /// Reports a hint for the user about how the notice could be addressed.
66    pub fn hint(&self) -> Option<String> {
67        match self {
68            PlanNotice::UpsertSinkKeyNotEnforced { .. } => {
69                Some("See: https://materialize.com/s/sink-key-selection".into())
70            }
71            _ => None,
72        }
73    }
74}
75
76impl fmt::Display for PlanNotice {
77    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78        match self {
79            PlanNotice::ObjectDoesNotExist { name, object_type } => {
80                write!(
81                    f,
82                    "{} {} does not exist, skipping",
83                    object_type,
84                    name.quoted()
85                )
86            }
87            PlanNotice::ColumnAlreadyExists {
88                column_name,
89                object_name,
90            } => {
91                write!(
92                    f,
93                    "column {} of relation {} already exists, skipping",
94                    column_name.quoted(),
95                    object_name.quoted()
96                )
97            }
98            PlanNotice::UpsertSinkKeyNotEnforced { .. } => {
99                write!(f, "upsert key not validated to be unique")
100            }
101        }
102    }
103}