use super::{display_relation, SQLError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NotUpdatableReason {
ConditionalInsteadRule,
Distinct,
GroupBy,
Having,
SetOperation,
With,
LimitOffset,
Aggregate,
Window,
SetReturning,
NotSingleRelation,
NoUpdatableColumns,
}
impl NotUpdatableReason {
pub const fn detail(self) -> &'static str {
match self {
Self::ConditionalInsteadRule => {
"Views with conditional DO INSTEAD rules are not automatically updatable."
}
Self::Distinct => "Views containing DISTINCT are not automatically updatable.",
Self::GroupBy => "Views containing GROUP BY are not automatically updatable.",
Self::Having => "Views containing HAVING are not automatically updatable.",
Self::SetOperation => {
"Views containing UNION, INTERSECT, or EXCEPT are not automatically updatable."
}
Self::With => "Views containing WITH are not automatically updatable.",
Self::LimitOffset => "Views containing LIMIT or OFFSET are not automatically updatable.",
Self::Aggregate => {
"Views that return aggregate functions are not automatically updatable."
}
Self::Window => "Views that return window functions are not automatically updatable.",
Self::SetReturning => {
"Views that return set-returning functions are not automatically updatable."
}
Self::NotSingleRelation => {
"Views that do not select from a single table or view are not automatically updatable."
}
Self::NoUpdatableColumns => {
"Views that have no updatable columns are not automatically updatable."
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnRestriction {
Computed,
SystemColumn,
}
impl ColumnRestriction {
const fn detail(self) -> &'static str {
match self {
Self::Computed => {
"View columns that are not columns of their base relation are not updatable."
}
Self::SystemColumn => "View columns that refer to system columns are not updatable.",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ViewCommand {
Insert,
Update,
Delete,
MergeInsert,
MergeUpdate,
MergeDelete,
}
pub fn view_not_updatable(
view: &str,
command: ViewCommand,
reason: NotUpdatableReason,
) -> SQLError {
let view = display_relation(view);
let (message, hint) = match command {
ViewCommand::Insert => (
format!("cannot insert into view \"{view}\""),
"To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.",
),
ViewCommand::Update => (
format!("cannot update view \"{view}\""),
"To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.",
),
ViewCommand::Delete => (
format!("cannot delete from view \"{view}\""),
"To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.",
),
ViewCommand::MergeInsert => (
format!("cannot insert into view \"{view}\""),
"To enable inserting into the view using MERGE, provide an INSTEAD OF INSERT trigger.",
),
ViewCommand::MergeUpdate => (
format!("cannot update view \"{view}\""),
"To enable updating the view using MERGE, provide an INSTEAD OF UPDATE trigger.",
),
ViewCommand::MergeDelete => (
format!("cannot delete from view \"{view}\""),
"To enable deleting from the view using MERGE, provide an INSTEAD OF DELETE trigger.",
),
};
SQLError::Diagnostic {
sqlstate: "55000".into(),
message,
detail: Some(reason.detail().into()),
hint: Some(hint.into()),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnWrite {
Insert,
Update,
Merge,
}
pub fn non_writable_column(
view: &str,
column: &str,
write: ColumnWrite,
restriction: ColumnRestriction,
) -> SQLError {
let verb = match write {
ColumnWrite::Insert => "insert into",
ColumnWrite::Update => "update",
ColumnWrite::Merge => "merge into",
};
SQLError::Diagnostic {
sqlstate: "0A000".into(),
message: format!(
"cannot {verb} column \"{column}\" of view \"{}\"",
display_relation(view)
),
detail: Some(restriction.detail().into()),
hint: None,
}
}
pub fn mixed_merge_paths(view: &str) -> SQLError {
SQLError::Diagnostic {
sqlstate: "0A000".into(),
message: format!("cannot merge into view \"{}\"", display_relation(view)),
detail: Some(
"MERGE is not supported for views with INSTEAD OF triggers for some actions but not all."
.into(),
),
hint: Some(
"To enable merging into the view, either provide a full set of INSTEAD OF triggers or drop the existing INSTEAD OF triggers."
.into(),
),
}
}
pub fn merge_with_rules(relation: &str) -> SQLError {
SQLError::Diagnostic {
sqlstate: "0A000".into(),
message: format!(
"cannot execute MERGE on relation \"{}\"",
display_relation(relation)
),
detail: Some("MERGE is not supported for relations with rules.".into()),
hint: None,
}
}
pub fn merge_into_materialized_view(relation: &str) -> SQLError {
SQLError::Diagnostic {
sqlstate: "0A000".into(),
message: format!(
"cannot execute MERGE on relation \"{}\"",
display_relation(relation)
),
detail: Some("This operation is not supported for materialized views.".into()),
hint: None,
}
}