Skip to main content

razeri_plugin_contract/
view_replacement.rs

1// Copyright (C) 2026 Gabriel Shea
2// Licensed under either of the Apache License, Version 2.0 or the MIT
3// license, at your option.
4
5//! **Replacing a view that other objects read.**
6//!
7//! A deploy runs again over the warehouse it made, and every view it makes is
8//! written `CREATE OR REPLACE VIEW`. Most engines replace a view in place
9//! whatever reads it. Some refuse while another object depends on the view,
10//! and say so: `cannot replace VIEW 'core_customers_identity_current', there
11//! are database objects which depend on it`. On such an engine a second deploy
12//! stops at the first view a later view, function or procedure reads.
13//!
14//! A dialect for such an engine answers `false` to
15//! [`DdlRenderer::replaces_a_view_that_is_read`](crate::DdlRenderer::replaces_a_view_that_is_read).
16//! Razeri then hands it each view as a [`ViewReplacement`]: the statement
17//! that makes the view, and the [`ViewReader`]s that read it among the
18//! objects Razeri makes later in the same file, the last made first. The
19//! default [`DdlRenderer::render_view_replacement`](crate::DdlRenderer::render_view_replacement)
20//! drops those readers and then makes the view; the file makes each reader
21//! again when it reaches it. Nothing is dropped with `CASCADE`, which would
22//! also take objects of the reader's own, and the grants on them.
23
24use crate::WrittenObject;
25
26/// **What kind of object reads a view.**
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum ReaderKind {
30    /// A view.
31    View,
32    /// A function, scalar or table-valued.
33    Function,
34    /// A stored procedure.
35    Procedure,
36}
37
38/// **One object Razeri makes later in the same file that reads a view.**
39///
40/// Every name arrives spelled as the statements write it: quoted where the
41/// dialect quotes, and qualified.
42#[derive(Debug, Clone, PartialEq, Eq)]
43#[non_exhaustive]
44pub struct ViewReader {
45    /// What kind of object it is.
46    pub kind: ReaderKind,
47    /// Its schema, as the statements write it, where it has one.
48    pub schema: Option<String>,
49    /// Its own name, as the statements write it.
50    pub name: String,
51    /// The schema and the name, qualified as the statements write them.
52    pub relation: String,
53    /// A routine's parameter types, in order, spelled as its `CREATE` spells
54    /// them; empty for a view and for a routine that takes none.
55    pub parameter_types: Vec<String>,
56}
57
58impl ViewReader {
59    /// A reader, from its kind, its written names and its parameter types.
60    #[must_use]
61    pub const fn new(
62        kind: ReaderKind,
63        schema: Option<String>,
64        name: String,
65        relation: String,
66        parameter_types: Vec<String>,
67    ) -> Self {
68        Self { kind, schema, name, relation, parameter_types }
69    }
70
71    /// The reader as a [`WrittenObject`], for a statement that names it.
72    #[must_use]
73    pub fn object(&self) -> WrittenObject<'_> {
74        WrittenObject::new(self.schema.as_deref(), &self.name, &self.relation)
75    }
76}
77
78/// **A view to make, and what reads it.**
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[non_exhaustive]
81pub struct ViewReplacement<'a> {
82    /// The view, spelled as its statement writes it.
83    pub view: WrittenObject<'a>,
84    /// The whole statement that makes it, `CREATE OR REPLACE VIEW … AS …`,
85    /// without its terminator.
86    pub create: &'a str,
87    /// The objects Razeri makes later in the same file that read it, directly
88    /// or through another reader, the last made first.
89    pub readers: &'a [ViewReader],
90}
91
92impl<'a> ViewReplacement<'a> {
93    /// A replacement, from the view, its statement and its readers.
94    #[must_use]
95    pub const fn new(view: WrittenObject<'a>, create: &'a str, readers: &'a [ViewReader]) -> Self {
96        Self { view, create, readers }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn a_reader_names_itself_as_a_written_object() {
106        let reader = ViewReader::new(
107            ReaderKind::Procedure,
108            Some("retail_core".to_owned()),
109            "\"core_customers_load\"".to_owned(),
110            "retail_core.\"core_customers_load\"".to_owned(),
111            Vec::new(),
112        );
113        let object = reader.object();
114        assert_eq!(object.schema, Some("retail_core"));
115        assert_eq!(object.relation, "retail_core.\"core_customers_load\"");
116    }
117}