Skip to main content

icydb_core/db/query/intent/
key_access.rs

1use crate::{
2    db::query::plan::{AccessPath, AccessPlan, PlanError, canonical},
3    traits::{EntityKind, FieldValue},
4    value::Value,
5};
6
7///
8/// KeyAccess
9/// Primary-key-only access hints for query planning.
10///
11
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub enum KeyAccess<K> {
14    Single(K),
15    Many(Vec<K>),
16}
17
18///
19/// KeyAccessKind
20/// Identifies which key-only builder set the access path.
21///
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum KeyAccessKind {
25    Single,
26    Many,
27    Only,
28}
29
30///
31/// KeyAccessState
32/// Tracks key-only access plus its origin for intent validation.
33///
34
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct KeyAccessState<K> {
37    pub kind: KeyAccessKind,
38    pub access: KeyAccess<K>,
39}
40
41// Build a model-level access plan for key-only intents.
42pub fn access_plan_from_keys_value<K>(access: &KeyAccess<K>) -> AccessPlan<Value>
43where
44    K: FieldValue,
45{
46    match access {
47        KeyAccess::Single(key) => AccessPlan::Path(AccessPath::ByKey(key.to_value())),
48        KeyAccess::Many(keys) => {
49            let mut values: Vec<Value> = keys.iter().map(FieldValue::to_value).collect();
50            canonical::canonicalize_key_values(&mut values);
51            if let Some(first) = values.first()
52                && values.len() == 1
53            {
54                return AccessPlan::Path(AccessPath::ByKey(first.clone()));
55            }
56
57            AccessPlan::Path(AccessPath::ByKeys(values))
58        }
59    }
60}
61
62// Convert model-level access plans into entity-keyed access plans.
63pub fn access_plan_to_entity_keys<E: EntityKind>(
64    model: &crate::model::entity::EntityModel,
65    access: AccessPlan<Value>,
66) -> Result<AccessPlan<E::Key>, PlanError> {
67    let plan = match access {
68        AccessPlan::Path(path) => AccessPlan::Path(access_path_to_entity_keys::<E>(model, path)?),
69        AccessPlan::Union(children) => {
70            let mut out = Vec::with_capacity(children.len());
71            for child in children {
72                out.push(access_plan_to_entity_keys::<E>(model, child)?);
73            }
74            AccessPlan::Union(out)
75        }
76        AccessPlan::Intersection(children) => {
77            let mut out = Vec::with_capacity(children.len());
78            for child in children {
79                out.push(access_plan_to_entity_keys::<E>(model, child)?);
80            }
81            AccessPlan::Intersection(out)
82        }
83    };
84
85    Ok(plan)
86}
87
88// Convert model-level access paths into entity-keyed access paths.
89pub fn access_path_to_entity_keys<E: EntityKind>(
90    model: &crate::model::entity::EntityModel,
91    path: AccessPath<Value>,
92) -> Result<AccessPath<E::Key>, PlanError> {
93    let path = match path {
94        AccessPath::ByKey(key) => AccessPath::ByKey(coerce_entity_key::<E>(model, &key)?),
95        AccessPath::ByKeys(keys) => {
96            let mut out = Vec::with_capacity(keys.len());
97            for key in keys {
98                out.push(coerce_entity_key::<E>(model, &key)?);
99            }
100            AccessPath::ByKeys(out)
101        }
102        AccessPath::KeyRange { start, end } => AccessPath::KeyRange {
103            start: coerce_entity_key::<E>(model, &start)?,
104            end: coerce_entity_key::<E>(model, &end)?,
105        },
106        AccessPath::IndexPrefix { index, values } => AccessPath::IndexPrefix { index, values },
107        AccessPath::FullScan => AccessPath::FullScan,
108    };
109
110    Ok(path)
111}
112
113// Convert model-level key values into typed entity keys.
114pub fn coerce_entity_key<E: EntityKind>(
115    model: &crate::model::entity::EntityModel,
116    key: &Value,
117) -> Result<E::Key, PlanError> {
118    E::Key::from_value(key).ok_or_else(|| PlanError::PrimaryKeyMismatch {
119        field: model.primary_key.name.to_string(),
120        key: key.clone(),
121    })
122}