Skip to main content

razeri_plugin_contract/
constraint_enforcement.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//! **Whether this engine actually checks the constraints it accepts** — the
6//! dialect's answer, and the one place the rendered DDL and the docs read it
7//! from.
8//!
9//! A `PRIMARY KEY` that is enforced and a `PRIMARY KEY` that is decoration look
10//! identical in a `CREATE TABLE`. On two of the engines Razeri renders for they
11//! are the second thing, and until this seam existed the only place that fact
12//! was written down was a book page:
13//!
14//! - **`BigQuery`** has the words for it. `PRIMARY KEY (x) NOT ENFORCED` is the
15//!   only legal spelling — a bare `PRIMARY KEY` is refused — so the engine
16//!   itself makes the declaration honest.
17//! - **`Redshift`** has no such words. It accepts `PRIMARY KEY`, `FOREIGN KEY`
18//!   and `UNIQUE`, enforces none of them, and there is no keyword to say so. A
19//!   reader of that DDL has no way to tell it apart from `PostgreSQL`'s, which
20//!   is the near-neighbour trap in its purest form.
21//!
22//! So the seam carries two things: the engine's own **keyword**, where it has
23//! one, and a **note** the renderer writes above the constraint list, where it
24//! does not. Both are the dialect's words.
25
26/// Whether the engine checks the key, unique and check constraints it accepts.
27///
28/// `#[non_exhaustive]`, on this module's whole family's rule: Razeri adds an
29/// arm when it has measured one.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31#[non_exhaustive]
32pub enum ConstraintEnforcement {
33    /// The engine rejects a row that violates a declared constraint. Eight of
34    /// Razeri's registered platforms, and the default: claiming an engine
35    /// checks nothing is the unusual assertion, and it is made deliberately,
36    /// one dialect at a time.
37    #[default]
38    Enforced,
39    /// **The engine accepts the declaration and checks nothing.** The
40    /// constraint is metadata — an optimizer hint, or documentation — and a
41    /// violating row is stored without complaint.
42    DeclaredOnly(DeclaredOnly),
43}
44
45impl ConstraintEnforcement {
46    /// The words appended to a rendered key constraint, or `""`.
47    ///
48    /// This is what [`crate::renderer::DdlRenderer::key_constraint_suffix`]
49    /// answers by default, so a dialect states enforcement once and the suffix
50    /// follows. Empty on an enforcing engine, and empty on a declaring engine
51    /// that has no keyword for it.
52    #[must_use]
53    pub const fn keyword(self) -> &'static str {
54        match self {
55            ConstraintEnforcement::DeclaredOnly(declared) => declared.keyword,
56            ConstraintEnforcement::Enforced => "",
57        }
58    }
59
60    /// The sentence the rendered DDL carries above a table's constraints, or
61    /// `None` where the engine enforces them.
62    ///
63    /// `None` on an engine whose *keyword* already says it: `NOT ENFORCED`
64    /// written on the constraint line is more precise than a comment above it,
65    /// and printing both would say the same thing twice in one statement.
66    #[must_use]
67    pub const fn ddl_note(self) -> Option<&'static str> {
68        match self {
69            ConstraintEnforcement::DeclaredOnly(declared) if declared.keyword.is_empty() => {
70                Some(declared.note)
71            }
72            _ => None,
73        }
74    }
75
76    /// Whether the engine leaves declared constraints unchecked — the fact the
77    /// platform docs and the conformance report read.
78    #[must_use]
79    pub const fn is_declared_only(self) -> bool {
80        matches!(self, ConstraintEnforcement::DeclaredOnly(_))
81    }
82}
83
84/// What a declaring-but-not-enforcing engine says about its own constraints.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub struct DeclaredOnly {
87    /// **The engine's own words, appended to every key constraint it renders**
88    /// — `BigQuery`'s `" NOT ENFORCED"`, leading space included, because the
89    /// suffix is concatenated onto the constraint text.
90    ///
91    /// `""` where the engine has no such keyword. That is not a lesser answer:
92    /// it is the honest one for `Redshift`, and it is what routes the fact into
93    /// [`ConstraintEnforcement::ddl_note`] instead.
94    pub keyword: &'static str,
95    /// **One sentence saying what this engine does with a declared
96    /// constraint**, written for whoever is reading the rendered DDL.
97    ///
98    /// Say what is and is not guaranteed, and where the guarantee has to come
99    /// from instead — a reader who learns only *"not enforced"* still does not
100    /// know whether their data is safe. It is rendered inside a SQL comment,
101    /// so keep it to prose and never spell a statement.
102    ///
103    /// Required even when `keyword` is non-empty: the docs read it in both
104    /// cases, and only the DDL note is skipped where the keyword already
105    /// speaks.
106    pub note: &'static str,
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    /// The default is the enforcing engine, which is eight of Razeri's ten and
114    /// the safe thing to assume of a dialect that has not said.
115    #[test]
116    fn the_default_is_an_enforcing_engine() {
117        assert_eq!(ConstraintEnforcement::default(), ConstraintEnforcement::Enforced);
118        assert_eq!(ConstraintEnforcement::default().keyword(), "");
119        assert_eq!(ConstraintEnforcement::default().ddl_note(), None);
120        assert!(!ConstraintEnforcement::default().is_declared_only());
121    }
122
123    /// **The two shapes of "declared only", and the split between them is the
124    /// point of the type.** An engine with a keyword writes it on the
125    /// constraint; an engine without one gets the comment, and neither gets
126    /// both.
127    #[test]
128    fn a_keyword_speaks_on_the_constraint_and_a_note_speaks_above_it() {
129        let with_keyword = ConstraintEnforcement::DeclaredOnly(DeclaredOnly {
130            keyword: " NOT ENFORCED",
131            note: "keys here are optimizer metadata.",
132        });
133        assert_eq!(with_keyword.keyword(), " NOT ENFORCED");
134        assert_eq!(
135            with_keyword.ddl_note(),
136            None,
137            "the keyword is on the constraint line already — a comment saying it again is one \
138             statement making the same claim twice"
139        );
140        assert!(with_keyword.is_declared_only());
141
142        let without_keyword = ConstraintEnforcement::DeclaredOnly(DeclaredOnly {
143            keyword: "",
144            note: "this engine accepts the declaration and checks nothing.",
145        });
146        assert_eq!(without_keyword.keyword(), "");
147        assert_eq!(
148            without_keyword.ddl_note(),
149            Some("this engine accepts the declaration and checks nothing."),
150            "with no keyword the comment is the only place a reader of this DDL can learn it"
151        );
152        assert!(without_keyword.is_declared_only());
153    }
154}