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 aggregate<T>(
137            &self,
138            _query: crate::prelude::Query,
139            _aggregates: &[crate::prelude::AggregateFunction],
140        ) -> DbmsResult<Vec<crate::prelude::AggregatedRow>>
141        where
142            T: crate::prelude::TableSchema,
143        {
144            todo!();
145        }
146
147        fn select_raw(
148            &self,
149            _table: &str,
150            _query: crate::prelude::Query,
151        ) -> DbmsResult<Vec<Vec<(crate::prelude::ColumnDef, crate::prelude::Value)>>> {
152            unimplemented!()
153        }
154
155        fn select_join(
156            &self,
157            _table: &str,
158            _query: crate::prelude::Query,
159        ) -> DbmsResult<Vec<Vec<(crate::prelude::JoinColumnDef, crate::prelude::Value)>>> {
160            unimplemented!()
161        }
162
163        fn delete<T>(
164            &self,
165            _behaviour: crate::prelude::DeleteBehavior,
166            _filter: Option<crate::prelude::Filter>,
167        ) -> DbmsResult<u64>
168        where
169            T: crate::prelude::TableSchema,
170        {
171            unimplemented!()
172        }
173
174        fn commit(&mut self) -> DbmsResult<()> {
175            unimplemented!()
176        }
177
178        fn rollback(&mut self) -> DbmsResult<()> {
179            unimplemented!()
180        }
181
182        fn has_drift(&self) -> DbmsResult<bool> {
183            unimplemented!()
184        }
185
186        fn pending_migrations(&self) -> DbmsResult<Vec<crate::prelude::MigrationOp>> {
187            unimplemented!()
188        }
189
190        fn migrate(&mut self, _policy: crate::prelude::MigrationPolicy) -> DbmsResult<()> {
191            unimplemented!()
192        }
193    }
194}