uqa_sql/semantics/view_rewrite/
updatability.rs1use super::{display_relation, SQLError};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum NotUpdatableReason {
14 ConditionalInsteadRule,
15 Distinct,
16 GroupBy,
17 Having,
18 SetOperation,
19 With,
20 LimitOffset,
21 Aggregate,
22 Window,
23 SetReturning,
24 NotSingleRelation,
25 NoUpdatableColumns,
27}
28
29impl NotUpdatableReason {
30 pub const fn detail(self) -> &'static str {
32 match self {
33 Self::ConditionalInsteadRule => {
34 "Views with conditional DO INSTEAD rules are not automatically updatable."
35 }
36 Self::Distinct => "Views containing DISTINCT are not automatically updatable.",
37 Self::GroupBy => "Views containing GROUP BY are not automatically updatable.",
38 Self::Having => "Views containing HAVING are not automatically updatable.",
39 Self::SetOperation => {
40 "Views containing UNION, INTERSECT, or EXCEPT are not automatically updatable."
41 }
42 Self::With => "Views containing WITH are not automatically updatable.",
43 Self::LimitOffset => "Views containing LIMIT or OFFSET are not automatically updatable.",
44 Self::Aggregate => {
45 "Views that return aggregate functions are not automatically updatable."
46 }
47 Self::Window => "Views that return window functions are not automatically updatable.",
48 Self::SetReturning => {
49 "Views that return set-returning functions are not automatically updatable."
50 }
51 Self::NotSingleRelation => {
52 "Views that do not select from a single table or view are not automatically updatable."
53 }
54 Self::NoUpdatableColumns => {
55 "Views that have no updatable columns are not automatically updatable."
56 }
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum ColumnRestriction {
64 Computed,
66 SystemColumn,
68}
69
70impl ColumnRestriction {
71 const fn detail(self) -> &'static str {
72 match self {
73 Self::Computed => {
74 "View columns that are not columns of their base relation are not updatable."
75 }
76 Self::SystemColumn => "View columns that refer to system columns are not updatable.",
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ViewCommand {
84 Insert,
85 Update,
86 Delete,
87 MergeInsert,
88 MergeUpdate,
89 MergeDelete,
90}
91
92pub fn view_not_updatable(
94 view: &str,
95 command: ViewCommand,
96 reason: NotUpdatableReason,
97) -> SQLError {
98 let view = display_relation(view);
99 let (message, hint) = match command {
100 ViewCommand::Insert => (
101 format!("cannot insert into view \"{view}\""),
102 "To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.",
103 ),
104 ViewCommand::Update => (
105 format!("cannot update view \"{view}\""),
106 "To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.",
107 ),
108 ViewCommand::Delete => (
109 format!("cannot delete from view \"{view}\""),
110 "To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.",
111 ),
112 ViewCommand::MergeInsert => (
113 format!("cannot insert into view \"{view}\""),
114 "To enable inserting into the view using MERGE, provide an INSTEAD OF INSERT trigger.",
115 ),
116 ViewCommand::MergeUpdate => (
117 format!("cannot update view \"{view}\""),
118 "To enable updating the view using MERGE, provide an INSTEAD OF UPDATE trigger.",
119 ),
120 ViewCommand::MergeDelete => (
121 format!("cannot delete from view \"{view}\""),
122 "To enable deleting from the view using MERGE, provide an INSTEAD OF DELETE trigger.",
123 ),
124 };
125 SQLError::Diagnostic {
126 sqlstate: "55000".into(),
127 message,
128 detail: Some(reason.detail().into()),
129 hint: Some(hint.into()),
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum ColumnWrite {
136 Insert,
137 Update,
138 Merge,
139}
140
141pub fn non_writable_column(
143 view: &str,
144 column: &str,
145 write: ColumnWrite,
146 restriction: ColumnRestriction,
147) -> SQLError {
148 let verb = match write {
149 ColumnWrite::Insert => "insert into",
150 ColumnWrite::Update => "update",
151 ColumnWrite::Merge => "merge into",
152 };
153 SQLError::Diagnostic {
154 sqlstate: "0A000".into(),
155 message: format!(
156 "cannot {verb} column \"{column}\" of view \"{}\"",
157 display_relation(view)
158 ),
159 detail: Some(restriction.detail().into()),
160 hint: None,
161 }
162}
163
164pub fn mixed_merge_paths(view: &str) -> SQLError {
166 SQLError::Diagnostic {
167 sqlstate: "0A000".into(),
168 message: format!("cannot merge into view \"{}\"", display_relation(view)),
169 detail: Some(
170 "MERGE is not supported for views with INSTEAD OF triggers for some actions but not all."
171 .into(),
172 ),
173 hint: Some(
174 "To enable merging into the view, either provide a full set of INSTEAD OF triggers or drop the existing INSTEAD OF triggers."
175 .into(),
176 ),
177 }
178}
179
180pub fn merge_with_rules(relation: &str) -> SQLError {
182 SQLError::Diagnostic {
183 sqlstate: "0A000".into(),
184 message: format!(
185 "cannot execute MERGE on relation \"{}\"",
186 display_relation(relation)
187 ),
188 detail: Some("MERGE is not supported for relations with rules.".into()),
189 hint: None,
190 }
191}
192
193pub fn merge_into_materialized_view(relation: &str) -> SQLError {
195 SQLError::Diagnostic {
196 sqlstate: "0A000".into(),
197 message: format!(
198 "cannot execute MERGE on relation \"{}\"",
199 display_relation(relation)
200 ),
201 detail: Some("This operation is not supported for materialized views.".into()),
202 hint: None,
203 }
204}