Skip to main content

pgevolve_core/ir/
sequence.rs

1//! `Sequence` — a standalone or column-owned Postgres sequence.
2
3use serde::{Deserialize, Serialize};
4
5use crate::identifier::QualifiedName;
6use crate::ir::column_type::ColumnType;
7use crate::ir::difference::Difference;
8use crate::ir::eq::{Equiv, field_difference};
9
10/// A Postgres sequence.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Sequence {
13    /// Schema-qualified sequence name.
14    pub qname: QualifiedName,
15    /// Sequence data type (always one of `SmallInt`, `Integer`, `BigInt`).
16    pub data_type: ColumnType,
17    /// Start value.
18    pub start: i64,
19    /// Increment.
20    pub increment: i64,
21    /// Min value (`None` = type's minimum).
22    pub min_value: Option<i64>,
23    /// Max value (`None` = type's maximum).
24    pub max_value: Option<i64>,
25    /// Cache size.
26    pub cache: i64,
27    /// Whether the sequence cycles.
28    pub cycle: bool,
29    /// Owning column, if any (e.g., from `SERIAL` / `IDENTITY`).
30    pub owned_by: Option<SequenceOwner>,
31    /// Optional comment.
32    pub comment: Option<String>,
33    /// Object owner. `None` = unmanaged (the differ ignores ownership).
34    /// `Some(role)` = managed: diff emits `ALTER SEQUENCE ... OWNER TO role`.
35    pub owner: Option<crate::identifier::Identifier>,
36    /// Grants on this object. Empty = no grants. Canonicalized.
37    pub grants: Vec<crate::ir::grant::Grant>,
38}
39
40impl Equiv for Sequence {
41    fn differences(&self, other: &Self) -> Vec<Difference> {
42        let Self {
43            qname: _,
44            data_type: _,
45            start: _,
46            increment: _,
47            min_value: _,
48            max_value: _,
49            cache: _,
50            cycle: _,
51            owned_by: _,
52            comment: _,
53            owner: _,
54            grants: _,
55        } = self;
56        let mut out = Vec::new();
57        out.extend(field_difference("qname", &self.qname, &other.qname));
58        out.extend(field_difference(
59            "data_type",
60            &format!("{:?}", self.data_type),
61            &format!("{:?}", other.data_type),
62        ));
63        out.extend(field_difference("start", &self.start, &other.start));
64        out.extend(field_difference(
65            "increment",
66            &self.increment,
67            &other.increment,
68        ));
69        out.extend(field_difference(
70            "min_value",
71            &format!("{:?}", self.min_value),
72            &format!("{:?}", other.min_value),
73        ));
74        out.extend(field_difference(
75            "max_value",
76            &format!("{:?}", self.max_value),
77            &format!("{:?}", other.max_value),
78        ));
79        out.extend(field_difference("cache", &self.cache, &other.cache));
80        out.extend(field_difference("cycle", &self.cycle, &other.cycle));
81        out.extend(field_difference(
82            "owned_by",
83            &format!("{:?}", self.owned_by),
84            &format!("{:?}", other.owned_by),
85        ));
86        out.extend(field_difference(
87            "comment",
88            &format!("{:?}", self.comment),
89            &format!("{:?}", other.comment),
90        ));
91        out.extend(field_difference(
92            "owner",
93            &format!("{:?}", self.owner),
94            &format!("{:?}", other.owner),
95        ));
96        out.extend(field_difference(
97            "grants",
98            &format!("{:?}", self.grants),
99            &format!("{:?}", other.grants),
100        ));
101        out
102    }
103}
104
105/// Identifies a column that owns this sequence (Postgres `OWNED BY`).
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107pub struct SequenceOwner {
108    /// Owning table.
109    pub table: QualifiedName,
110    /// Owning column name.
111    pub column: crate::identifier::Identifier,
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::identifier::{Identifier, QualifiedName};
118    use crate::ir::eq::Equiv;
119
120    fn s(name: &str) -> QualifiedName {
121        QualifiedName::new(
122            Identifier::from_unquoted("app").unwrap(),
123            Identifier::from_unquoted(name).unwrap(),
124        )
125    }
126
127    fn base() -> Sequence {
128        Sequence {
129            qname: s("seq1"),
130            data_type: ColumnType::BigInt,
131            start: 1,
132            increment: 1,
133            min_value: None,
134            max_value: None,
135            cache: 1,
136            cycle: false,
137            owned_by: None,
138            comment: None,
139            owner: None,
140            grants: Vec::new(),
141        }
142    }
143
144    #[test]
145    fn sequences_equal_when_identical() {
146        assert!(base().canonical_eq(&base()));
147    }
148
149    #[test]
150    fn sequence_diff_reports_increment_change() {
151        let mut other = base();
152        other.increment = 2;
153        let d = base().differences(&other);
154        assert!(d.iter().any(|x| x.path == "increment"));
155    }
156
157    #[test]
158    fn sequence_diff_reports_qname_change() {
159        let mut other = base();
160        other.qname = s("seq2");
161        let d = base().differences(&other);
162        assert!(d.iter().any(|x| x.path == "qname"));
163    }
164
165    #[test]
166    fn owner_change_diffs() {
167        let mut b = base();
168        b.owner = Some(Identifier::from_unquoted("new_owner").unwrap());
169        assert!(base().differences(&b).iter().any(|x| x.path == "owner"));
170    }
171
172    #[test]
173    fn grants_change_diffs() {
174        let mut b = base();
175        b.grants.push(crate::ir::grant::Grant {
176            grantee: crate::ir::grant::GrantTarget::Public,
177            privilege: crate::ir::grant::Privilege::Usage,
178            with_grant_option: false,
179            columns: None,
180        });
181        assert!(base().differences(&b).iter().any(|x| x.path == "grants"));
182    }
183}