1use crate::error::{HematiteError, Result};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct View {
7 pub name: String,
8 pub query_sql: String,
9 pub column_names: Vec<String>,
10 pub dependencies: Vec<String>,
11}
12
13impl View {
14 pub fn validate(&self) -> Result<()> {
15 if self.name.is_empty() {
16 return Err(HematiteError::StorageError(
17 "View name cannot be empty".to_string(),
18 ));
19 }
20 if self.query_sql.trim().is_empty() {
21 return Err(HematiteError::StorageError(format!(
22 "View '{}' must store a query",
23 self.name
24 )));
25 }
26 if self.column_names.is_empty() {
27 return Err(HematiteError::StorageError(format!(
28 "View '{}' must expose at least one column",
29 self.name
30 )));
31 }
32 Ok(())
33 }
34
35 pub fn serialize(&self, buffer: &mut Vec<u8>) {
36 write_string(buffer, &self.name);
37 write_string(buffer, &self.query_sql);
38 buffer.extend_from_slice(&(self.column_names.len() as u32).to_le_bytes());
39 for column_name in &self.column_names {
40 write_string(buffer, column_name);
41 }
42 buffer.extend_from_slice(&(self.dependencies.len() as u32).to_le_bytes());
43 for dependency in &self.dependencies {
44 write_string(buffer, dependency);
45 }
46 }
47
48 pub fn deserialize(buffer: &[u8], offset: &mut usize) -> Result<Self> {
49 let name = read_string_with_len(buffer, offset, "view name")?;
50 let query_sql = read_string_with_len(buffer, offset, "view query")?;
51 let column_count = read_len(buffer, offset, "view column count")?;
52 let mut column_names = Vec::with_capacity(column_count);
53 for _ in 0..column_count {
54 column_names.push(read_string_with_len(buffer, offset, "view column name")?);
55 }
56 let dependency_count = read_len(buffer, offset, "view dependency count")?;
57 let mut dependencies = Vec::with_capacity(dependency_count);
58 for _ in 0..dependency_count {
59 dependencies.push(read_string_with_len(buffer, offset, "view dependency")?);
60 }
61 let view = Self {
62 name,
63 query_sql,
64 column_names,
65 dependencies,
66 };
67 view.validate()?;
68 Ok(view)
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum TriggerEvent {
74 Insert,
75 Update,
76 Delete,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct Trigger {
81 pub name: String,
82 pub table_name: String,
83 pub event: TriggerEvent,
84 pub body_sql: String,
85 pub old_alias: Option<String>,
86 pub new_alias: Option<String>,
87}
88
89impl Trigger {
90 pub fn validate(&self) -> Result<()> {
91 if self.name.is_empty() {
92 return Err(HematiteError::StorageError(
93 "Trigger name cannot be empty".to_string(),
94 ));
95 }
96 if self.table_name.is_empty() {
97 return Err(HematiteError::StorageError(format!(
98 "Trigger '{}' must reference a table",
99 self.name
100 )));
101 }
102 if self.body_sql.trim().is_empty() {
103 return Err(HematiteError::StorageError(format!(
104 "Trigger '{}' must store a body statement",
105 self.name
106 )));
107 }
108 Ok(())
109 }
110
111 pub fn serialize(&self, buffer: &mut Vec<u8>) {
112 write_string(buffer, &self.name);
113 write_string(buffer, &self.table_name);
114 buffer.push(trigger_event_to_byte(self.event));
115 write_string(buffer, &self.body_sql);
116 write_optional_string(buffer, self.old_alias.as_deref());
117 write_optional_string(buffer, self.new_alias.as_deref());
118 }
119
120 pub fn deserialize(buffer: &[u8], offset: &mut usize) -> Result<Self> {
121 let name = read_string_with_len(buffer, offset, "trigger name")?;
122 let table_name = read_string_with_len(buffer, offset, "trigger table")?;
123 let event = read_trigger_event(buffer, offset)?;
124 let body_sql = read_string_with_len(buffer, offset, "trigger body")?;
125 let old_alias = read_optional_string(buffer, offset, "trigger OLD alias")?;
126 let new_alias = read_optional_string(buffer, offset, "trigger NEW alias")?;
127 let trigger = Self {
128 name,
129 table_name,
130 event,
131 body_sql,
132 old_alias,
133 new_alias,
134 };
135 trigger.validate()?;
136 Ok(trigger)
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum NamedConstraintKind {
142 Check,
143 ForeignKey,
144 Unique,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq)]
148pub struct NamedConstraint {
149 pub table_name: String,
150 pub name: String,
151 pub kind: NamedConstraintKind,
152}
153
154fn write_string(buffer: &mut Vec<u8>, value: &str) {
155 buffer.extend_from_slice(&(value.len() as u32).to_le_bytes());
156 buffer.extend_from_slice(value.as_bytes());
157}
158
159fn write_optional_string(buffer: &mut Vec<u8>, value: Option<&str>) {
160 match value {
161 Some(value) => {
162 buffer.push(1);
163 write_string(buffer, value);
164 }
165 None => buffer.push(0),
166 }
167}
168
169fn read_len(buffer: &[u8], offset: &mut usize, label: &str) -> Result<usize> {
170 if *offset + 4 > buffer.len() {
171 return Err(HematiteError::CorruptedData(format!("Invalid {}", label)));
172 }
173 let len = u32::from_le_bytes(
174 buffer[*offset..*offset + 4]
175 .try_into()
176 .map_err(|_| HematiteError::CorruptedData(format!("Invalid {}", label)))?,
177 ) as usize;
178 *offset += 4;
179 Ok(len)
180}
181
182fn read_string_with_len(buffer: &[u8], offset: &mut usize, label: &str) -> Result<String> {
183 let len = read_len(buffer, offset, &format!("{} length", label))?;
184 if *offset + len > buffer.len() {
185 return Err(HematiteError::CorruptedData(format!("Invalid {}", label)));
186 }
187 let value = String::from_utf8(buffer[*offset..*offset + len].to_vec())
188 .map_err(|_| HematiteError::CorruptedData(format!("Invalid UTF-8 in {}", label)))?;
189 *offset += len;
190 Ok(value)
191}
192
193fn read_optional_string(buffer: &[u8], offset: &mut usize, label: &str) -> Result<Option<String>> {
194 if *offset >= buffer.len() {
195 return Err(HematiteError::CorruptedData(format!("Invalid {}", label)));
196 }
197 let flag = buffer[*offset];
198 *offset += 1;
199 match flag {
200 0 => Ok(None),
201 1 => Ok(Some(read_string_with_len(buffer, offset, label)?)),
202 _ => Err(HematiteError::CorruptedData(format!("Invalid {}", label))),
203 }
204}
205
206fn trigger_event_to_byte(event: TriggerEvent) -> u8 {
207 match event {
208 TriggerEvent::Insert => 0,
209 TriggerEvent::Update => 1,
210 TriggerEvent::Delete => 2,
211 }
212}
213
214fn read_trigger_event(buffer: &[u8], offset: &mut usize) -> Result<TriggerEvent> {
215 if *offset >= buffer.len() {
216 return Err(HematiteError::CorruptedData(
217 "Invalid trigger event".to_string(),
218 ));
219 }
220 let event = match buffer[*offset] {
221 0 => TriggerEvent::Insert,
222 1 => TriggerEvent::Update,
223 2 => TriggerEvent::Delete,
224 _ => {
225 return Err(HematiteError::CorruptedData(
226 "Invalid trigger event".to_string(),
227 ))
228 }
229 };
230 *offset += 1;
231 Ok(event)
232}