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 key-only access plan without predicate-based planning.
42// Build a model-level access plan for key-only intents.
43// Build a model-level access plan for key-only intents.
44pub fn access_plan_from_keys_value<K>(access: &KeyAccess<K>) -> AccessPlan<Value>
45where
46    K: FieldValue,
47{
48    match access {
49        KeyAccess::Single(key) => AccessPlan::Path(AccessPath::ByKey(key.to_value())),
50        KeyAccess::Many(keys) => {
51            let mut values: Vec<Value> = keys.iter().map(FieldValue::to_value).collect();
52            canonical::canonicalize_key_values(&mut values);
53            if let Some(first) = values.first()
54                && values.len() == 1
55            {
56                return AccessPlan::Path(AccessPath::ByKey(first.clone()));
57            }
58
59            AccessPlan::Path(AccessPath::ByKeys(values))
60        }
61    }
62}
63
64// Convert model-level access plans into entity-keyed access plans.
65pub fn access_plan_to_entity_keys<E: EntityKind>(
66    model: &crate::model::entity::EntityModel,
67    access: AccessPlan<Value>,
68) -> Result<AccessPlan<E::Id>, PlanError> {
69    let plan = match access {
70        AccessPlan::Path(path) => AccessPlan::Path(access_path_to_entity_keys::<E>(model, path)?),
71        AccessPlan::Union(children) => {
72            let mut out = Vec::with_capacity(children.len());
73            for child in children {
74                out.push(access_plan_to_entity_keys::<E>(model, child)?);
75            }
76            AccessPlan::Union(out)
77        }
78        AccessPlan::Intersection(children) => {
79            let mut out = Vec::with_capacity(children.len());
80            for child in children {
81                out.push(access_plan_to_entity_keys::<E>(model, child)?);
82            }
83            AccessPlan::Intersection(out)
84        }
85    };
86
87    Ok(plan)
88}
89
90// Convert model-level access paths into entity-keyed access paths.
91pub fn access_path_to_entity_keys<E: EntityKind>(
92    model: &crate::model::entity::EntityModel,
93    path: AccessPath<Value>,
94) -> Result<AccessPath<E::Id>, PlanError> {
95    let path = match path {
96        AccessPath::ByKey(key) => AccessPath::ByKey(coerce_entity_key::<E>(model, &key)?),
97        AccessPath::ByKeys(keys) => {
98            let mut out = Vec::with_capacity(keys.len());
99            for key in keys {
100                out.push(coerce_entity_key::<E>(model, &key)?);
101            }
102            AccessPath::ByKeys(out)
103        }
104        AccessPath::KeyRange { start, end } => AccessPath::KeyRange {
105            start: coerce_entity_key::<E>(model, &start)?,
106            end: coerce_entity_key::<E>(model, &end)?,
107        },
108        AccessPath::IndexPrefix { index, values } => AccessPath::IndexPrefix { index, values },
109        AccessPath::FullScan => AccessPath::FullScan,
110    };
111
112    Ok(path)
113}
114
115// Convert model-level key values into typed entity keys.
116pub fn coerce_entity_key<E: EntityKind>(
117    model: &crate::model::entity::EntityModel,
118    key: &Value,
119) -> Result<E::Id, PlanError> {
120    E::Id::from_value(key).ok_or_else(|| PlanError::PrimaryKeyMismatch {
121        field: model.primary_key.name.to_string(),
122        key: key.clone(),
123    })
124}