1use crate::ast::identifiers::ObjectId;
3use crate::model::column::Column;
4use serde::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum Privilege {
9 Select,
10 Insert,
11 Update,
12 Delete,
13 Truncate,
14 References,
15 Trigger,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
19pub struct PrivilegeMatrix {
20 pub grants: HashMap<ObjectId, HashSet<Privilege>>,
22}
23
24impl PrivilegeMatrix {
25 pub fn grant(&mut self, role: ObjectId, privileges: HashSet<Privilege>) {
26 self.grants.entry(role).or_default().extend(privileges);
27 }
28
29 pub fn revoke(&mut self, role: &ObjectId, privileges: &HashSet<Privilege>) {
30 if let Some(owned) = self.grants.get_mut(role) {
31 for p in privileges {
32 owned.remove(p);
33 }
34 }
35 }
36
37 pub fn has_privilege(&self, role: &ObjectId, privilege: Privilege) -> bool {
38 self.grants
39 .get(role)
40 .is_some_and(|set| set.contains(&privilege))
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub enum RelationKind {
46 Table,
47 View,
48 MaterializedView,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub enum Persistence {
53 Permanent,
54 Temporary,
55 Unlogged,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59pub struct RelationState {
60 pub id: ObjectId,
61 pub owner: ObjectId,
62 pub columns: Vec<Column>,
63 pub generation: u64,
64 pub estimated_rows: Option<u64>,
65 pub relpages: Option<u64>,
66 pub kind: RelationKind,
67 pub persistence: Persistence,
68 pub triggers: HashSet<String>,
69 pub policies: HashSet<String>,
70 pub last_analyze: Option<String>,
71 pub last_autoanalyze: Option<String>,
72 pub created_at_tx_depth: usize, pub privileges: PrivilegeMatrix,
74 pub partition_type: Option<String>, pub partition_by: Option<String>, }
77
78impl Default for RelationState {
79 fn default() -> Self {
80 Self {
81 id: ObjectId::new("public", "dummy"),
82 owner: ObjectId::new("public", "postgres"),
83 columns: Vec::new(),
84 generation: 0,
85 estimated_rows: Some(0),
86 relpages: None,
87 kind: RelationKind::Table,
88 persistence: Persistence::Permanent,
89 triggers: HashSet::new(),
90 policies: HashSet::new(),
91 last_analyze: None,
92 last_autoanalyze: None,
93 created_at_tx_depth: 0,
94 privileges: PrivilegeMatrix::default(),
95 partition_type: None,
96 partition_by: None,
97 }
98 }
99}
100
101impl RelationState {
102 pub fn new(
103 id: ObjectId,
104 owner: ObjectId,
105 generation: u64,
106 estimated_rows: Option<u64>,
107 kind: RelationKind,
108 persistence: Persistence,
109 created_at_tx_depth: usize,
110 ) -> Self {
111 Self {
112 id,
113 owner,
114 columns: Vec::new(),
115 generation,
116 estimated_rows,
117 relpages: None,
118 kind,
119 persistence,
120 triggers: HashSet::new(),
121 policies: HashSet::new(),
122 last_analyze: None,
123 last_autoanalyze: None,
124 created_at_tx_depth,
125 privileges: PrivilegeMatrix::default(),
126 partition_type: None,
127 partition_by: None,
128 }
129 }
130
131 pub fn apply_column_action(&mut self, action: &ColumnAction) {
132 match action {
133 ColumnAction::Add {
134 name,
135 data_type,
136 not_null,
137 default,
138 } => {
139 if !self.columns.iter().any(|c| c.name == *name) {
140 self.columns.push(Column {
141 name: name.clone(),
142 data_type: data_type.clone(),
143 default: default.clone(),
144 is_nullable: !not_null,
145 avg_width: None,
146 default_expr_text: None,
147 type_modifier: None,
148 });
149 }
150 }
151 ColumnAction::Drop { name } => {
152 self.columns.retain(|c| c.name != *name);
153 }
154 ColumnAction::Rename { from, to } => {
155 if let Some(col) = self.columns.iter_mut().find(|c| c.name == *from) {
156 col.name = to.clone();
157 }
158 }
159 ColumnAction::SetNotNull { name } => {
160 if let Some(col) = self.columns.iter_mut().find(|c| c.name == *name) {
161 col.is_nullable = false;
162 }
163 }
164 ColumnAction::DropNotNull { name } => {
165 if let Some(col) = self.columns.iter_mut().find(|c| c.name == *name) {
166 col.is_nullable = true;
167 }
168 }
169 ColumnAction::SetType { name, data_type } => {
170 if let Some(col) = self.columns.iter_mut().find(|c| c.name == *name) {
171 col.data_type = Some(data_type.clone());
172 }
173 }
174 ColumnAction::SetDefault { name, default } => {
175 if let Some(col) = self.columns.iter_mut().find(|c| c.name == *name) {
176 col.default = default.clone();
177 }
178 }
179 }
180 }
181
182 pub fn has_column(&self, name: &str) -> bool {
183 self.columns.iter().any(|c| c.name == name)
184 }
185
186 pub fn get_column(&self, name: &str) -> Option<&Column> {
187 self.columns.iter().find(|c| c.name == name)
188 }
189
190 pub fn is_stale(&self) -> bool {
191 self.last_analyze.is_none() && self.last_autoanalyze.is_none()
192 }
193}
194
195#[derive(Debug, Clone, PartialEq)]
196pub enum ColumnAction {
197 Add {
198 name: String,
199 data_type: Option<String>,
200 not_null: bool,
201 default: Option<crate::analysis::expr_ir::ExprIr>,
202 },
203 Drop {
204 name: String,
205 },
206 Rename {
207 from: String,
208 to: String,
209 },
210 SetNotNull {
211 name: String,
212 },
213 DropNotNull {
214 name: String,
215 },
216 SetType {
217 name: String,
218 data_type: String,
219 },
220 SetDefault {
221 name: String,
222 default: Option<crate::analysis::expr_ir::ExprIr>,
223 },
224}
225
226#[allow(clippy::large_enum_variant)]
227#[derive(Debug, Clone, PartialEq)]
228pub enum RelationOverlay {
229 Present(RelationState),
230 Dropped,
231}