1use crate::reserved::FIRST_USER_FIELD_ID;
2use llkv_result::{Error, Result};
3use llkv_types::FieldId;
4use rustc_hash::FxHashMap;
5use std::sync::{Arc, RwLock};
6
7#[derive(Debug, Clone, PartialEq, Eq, Default, bitcode::Encode, bitcode::Decode)]
9pub struct FieldConstraints {
10 pub primary_key: bool,
11 pub unique: bool,
12 pub check_expr: Option<String>,
13}
14
15#[derive(Debug, Clone)]
17pub struct FieldDefinition {
18 pub(super) display_name: String,
19 pub(super) constraints: FieldConstraints,
20}
21
22impl FieldDefinition {
23 pub fn new(display_name: impl Into<String>) -> Self {
24 Self {
25 display_name: display_name.into(),
26 constraints: FieldConstraints::default(),
27 }
28 }
29
30 pub fn with_primary_key(mut self, primary_key: bool) -> Self {
31 self.constraints.primary_key = primary_key;
32 if primary_key {
33 self.constraints.unique = true;
34 }
35 self
36 }
37
38 pub fn with_unique(mut self, unique: bool) -> Self {
39 if unique {
40 self.constraints.unique = true;
41 }
42 self
43 }
44
45 pub fn with_check_expr(mut self, check_expr: Option<String>) -> Self {
46 self.constraints.check_expr = check_expr;
47 self
48 }
49
50 pub fn constraints(&self) -> &FieldConstraints {
51 &self.constraints
52 }
53}
54
55impl From<&str> for FieldDefinition {
56 fn from(value: &str) -> Self {
57 FieldDefinition::new(value)
58 }
59}
60
61impl From<String> for FieldDefinition {
62 fn from(value: String) -> Self {
63 FieldDefinition::new(value)
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct FieldInfo {
70 pub field_id: FieldId,
71 pub display_name: String,
72 pub canonical_name: String,
73 pub constraints: FieldConstraints,
74}
75
76#[derive(Debug, Clone)]
77struct FieldMetadata {
78 display_name: String,
79 canonical_name: String,
80 constraints: FieldConstraints,
81}
82
83#[derive(Debug, Clone)]
85pub struct FieldResolver {
86 inner: Arc<RwLock<FieldResolverInner>>,
87}
88
89#[derive(Debug)]
90struct FieldResolverInner {
91 field_name_to_id: FxHashMap<String, FieldId>,
92 field_id_to_meta: FxHashMap<FieldId, FieldMetadata>,
93 next_field_id: FieldId,
94}
95
96impl FieldResolver {
97 pub fn new() -> Self {
98 Self {
99 inner: Arc::new(RwLock::new(FieldResolverInner {
100 field_name_to_id: FxHashMap::default(),
101 field_id_to_meta: FxHashMap::default(),
102 next_field_id: FIRST_USER_FIELD_ID,
103 })),
104 }
105 }
106
107 pub fn register_field(&self, definition: impl Into<FieldDefinition>) -> Result<FieldId> {
108 let FieldDefinition {
109 display_name,
110 constraints,
111 } = definition.into();
112 let canonical_name = display_name.to_ascii_lowercase();
113
114 let mut inner = self.inner.write().map_err(|_| {
115 Error::Internal("Failed to acquire field resolver write lock".to_string())
116 })?;
117
118 if inner.field_name_to_id.contains_key(&canonical_name) {
119 return Err(Error::CatalogError(format!(
120 "Field '{}' already exists in table",
121 display_name
122 )));
123 }
124
125 let field_id = inner.next_field_id;
126 inner.next_field_id = inner
127 .next_field_id
128 .checked_add(1)
129 .ok_or_else(|| Error::Internal("FieldId overflow".to_string()))?;
130
131 inner
132 .field_name_to_id
133 .insert(canonical_name.clone(), field_id);
134 inner.field_id_to_meta.insert(
135 field_id,
136 FieldMetadata {
137 display_name,
138 canonical_name,
139 constraints,
140 },
141 );
142
143 Ok(field_id)
144 }
145
146 pub fn field_id(&self, name: &str) -> Option<FieldId> {
147 let canonical = name.to_ascii_lowercase();
148 let inner = self.inner.read().ok()?;
149 inner.field_name_to_id.get(&canonical).copied()
150 }
151
152 pub fn field_name(&self, id: FieldId) -> Option<String> {
153 let inner = self.inner.read().ok()?;
154 inner
155 .field_id_to_meta
156 .get(&id)
157 .map(|meta| meta.display_name.clone())
158 }
159
160 pub fn field_exists(&self, name: &str) -> bool {
161 self.field_id(name).is_some()
162 }
163
164 pub fn field_count(&self) -> usize {
165 match self.inner.read() {
166 Ok(inner) => inner.field_id_to_meta.len(),
167 Err(_) => 0,
168 }
169 }
170
171 pub fn field_names(&self) -> Vec<String> {
172 match self.inner.read() {
173 Ok(inner) => inner
174 .field_id_to_meta
175 .values()
176 .map(|meta| meta.display_name.clone())
177 .collect(),
178 Err(_) => Vec::new(),
179 }
180 }
181
182 pub fn field_constraints(&self, id: FieldId) -> Option<FieldConstraints> {
183 let inner = self.inner.read().ok()?;
184 inner
185 .field_id_to_meta
186 .get(&id)
187 .map(|meta| meta.constraints.clone())
188 }
189
190 pub fn field_constraints_by_name(&self, name: &str) -> Option<FieldConstraints> {
191 let id = self.field_id(name)?;
192 self.field_constraints(id)
193 }
194
195 pub fn set_field_unique(&self, name: &str, unique: bool) -> Result<()> {
196 let canonical = name.to_ascii_lowercase();
197 let mut inner = self.inner.write().map_err(|_| {
198 Error::Internal("Failed to acquire field resolver write lock".to_string())
199 })?;
200
201 let field_id = *inner.field_name_to_id.get(&canonical).ok_or_else(|| {
202 Error::CatalogError(format!("Field '{}' does not exist in table", name))
203 })?;
204
205 let metadata = inner.field_id_to_meta.get_mut(&field_id).ok_or_else(|| {
206 Error::CatalogError(format!("Field '{}' metadata is missing from catalog", name))
207 })?;
208
209 if unique {
210 metadata.constraints.unique = true;
211 } else if !metadata.constraints.primary_key {
212 metadata.constraints.unique = false;
213 }
214
215 Ok(())
216 }
217
218 pub fn rename_field(&self, old_name: &str, new_name: &str) -> Result<()> {
219 let old_canonical = old_name.to_ascii_lowercase();
220 let new_canonical = new_name.to_ascii_lowercase();
221
222 let mut inner = self.inner.write().map_err(|_| {
223 Error::Internal("Failed to acquire field resolver write lock".to_string())
224 })?;
225
226 let field_id = *inner.field_name_to_id.get(&old_canonical).ok_or_else(|| {
227 Error::CatalogError(format!("Field '{}' does not exist in table", old_name))
228 })?;
229
230 if inner.field_name_to_id.contains_key(&new_canonical) {
231 return Err(Error::CatalogError(format!(
232 "Field '{}' already exists in table",
233 new_name
234 )));
235 }
236
237 inner.field_name_to_id.remove(&old_canonical);
238 inner
239 .field_name_to_id
240 .insert(new_canonical.clone(), field_id);
241
242 let metadata = inner.field_id_to_meta.get_mut(&field_id).ok_or_else(|| {
243 Error::CatalogError(format!(
244 "Field '{}' metadata is missing from catalog",
245 old_name
246 ))
247 })?;
248
249 metadata.display_name = new_name.to_string();
250 metadata.canonical_name = new_canonical;
251
252 Ok(())
253 }
254
255 pub fn field_info(&self, id: FieldId) -> Option<FieldInfo> {
256 let inner = self.inner.read().ok()?;
257 inner.field_id_to_meta.get(&id).map(|meta| FieldInfo {
258 field_id: id,
259 display_name: meta.display_name.clone(),
260 canonical_name: meta.canonical_name.clone(),
261 constraints: meta.constraints.clone(),
262 })
263 }
264
265 pub fn field_info_by_name(&self, name: &str) -> Option<FieldInfo> {
266 let id = self.field_id(name)?;
267 self.field_info(id)
268 }
269
270 pub fn export_state(&self) -> FieldResolverState {
271 let inner = match self.inner.read() {
272 Ok(inner) => inner,
273 Err(_) => {
274 return FieldResolverState {
275 fields: Vec::new(),
276 next_field_id: FIRST_USER_FIELD_ID,
277 };
278 }
279 };
280
281 let mut fields = Vec::new();
282 for (&field_id, meta) in &inner.field_id_to_meta {
283 fields.push(FieldState {
284 field_id,
285 display_name: meta.display_name.clone(),
286 canonical_name: meta.canonical_name.clone(),
287 constraints: meta.constraints.clone(),
288 });
289 }
290
291 FieldResolverState {
292 fields,
293 next_field_id: inner.next_field_id,
294 }
295 }
296
297 pub fn from_state(state: FieldResolverState) -> Result<Self> {
298 let mut field_name_to_id = FxHashMap::default();
299 let mut field_id_to_meta = FxHashMap::default();
300
301 for field_state in state.fields {
302 let FieldState {
303 field_id,
304 display_name,
305 canonical_name,
306 constraints,
307 } = field_state;
308
309 if field_id_to_meta.contains_key(&field_id) {
310 return Err(Error::CatalogError(format!(
311 "Duplicate field_id {} in field resolver state",
312 field_id
313 )));
314 }
315
316 if field_name_to_id.contains_key(&canonical_name) {
317 return Err(Error::CatalogError(format!(
318 "Duplicate field name '{}' in field resolver state",
319 display_name
320 )));
321 }
322
323 field_name_to_id.insert(canonical_name.clone(), field_id);
324 field_id_to_meta.insert(
325 field_id,
326 FieldMetadata {
327 display_name,
328 canonical_name,
329 constraints,
330 },
331 );
332 }
333
334 Ok(Self {
335 inner: Arc::new(RwLock::new(FieldResolverInner {
336 field_name_to_id,
337 field_id_to_meta,
338 next_field_id: state.next_field_id,
339 })),
340 })
341 }
342}
343
344impl Default for FieldResolver {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
352pub struct FieldResolverState {
353 pub fields: Vec<FieldState>,
354 pub next_field_id: FieldId,
355}
356
357#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
359pub struct FieldState {
360 pub field_id: FieldId,
361 pub display_name: String,
362 pub canonical_name: String,
363 pub constraints: FieldConstraints,
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn field_resolver_register_and_lookup() {
372 let resolver = FieldResolver::new();
373 let fid = resolver.register_field("UserName").unwrap();
374 assert_eq!(resolver.field_id("username"), Some(fid));
375 assert_eq!(resolver.field_name(fid), Some("UserName".to_string()));
376 }
377
378 #[test]
379 fn field_resolver_set_unique() {
380 let resolver = FieldResolver::new();
381 resolver
382 .register_field(FieldDefinition::new("id").with_primary_key(true))
383 .unwrap();
384 resolver
385 .register_field(FieldDefinition::new("email"))
386 .unwrap();
387 resolver.set_field_unique("email", true).unwrap();
388 assert!(resolver.field_constraints_by_name("email").unwrap().unique);
389 resolver.set_field_unique("email", false).unwrap();
390 assert!(!resolver.field_constraints_by_name("email").unwrap().unique);
391 }
392
393 #[test]
394 fn field_resolver_rename_field() {
395 let resolver = FieldResolver::new();
396 let fid = resolver.register_field("name").unwrap();
397 resolver.rename_field("name", "name_new").unwrap();
398 assert_eq!(resolver.field_id("name"), None);
399 assert_eq!(resolver.field_id("name_new"), Some(fid));
400 assert_eq!(resolver.field_name(fid), Some("name_new".to_string()));
401 }
402
403 #[test]
404 fn field_resolver_export_roundtrip() {
405 let resolver = FieldResolver::new();
406 let fid1 = resolver.register_field("field1").unwrap();
407 let fid2 = resolver.register_field("Field2").unwrap();
408
409 let state = resolver.export_state();
410 let restored = FieldResolver::from_state(state).unwrap();
411
412 assert_eq!(restored.field_id("field1"), Some(fid1));
413 assert_eq!(restored.field_id("field2"), Some(fid2));
414 }
415}