llkv_table/resolvers/
field.rs

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/// Constraint metadata recorded for each field.
8#[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/// Definition for registering a field with the catalog.
16#[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/// Rich field metadata exposed by the catalog.
68#[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/// Per-table field name resolver.
84#[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/// Serializable field resolver state.
351#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
352pub struct FieldResolverState {
353    pub fields: Vec<FieldState>,
354    pub next_field_id: FieldId,
355}
356
357/// Serializable field state.
358#[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}