llkv_table/resolvers/
field.rs

1use crate::reserved::FIRST_USER_FIELD_ID;
2use llkv_column_map::types::FieldId;
3use llkv_result::{Error, Result};
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 field_info(&self, id: FieldId) -> Option<FieldInfo> {
219        let inner = self.inner.read().ok()?;
220        inner.field_id_to_meta.get(&id).map(|meta| FieldInfo {
221            field_id: id,
222            display_name: meta.display_name.clone(),
223            canonical_name: meta.canonical_name.clone(),
224            constraints: meta.constraints.clone(),
225        })
226    }
227
228    pub fn field_info_by_name(&self, name: &str) -> Option<FieldInfo> {
229        let id = self.field_id(name)?;
230        self.field_info(id)
231    }
232
233    pub fn export_state(&self) -> FieldResolverState {
234        let inner = match self.inner.read() {
235            Ok(inner) => inner,
236            Err(_) => {
237                return FieldResolverState {
238                    fields: Vec::new(),
239                    next_field_id: FIRST_USER_FIELD_ID,
240                };
241            }
242        };
243
244        let mut fields = Vec::new();
245        for (&field_id, meta) in &inner.field_id_to_meta {
246            fields.push(FieldState {
247                field_id,
248                display_name: meta.display_name.clone(),
249                canonical_name: meta.canonical_name.clone(),
250                constraints: meta.constraints.clone(),
251            });
252        }
253
254        FieldResolverState {
255            fields,
256            next_field_id: inner.next_field_id,
257        }
258    }
259
260    pub fn from_state(state: FieldResolverState) -> Result<Self> {
261        let mut field_name_to_id = FxHashMap::default();
262        let mut field_id_to_meta = FxHashMap::default();
263
264        for field_state in state.fields {
265            let FieldState {
266                field_id,
267                display_name,
268                canonical_name,
269                constraints,
270            } = field_state;
271
272            if field_id_to_meta.contains_key(&field_id) {
273                return Err(Error::CatalogError(format!(
274                    "Duplicate field_id {} in field resolver state",
275                    field_id
276                )));
277            }
278
279            if field_name_to_id.contains_key(&canonical_name) {
280                return Err(Error::CatalogError(format!(
281                    "Duplicate field name '{}' in field resolver state",
282                    display_name
283                )));
284            }
285
286            field_name_to_id.insert(canonical_name.clone(), field_id);
287            field_id_to_meta.insert(
288                field_id,
289                FieldMetadata {
290                    display_name,
291                    canonical_name,
292                    constraints,
293                },
294            );
295        }
296
297        Ok(Self {
298            inner: Arc::new(RwLock::new(FieldResolverInner {
299                field_name_to_id,
300                field_id_to_meta,
301                next_field_id: state.next_field_id,
302            })),
303        })
304    }
305}
306
307impl Default for FieldResolver {
308    fn default() -> Self {
309        Self::new()
310    }
311}
312
313/// Serializable field resolver state.
314#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
315pub struct FieldResolverState {
316    pub fields: Vec<FieldState>,
317    pub next_field_id: FieldId,
318}
319
320/// Serializable field state.
321#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
322pub struct FieldState {
323    pub field_id: FieldId,
324    pub display_name: String,
325    pub canonical_name: String,
326    pub constraints: FieldConstraints,
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332
333    #[test]
334    fn field_resolver_register_and_lookup() {
335        let resolver = FieldResolver::new();
336        let fid = resolver.register_field("UserName").unwrap();
337        assert_eq!(resolver.field_id("username"), Some(fid));
338        assert_eq!(resolver.field_name(fid), Some("UserName".to_string()));
339    }
340
341    #[test]
342    fn field_resolver_set_unique() {
343        let resolver = FieldResolver::new();
344        resolver
345            .register_field(FieldDefinition::new("id").with_primary_key(true))
346            .unwrap();
347        resolver
348            .register_field(FieldDefinition::new("email"))
349            .unwrap();
350        resolver.set_field_unique("email", true).unwrap();
351        assert!(resolver.field_constraints_by_name("email").unwrap().unique);
352        resolver.set_field_unique("email", false).unwrap();
353        assert!(!resolver.field_constraints_by_name("email").unwrap().unique);
354    }
355
356    #[test]
357    fn field_resolver_export_roundtrip() {
358        let resolver = FieldResolver::new();
359        let fid1 = resolver.register_field("field1").unwrap();
360        let fid2 = resolver.register_field("Field2").unwrap();
361
362        let state = resolver.export_state();
363        let restored = FieldResolver::from_state(state).unwrap();
364
365        assert_eq!(restored.field_id("field1"), Some(fid1));
366        assert_eq!(restored.field_id("field2"), Some(fid2));
367    }
368}