Skip to main content

wasm_dbms_api/dbms/
foreign_fetcher.rs

1use std::collections::HashMap;
2
3use crate::dbms::table::TableColumns;
4use crate::dbms::value::Value;
5use crate::error::DbmsResult;
6use crate::prelude::{ColumnDef, Database};
7
8/// Fetches related records from foreign tables referenced by foreign keys.
9///
10/// This trait provides two methods:
11///
12/// - [`ForeignFetcher::fetch`] retrieves a single foreign record by primary key.
13///   Used during integrity checks (insert/update validation) to verify that a
14///   foreign key reference points to an existing record.
15///
16/// - [`ForeignFetcher::fetch_batch`] retrieves multiple foreign records in one
17///   query using `Filter::In`. Used during eager relation loading to resolve the
18///   N+1 query problem by batching all FK lookups for a result set.
19pub trait ForeignFetcher: Default {
20    /// Fetches a single foreign record for integrity validation.
21    ///
22    /// # Arguments
23    ///
24    /// * `database` - The database from which to fetch the data.
25    /// * `table` - The name of the foreign table to query.
26    /// * `local_column` - The local column that holds the foreign key reference.
27    /// * `pk_value` - The primary key value to look up in the foreign table.
28    ///
29    /// # Returns
30    ///
31    /// A result containing the fetched table columns or an error.
32    fn fetch(
33        &self,
34        database: &impl Database,
35        table: &str,
36        local_column: &'static str,
37        pk_value: Value,
38    ) -> DbmsResult<TableColumns>;
39
40    /// Batch-fetches foreign records for eager relation loading.
41    ///
42    /// Resolves the N+1 query problem by fetching all foreign records whose
43    /// primary key is contained in `pk_values` in a single `Filter::In` query.
44    ///
45    /// # Arguments
46    ///
47    /// * `database` - The database from which to fetch the data.
48    /// * `table` - The name of the foreign table to query.
49    /// * `pk_values` - The distinct primary key values to look up.
50    ///
51    /// # Returns
52    ///
53    /// A map from each primary key value to its fetched column data.
54    fn fetch_batch(
55        &self,
56        database: &impl Database,
57        table: &str,
58        pk_values: &[Value],
59    ) -> DbmsResult<HashMap<Value, Vec<(ColumnDef, Value)>>>;
60}
61
62/// A no-op foreign fetcher that does not perform any fetching.
63#[derive(Default)]
64pub struct NoForeignFetcher;
65
66impl ForeignFetcher for NoForeignFetcher {
67    fn fetch(
68        &self,
69        _database: &impl Database,
70        _table: &str,
71        _local_column: &'static str,
72        _pk_value: Value,
73    ) -> DbmsResult<TableColumns> {
74        unimplemented!("NoForeignFetcher should have a table without foreign keys");
75    }
76
77    fn fetch_batch(
78        &self,
79        _database: &impl Database,
80        _table: &str,
81        _pk_values: &[Value],
82    ) -> DbmsResult<HashMap<Value, Vec<(ColumnDef, Value)>>> {
83        unimplemented!("NoForeignFetcher should have a table without foreign keys");
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    #[should_panic(expected = "NoForeignFetcher should have a table without foreign keys")]
93    fn test_no_foreign_fetcher() {
94        let fetcher = NoForeignFetcher;
95        let _ = fetcher.fetch(
96            &MockDatabase,
97            "some_table",
98            "some_column",
99            Value::Uint32(1.into()),
100        );
101    }
102
103    #[test]
104    #[should_panic(expected = "NoForeignFetcher should have a table without foreign keys")]
105    fn test_no_foreign_fetcher_batch() {
106        let fetcher = NoForeignFetcher;
107        let _ = fetcher.fetch_batch(&MockDatabase, "some_table", &[Value::Uint32(1.into())]);
108    }
109
110    struct MockDatabase;
111
112    impl Database for MockDatabase {
113        fn select<T>(&self, _query: crate::prelude::Query) -> DbmsResult<Vec<T::Record>>
114        where
115            T: crate::prelude::TableSchema,
116        {
117            unimplemented!()
118        }
119
120        fn insert<T>(&self, _record: T::Insert) -> DbmsResult<()>
121        where
122            T: crate::prelude::TableSchema,
123            T::Insert: crate::prelude::InsertRecord<Schema = T>,
124        {
125            unimplemented!()
126        }
127
128        fn update<T>(&self, _patch: T::Update) -> DbmsResult<u64>
129        where
130            T: crate::prelude::TableSchema,
131            T::Update: crate::prelude::UpdateRecord<Schema = T>,
132        {
133            unimplemented!()
134        }
135
136        fn select_raw(
137            &self,
138            _table: &str,
139            _query: crate::prelude::Query,
140        ) -> DbmsResult<Vec<Vec<(crate::prelude::ColumnDef, crate::prelude::Value)>>> {
141            unimplemented!()
142        }
143
144        fn delete<T>(
145            &self,
146            _behaviour: crate::prelude::DeleteBehavior,
147            _filter: Option<crate::prelude::Filter>,
148        ) -> DbmsResult<u64>
149        where
150            T: crate::prelude::TableSchema,
151        {
152            unimplemented!()
153        }
154
155        fn commit(&mut self) -> DbmsResult<()> {
156            unimplemented!()
157        }
158
159        fn rollback(&mut self) -> DbmsResult<()> {
160            unimplemented!()
161        }
162    }
163}