razeri-plugin-contract 0.17.0

The dialect contract Razeri plugins are written against — rendering traits, data types, platform identity
Documentation
// Copyright (C) 2026 Gabriel Shea
// Licensed under either of the Apache License, Version 2.0 or the MIT
// license, at your option.

//! **Replacing a view that other objects read.**
//!
//! A deploy runs again over the warehouse it made, and every view it makes is
//! written `CREATE OR REPLACE VIEW`. Most engines replace a view in place
//! whatever reads it. Some refuse while another object depends on the view,
//! and say so: `cannot replace VIEW 'core_customers_identity_current', there
//! are database objects which depend on it`. On such an engine a second deploy
//! stops at the first view a later view, function or procedure reads.
//!
//! A dialect for such an engine answers `false` to
//! [`DdlRenderer::replaces_a_view_that_is_read`](crate::DdlRenderer::replaces_a_view_that_is_read).
//! Razeri then hands it each view as a [`ViewReplacement`]: the statement
//! that makes the view, and the [`ViewReader`]s that read it among the
//! objects Razeri makes later in the same file, the last made first. The
//! default [`DdlRenderer::render_view_replacement`](crate::DdlRenderer::render_view_replacement)
//! drops those readers and then makes the view; the file makes each reader
//! again when it reaches it. Nothing is dropped with `CASCADE`, which would
//! also take objects of the reader's own, and the grants on them.

use crate::WrittenObject;

/// **What kind of object reads a view.**
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ReaderKind {
    /// A view.
    View,
    /// A function, scalar or table-valued.
    Function,
    /// A stored procedure.
    Procedure,
}

/// **One object Razeri makes later in the same file that reads a view.**
///
/// Every name arrives spelled as the statements write it: quoted where the
/// dialect quotes, and qualified.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ViewReader {
    /// What kind of object it is.
    pub kind: ReaderKind,
    /// Its schema, as the statements write it, where it has one.
    pub schema: Option<String>,
    /// Its own name, as the statements write it.
    pub name: String,
    /// The schema and the name, qualified as the statements write them.
    pub relation: String,
    /// A routine's parameter types, in order, spelled as its `CREATE` spells
    /// them; empty for a view and for a routine that takes none.
    pub parameter_types: Vec<String>,
}

impl ViewReader {
    /// A reader, from its kind, its written names and its parameter types.
    #[must_use]
    pub const fn new(
        kind: ReaderKind,
        schema: Option<String>,
        name: String,
        relation: String,
        parameter_types: Vec<String>,
    ) -> Self {
        Self { kind, schema, name, relation, parameter_types }
    }

    /// The reader as a [`WrittenObject`], for a statement that names it.
    #[must_use]
    pub fn object(&self) -> WrittenObject<'_> {
        WrittenObject::new(self.schema.as_deref(), &self.name, &self.relation)
    }
}

/// **A view to make, and what reads it.**
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct ViewReplacement<'a> {
    /// The view, spelled as its statement writes it.
    pub view: WrittenObject<'a>,
    /// The whole statement that makes it, `CREATE OR REPLACE VIEW … AS …`,
    /// without its terminator.
    pub create: &'a str,
    /// The objects Razeri makes later in the same file that read it, directly
    /// or through another reader, the last made first.
    pub readers: &'a [ViewReader],
}

impl<'a> ViewReplacement<'a> {
    /// A replacement, from the view, its statement and its readers.
    #[must_use]
    pub const fn new(view: WrittenObject<'a>, create: &'a str, readers: &'a [ViewReader]) -> Self {
        Self { view, create, readers }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn a_reader_names_itself_as_a_written_object() {
        let reader = ViewReader::new(
            ReaderKind::Procedure,
            Some("retail_core".to_owned()),
            "\"core_customers_load\"".to_owned(),
            "retail_core.\"core_customers_load\"".to_owned(),
            Vec::new(),
        );
        let object = reader.object();
        assert_eq!(object.schema, Some("retail_core"));
        assert_eq!(object.relation, "retail_core.\"core_customers_load\"");
    }
}