Skip to main content

mz_deploy/project/compiler/object_validation/
schema_constraints.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//! Schema-level constraint validation.
11//!
12//! Validates structural constraints across objects within a schema, such as
13//! ensuring storage and computation objects are not mixed in the same schema.
14
15use crate::project::ast::Statement;
16use crate::project::error::{ValidationError, ValidationErrorKind};
17use crate::project::ir::compiled::DatabaseObject;
18use std::path::PathBuf;
19
20/// Validates that a schema doesn't mix storage objects with computation objects.
21///
22/// This validation prevents accidentally recreating tables or sinks when recreating views,
23/// which would cause data loss. Storage and computation objects should be in separate schemas.
24///
25/// # Object Groups
26///
27/// - **Storage objects**: Tables, Sinks (can coexist in same schema)
28/// - **Computation objects**: Views, Materialized Views (can coexist in same schema)
29/// - These two groups CANNOT mix in the same schema
30///
31/// # Validation Rules
32///
33/// Valid combinations within a schema:
34/// - Tables only
35/// - Tables + Sinks
36/// - Sinks only
37/// - Views only
38/// - Views + Materialized Views
39/// - Materialized Views only
40///
41/// Invalid combinations:
42/// - Tables + Views
43/// - Tables + Materialized Views
44/// - Sinks + Views
45/// - Sinks + Materialized Views
46/// - Tables + Sinks + Views
47/// - (any mix of storage and computation)
48///
49/// # Arguments
50///
51/// * `schema_name` - The name of the schema being validated
52/// * `objects` - All database objects in the schema
53/// * `errors` - Vector to collect validation errors
54pub(super) fn validate_no_storage_and_computation_in_schema(
55    schema_name: &str,
56    objects: &[DatabaseObject],
57    errors: &mut Vec<ValidationError>,
58) {
59    let mut has_storage = false;
60    let mut has_computation = false;
61    let mut storage_names = Vec::new();
62    let mut computation_names = Vec::new();
63
64    for obj in objects {
65        match &obj.stmt {
66            Statement::CreateTable(_)
67            | Statement::CreateTableFromSource(_)
68            | Statement::CreateSource(_)
69            | Statement::CreateSink(_)
70            | Statement::CreateSecret(_)
71            | Statement::CreateConnection(_) => {
72                has_storage = true;
73                let ident = obj.stmt.ident();
74                storage_names.push(ident.object.to_string());
75            }
76            Statement::CreateView(_) | Statement::CreateMaterializedView(_) => {
77                has_computation = true;
78                let ident = obj.stmt.ident();
79                computation_names.push(ident.object.to_string());
80            }
81        }
82    }
83
84    if has_storage && has_computation {
85        errors.push(ValidationError::with_file(
86            ValidationErrorKind::StorageAndComputationObjectsInSameSchema {
87                schema_name: schema_name.to_string(),
88                storage_objects: storage_names,
89                computation_objects: computation_names,
90            },
91            PathBuf::from(schema_name),
92        ));
93    }
94}