Skip to main content

wasm_dbms_memory/
schema_registry.rs

1// Rust guideline compliant 2026-02-28
2
3use std::collections::HashMap;
4
5use wasm_dbms_api::prelude::{
6    DEFAULT_ALIGNMENT, DataSize, Encode, MSize, MemoryResult, Page, PageOffset, TableFingerprint,
7    TableSchema,
8};
9
10use crate::table_registry::{AutoincrementLedger, IndexLedger};
11use crate::{MemoryAccess, MemoryManager, MemoryProvider};
12
13/// Data regarding the table registry page.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct TableRegistryPage {
16    /// The page where the list of pages for this table is stored.
17    pub pages_list_page: Page,
18    /// The page where the free segments for this table are stored.
19    pub free_segments_page: Page,
20    /// The page where the index registry for this table is stored.
21    pub index_registry_page: Page,
22    /// The page where the autoincrement registry for this table is stored.
23    /// Only used if the table has an autoincrement column.
24    pub autoincrement_registry_page: Option<Page>,
25}
26
27/// The schema registry takes care of storing and retrieving table schemas from memory.
28#[derive(Debug, Default, Clone, PartialEq, Eq)]
29pub struct SchemaRegistry {
30    tables: HashMap<TableFingerprint, TableRegistryPage>,
31}
32
33impl SchemaRegistry {
34    /// Load the schema registry from memory.
35    pub fn load(mm: &mut MemoryManager<impl MemoryProvider>) -> MemoryResult<Self> {
36        let page = mm.schema_page();
37        let registry: Self = mm.read_at(page, 0)?;
38        Ok(registry)
39    }
40
41    /// Registers a table and allocates it registry page.
42    ///
43    /// The [`TableSchema`] type parameter is used to get the [`TableSchema::fingerprint`] of the table schema.
44    pub fn register_table<TS>(
45        &mut self,
46        mm: &mut MemoryManager<impl MemoryProvider>,
47    ) -> MemoryResult<TableRegistryPage>
48    where
49        TS: TableSchema,
50    {
51        // check if already registered
52        let fingerprint = TS::fingerprint();
53        if let Some(pages) = self.tables.get(&fingerprint) {
54            return Ok(*pages);
55        }
56
57        // allocate table registry page
58        let pages_list_page = mm.allocate_page()?;
59        let free_segments_page = mm.allocate_page()?;
60        let index_registry_page = mm.allocate_page()?;
61        // allocate autoincrement registry page if needed
62        let has_autoincrement = TS::columns().iter().any(|col| col.auto_increment);
63        let autoincrement_registry_page = if has_autoincrement {
64            Some(mm.allocate_page()?)
65        } else {
66            None
67        };
68
69        // insert into tables map
70        let pages = TableRegistryPage {
71            pages_list_page,
72            free_segments_page,
73            index_registry_page,
74            autoincrement_registry_page,
75        };
76        self.tables.insert(fingerprint, pages);
77
78        // get schema page
79        let page = mm.schema_page();
80        // write self to schema page
81        mm.write_at(page, 0, self)?;
82
83        // init index ledger for this table
84        IndexLedger::init(pages.index_registry_page, TS::indexes(), mm)?;
85        // init autoincrement ledger for this table if needed
86        if let Some(autoinc_page) = pages.autoincrement_registry_page {
87            AutoincrementLedger::init::<TS>(autoinc_page, mm)?;
88        }
89
90        Ok(pages)
91    }
92
93    /// Save the schema registry to memory.
94    pub fn save(&self, mm: &mut MemoryManager<impl MemoryProvider>) -> MemoryResult<()> {
95        let page = mm.schema_page();
96        mm.write_at(page, 0, self)
97    }
98
99    /// Returns the table registry page for a given table schema.
100    pub fn table_registry_page<TS>(&self) -> Option<TableRegistryPage>
101    where
102        TS: TableSchema,
103    {
104        self.tables.get(&TS::fingerprint()).copied()
105    }
106}
107
108impl Encode for SchemaRegistry {
109    const SIZE: DataSize = DataSize::Dynamic;
110
111    const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
112
113    fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
114        // prepare buffer; size is 8 bytes for len + (8 + (4 * 2)) bytes for each entry
115        let mut buffer = Vec::with_capacity(self.size() as usize);
116        // write 8 bytes len of map
117        buffer.extend_from_slice(&(self.tables.len() as u64).to_le_bytes());
118        // write each entry
119        for (fingerprint, page) in &self.tables {
120            buffer.extend_from_slice(&fingerprint.to_le_bytes());
121            buffer.extend_from_slice(&page.pages_list_page.to_le_bytes());
122            buffer.extend_from_slice(&page.free_segments_page.to_le_bytes());
123            buffer.extend_from_slice(&page.index_registry_page.to_le_bytes());
124            // autoincrement registry page is optional, so we write a flag and then the page if it exists
125            if let Some(autoinc_page) = page.autoincrement_registry_page {
126                buffer.push(1); // flag for presence of autoincrement registry page
127                buffer.extend_from_slice(&autoinc_page.to_le_bytes());
128            } else {
129                buffer.push(0); // flag for absence of autoincrement registry page
130            }
131        }
132        std::borrow::Cow::Owned(buffer)
133    }
134
135    fn decode(data: std::borrow::Cow<[u8]>) -> MemoryResult<Self>
136    where
137        Self: Sized,
138    {
139        let mut offset = 0;
140        // read len
141        let len = u64::from_le_bytes(
142            data[offset..offset + 8]
143                .try_into()
144                .expect("failed to read length"),
145        ) as usize;
146        offset += 8;
147        let mut tables = HashMap::with_capacity(len);
148        // read each entry
149        for _ in 0..len {
150            let fingerprint = u64::from_le_bytes(data[offset..offset + 8].try_into()?);
151            offset += 8;
152            let pages_list_page = Page::from_le_bytes(data[offset..offset + 4].try_into()?);
153            offset += 4;
154            let deleted_records_page = Page::from_le_bytes(data[offset..offset + 4].try_into()?);
155            offset += 4;
156            let index_registry_page = Page::from_le_bytes(data[offset..offset + 4].try_into()?);
157            offset += 4;
158            let has_autoincrement = data[offset] == 1;
159            offset += 1;
160            let autoincrement_registry_page = if has_autoincrement {
161                let page = Page::from_le_bytes(data[offset..offset + 4].try_into()?);
162                offset += 4;
163                Some(page)
164            } else {
165                None
166            };
167            tables.insert(
168                fingerprint,
169                TableRegistryPage {
170                    pages_list_page,
171                    free_segments_page: deleted_records_page,
172                    index_registry_page,
173                    autoincrement_registry_page,
174                },
175            );
176        }
177        Ok(Self { tables })
178    }
179
180    fn size(&self) -> MSize {
181        // - 8 bytes for `self.tables.len()`
182        // - for each entry:
183        //  - 8 bytes for the fingerprint
184        //  - 4 bytes for the pages_list_page
185        //  - 4 bytes for the free_segments_page
186        //  - 4 bytes for the index_registry_page
187        //  - 1 byte for the autoincrement registry page flag
188        //  - 4 bytes for the autoincrement registry page if it exists
189        let autoinc_pages = self
190            .tables
191            .values()
192            .filter(|page| page.autoincrement_registry_page.is_some())
193            .count() as MSize;
194
195        8 + (self.tables.len() as MSize * (4 * 3 + 8 + 1)) + (autoinc_pages * 4)
196    }
197}
198
199#[cfg(test)]
200mod tests {
201
202    use candid::CandidType;
203    use serde::{Deserialize, Serialize};
204    use wasm_dbms_api::prelude::{
205        ColumnDef, DbmsResult, IndexDef, InsertRecord, Int32, NoForeignFetcher, TableColumns,
206        TableRecord, UpdateRecord,
207    };
208
209    use super::*;
210    use crate::{HeapMemoryProvider, RecordAddress};
211
212    fn make_mm() -> MemoryManager<HeapMemoryProvider> {
213        MemoryManager::init(HeapMemoryProvider::default())
214    }
215
216    #[test]
217    fn test_should_encode_and_decode_schema_registry() {
218        let mut mm = make_mm();
219
220        // load
221        let mut registry =
222            SchemaRegistry::load(&mut mm).expect("failed to load init schema registry");
223
224        // register table
225        let registry_page = registry
226            .register_table::<User>(&mut mm)
227            .expect("failed to register table");
228
229        // get table registry page
230        let fetched_page = registry
231            .table_registry_page::<User>()
232            .expect("failed to get table registry page");
233        assert_eq!(registry_page, fetched_page);
234
235        // encode
236        let encoded = registry.encode();
237        // decode
238        let decoded = SchemaRegistry::decode(encoded).expect("failed to decode");
239        assert_eq!(registry, decoded);
240
241        // try to actually add another
242        let another_registry_page = registry
243            .register_table::<AnotherTable>(&mut mm)
244            .expect("failed to register another table");
245        let another_fetched_page = registry
246            .table_registry_page::<AnotherTable>()
247            .expect("failed to get another table registry page");
248        assert_eq!(another_registry_page, another_fetched_page);
249
250        // re-init
251        let reloaded = SchemaRegistry::load(&mut mm).expect("failed to reload schema registry");
252        assert_eq!(registry, reloaded);
253        // should have two
254        assert_eq!(reloaded.tables.len(), 2);
255        assert_eq!(
256            reloaded
257                .table_registry_page::<User>()
258                .expect("failed to get first table registry page after reload"),
259            registry_page
260        );
261        assert_eq!(
262            reloaded
263                .table_registry_page::<AnotherTable>()
264                .expect("failed to get second table registry page after reload"),
265            another_registry_page
266        );
267    }
268
269    #[test]
270    fn test_should_not_register_same_table_twice() {
271        let mut mm = make_mm();
272        let mut registry = SchemaRegistry::default();
273
274        let first_page = registry
275            .register_table::<User>(&mut mm)
276            .expect("failed to register table first time");
277        let second_page = registry
278            .register_table::<User>(&mut mm)
279            .expect("failed to register table second time");
280
281        assert_eq!(first_page, second_page);
282        assert_eq!(registry.tables.len(), 1);
283    }
284
285    #[test]
286    fn test_should_init_index_ledger() {
287        let mut mm = make_mm();
288        let mut registry = SchemaRegistry::default();
289
290        let pages = registry
291            .register_table::<User>(&mut mm)
292            .expect("failed to register table");
293
294        // check that index ledger is initialized with the correct indexes
295        let mut index_ledger = IndexLedger::load(pages.index_registry_page, &mut mm)
296            .expect("failed to load index ledger");
297
298        // insert an index for id
299        index_ledger
300            .insert(
301                &["id"],
302                Int32::from(1i32),
303                RecordAddress { page: 1, offset: 0 },
304                &mut mm,
305            )
306            .expect("failed to insert index");
307        // search the index
308        let result = index_ledger
309            .search(&["id"], &Int32::from(1i32), &mut mm)
310            .expect("failed to search index")
311            .get(0)
312            .copied()
313            .expect("no index at 0");
314        assert_eq!(result, RecordAddress { page: 1, offset: 0 });
315    }
316
317    #[derive(Clone, CandidType)]
318    struct AnotherTable;
319
320    impl Encode for AnotherTable {
321        const SIZE: DataSize = DataSize::Dynamic;
322
323        const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
324
325        fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
326            std::borrow::Cow::Owned(vec![])
327        }
328
329        fn decode(_data: std::borrow::Cow<[u8]>) -> MemoryResult<Self>
330        where
331            Self: Sized,
332        {
333            Ok(AnotherTable)
334        }
335
336        fn size(&self) -> MSize {
337            0
338        }
339    }
340
341    #[derive(Clone, CandidType, Deserialize)]
342    struct AnotherTableRecord;
343
344    impl TableRecord for AnotherTableRecord {
345        type Schema = AnotherTable;
346
347        fn from_values(_values: TableColumns) -> Self {
348            AnotherTableRecord
349        }
350
351        fn to_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
352            vec![]
353        }
354    }
355
356    #[derive(Clone, CandidType, Serialize)]
357    struct AnotherTableInsert;
358
359    impl InsertRecord for AnotherTableInsert {
360        type Record = AnotherTableRecord;
361        type Schema = AnotherTable;
362
363        fn from_values(_values: &[(ColumnDef, wasm_dbms_api::prelude::Value)]) -> DbmsResult<Self> {
364            Ok(AnotherTableInsert)
365        }
366
367        fn into_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
368            vec![]
369        }
370
371        fn into_record(self) -> Self::Schema {
372            AnotherTable
373        }
374    }
375
376    #[derive(Clone, CandidType, Serialize)]
377    struct AnotherTableUpdate;
378
379    impl UpdateRecord for AnotherTableUpdate {
380        type Record = AnotherTableRecord;
381        type Schema = AnotherTable;
382
383        fn from_values(
384            _values: &[(ColumnDef, wasm_dbms_api::prelude::Value)],
385            _where_clause: Option<wasm_dbms_api::prelude::Filter>,
386        ) -> Self {
387            AnotherTableUpdate
388        }
389
390        fn update_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
391            vec![]
392        }
393
394        fn where_clause(&self) -> Option<wasm_dbms_api::prelude::Filter> {
395            None
396        }
397    }
398
399    impl TableSchema for AnotherTable {
400        type Record = AnotherTableRecord;
401        type Insert = AnotherTableInsert;
402        type Update = AnotherTableUpdate;
403        type ForeignFetcher = NoForeignFetcher;
404
405        fn table_name() -> &'static str {
406            "another_table"
407        }
408
409        fn columns() -> &'static [wasm_dbms_api::prelude::ColumnDef] {
410            &[]
411        }
412
413        fn primary_key() -> &'static str {
414            ""
415        }
416
417        fn indexes() -> &'static [wasm_dbms_api::prelude::IndexDef] {
418            &[]
419        }
420
421        fn to_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
422            vec![]
423        }
424
425        fn sanitizer(
426            _column_name: &'static str,
427        ) -> Option<Box<dyn wasm_dbms_api::prelude::Sanitize>> {
428            None
429        }
430
431        fn validator(
432            _column_name: &'static str,
433        ) -> Option<Box<dyn wasm_dbms_api::prelude::Validate>> {
434            None
435        }
436    }
437
438    // -- User mock for tests --
439
440    #[derive(Clone, CandidType)]
441    struct User;
442
443    impl Encode for User {
444        const SIZE: DataSize = DataSize::Dynamic;
445        const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
446
447        fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
448            std::borrow::Cow::Owned(vec![])
449        }
450
451        fn decode(_data: std::borrow::Cow<[u8]>) -> MemoryResult<Self>
452        where
453            Self: Sized,
454        {
455            Ok(User)
456        }
457
458        fn size(&self) -> MSize {
459            0
460        }
461    }
462
463    #[derive(Clone, CandidType, Deserialize)]
464    struct UserRecord;
465
466    impl TableRecord for UserRecord {
467        type Schema = User;
468
469        fn from_values(_values: TableColumns) -> Self {
470            UserRecord
471        }
472
473        fn to_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
474            vec![]
475        }
476    }
477
478    #[derive(Clone, CandidType, Serialize)]
479    struct UserInsert;
480
481    impl InsertRecord for UserInsert {
482        type Record = UserRecord;
483        type Schema = User;
484
485        fn from_values(_values: &[(ColumnDef, wasm_dbms_api::prelude::Value)]) -> DbmsResult<Self> {
486            Ok(UserInsert)
487        }
488
489        fn into_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
490            vec![]
491        }
492
493        fn into_record(self) -> Self::Schema {
494            User
495        }
496    }
497
498    #[derive(Clone, CandidType, Serialize)]
499    struct UserUpdate;
500
501    impl UpdateRecord for UserUpdate {
502        type Record = UserRecord;
503        type Schema = User;
504
505        fn from_values(
506            _values: &[(ColumnDef, wasm_dbms_api::prelude::Value)],
507            _where_clause: Option<wasm_dbms_api::prelude::Filter>,
508        ) -> Self {
509            UserUpdate
510        }
511
512        fn update_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
513            vec![]
514        }
515
516        fn where_clause(&self) -> Option<wasm_dbms_api::prelude::Filter> {
517            None
518        }
519    }
520
521    impl TableSchema for User {
522        type Record = UserRecord;
523        type Insert = UserInsert;
524        type Update = UserUpdate;
525        type ForeignFetcher = NoForeignFetcher;
526
527        fn table_name() -> &'static str {
528            "users"
529        }
530
531        fn columns() -> &'static [wasm_dbms_api::prelude::ColumnDef] {
532            &[]
533        }
534
535        fn primary_key() -> &'static str {
536            "id"
537        }
538
539        fn indexes() -> &'static [wasm_dbms_api::prelude::IndexDef] {
540            &[IndexDef(&["id"])]
541        }
542
543        fn to_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
544            vec![]
545        }
546
547        fn sanitizer(
548            _column_name: &'static str,
549        ) -> Option<Box<dyn wasm_dbms_api::prelude::Sanitize>> {
550            None
551        }
552
553        fn validator(
554            _column_name: &'static str,
555        ) -> Option<Box<dyn wasm_dbms_api::prelude::Validate>> {
556            None
557        }
558    }
559
560    #[test]
561    fn test_table_registry_page_returns_none_for_unregistered_table() {
562        let registry = SchemaRegistry::default();
563        assert!(registry.table_registry_page::<User>().is_none());
564    }
565
566    #[test]
567    fn test_empty_registry_encode_decode() {
568        let registry = SchemaRegistry::default();
569        let encoded = registry.encode();
570        let decoded = SchemaRegistry::decode(encoded).expect("failed to decode empty registry");
571        assert_eq!(registry, decoded);
572        assert_eq!(decoded.tables.len(), 0);
573    }
574
575    #[test]
576    fn test_load_fresh_memory_returns_empty_registry() {
577        let mut mm = make_mm();
578        let registry = SchemaRegistry::load(&mut mm).expect("failed to load from fresh memory");
579        assert_eq!(registry.tables.len(), 0);
580    }
581
582    #[test]
583    fn test_save_and_reload() {
584        let mut mm = make_mm();
585        let mut registry = SchemaRegistry::default();
586        registry
587            .register_table::<User>(&mut mm)
588            .expect("failed to register");
589        // Modify in-memory, then explicitly save
590        registry
591            .register_table::<AnotherTable>(&mut mm)
592            .expect("failed to register another");
593        registry.save(&mut mm).expect("failed to save");
594
595        let reloaded = SchemaRegistry::load(&mut mm).expect("failed to reload");
596        assert_eq!(reloaded.tables.len(), 2);
597        assert_eq!(registry, reloaded);
598    }
599
600    #[test]
601    fn test_schema_registry_size() {
602        let mut mm = make_mm();
603        let mut registry = SchemaRegistry::default();
604        // Empty size: 8 bytes for length
605        assert_eq!(registry.size(), 8);
606        registry
607            .register_table::<User>(&mut mm)
608            .expect("failed to register");
609        // One entry without autoincrement: 8 + (8 + 4 + 4 + 4 + 1) = 29
610        // (1 byte for autoincrement flag, no page bytes since User has no autoincrement column)
611        assert_eq!(registry.size(), 29);
612    }
613
614    #[test]
615    fn test_should_allocate_autoincrement_page_when_column_has_autoincrement() {
616        let mut mm = make_mm();
617        let mut registry = SchemaRegistry::default();
618
619        let pages = registry
620            .register_table::<AutoincrementTable>(&mut mm)
621            .expect("failed to register autoincrement table");
622
623        assert!(
624            pages.autoincrement_registry_page.is_some(),
625            "autoincrement registry page should be allocated for tables with autoincrement columns"
626        );
627    }
628
629    #[test]
630    fn test_should_not_allocate_autoincrement_page_when_no_autoincrement_column() {
631        let mut mm = make_mm();
632        let mut registry = SchemaRegistry::default();
633
634        let pages = registry
635            .register_table::<User>(&mut mm)
636            .expect("failed to register user table");
637
638        assert!(
639            pages.autoincrement_registry_page.is_none(),
640            "autoincrement registry page should not be allocated for tables without autoincrement columns"
641        );
642    }
643
644    #[test]
645    fn test_schema_registry_size_with_autoincrement() {
646        let mut mm = make_mm();
647        let mut registry = SchemaRegistry::default();
648
649        registry
650            .register_table::<AutoincrementTable>(&mut mm)
651            .expect("failed to register");
652        // One entry with autoincrement: 8 + (8 + 4 + 4 + 4 + 1 + 4) = 33
653        // (1 byte for autoincrement flag + 4 bytes for the autoincrement page)
654        assert_eq!(registry.size(), 33);
655    }
656
657    #[test]
658    fn test_should_encode_and_decode_registry_with_autoincrement() {
659        let mut mm = make_mm();
660        let mut registry = SchemaRegistry::default();
661
662        registry
663            .register_table::<AutoincrementTable>(&mut mm)
664            .expect("failed to register");
665
666        let encoded = registry.encode();
667        let decoded = SchemaRegistry::decode(encoded).expect("failed to decode");
668        assert_eq!(registry, decoded);
669
670        let page = decoded
671            .table_registry_page::<AutoincrementTable>()
672            .expect("missing autoincrement table");
673        assert!(page.autoincrement_registry_page.is_some());
674    }
675
676    // -- AutoincrementTable mock for tests --
677
678    #[derive(Clone, CandidType)]
679    struct AutoincrementTable;
680
681    impl Encode for AutoincrementTable {
682        const SIZE: DataSize = DataSize::Dynamic;
683        const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
684
685        fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
686            std::borrow::Cow::Owned(vec![])
687        }
688
689        fn decode(_data: std::borrow::Cow<[u8]>) -> MemoryResult<Self>
690        where
691            Self: Sized,
692        {
693            Ok(AutoincrementTable)
694        }
695
696        fn size(&self) -> MSize {
697            0
698        }
699    }
700
701    #[derive(Clone, CandidType, Deserialize)]
702    struct AutoincrementTableRecord;
703
704    impl TableRecord for AutoincrementTableRecord {
705        type Schema = AutoincrementTable;
706
707        fn from_values(_values: TableColumns) -> Self {
708            AutoincrementTableRecord
709        }
710
711        fn to_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
712            vec![]
713        }
714    }
715
716    #[derive(Clone, CandidType, Serialize)]
717    struct AutoincrementTableInsert;
718
719    impl InsertRecord for AutoincrementTableInsert {
720        type Record = AutoincrementTableRecord;
721        type Schema = AutoincrementTable;
722
723        fn from_values(_values: &[(ColumnDef, wasm_dbms_api::prelude::Value)]) -> DbmsResult<Self> {
724            Ok(AutoincrementTableInsert)
725        }
726
727        fn into_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
728            vec![]
729        }
730
731        fn into_record(self) -> Self::Schema {
732            AutoincrementTable
733        }
734    }
735
736    #[derive(Clone, CandidType, Serialize)]
737    struct AutoincrementTableUpdate;
738
739    impl UpdateRecord for AutoincrementTableUpdate {
740        type Record = AutoincrementTableRecord;
741        type Schema = AutoincrementTable;
742
743        fn from_values(
744            _values: &[(ColumnDef, wasm_dbms_api::prelude::Value)],
745            _where_clause: Option<wasm_dbms_api::prelude::Filter>,
746        ) -> Self {
747            AutoincrementTableUpdate
748        }
749
750        fn update_values(&self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
751            vec![]
752        }
753
754        fn where_clause(&self) -> Option<wasm_dbms_api::prelude::Filter> {
755            None
756        }
757    }
758
759    impl TableSchema for AutoincrementTable {
760        type Record = AutoincrementTableRecord;
761        type Insert = AutoincrementTableInsert;
762        type Update = AutoincrementTableUpdate;
763        type ForeignFetcher = NoForeignFetcher;
764
765        fn table_name() -> &'static str {
766            "autoincrement_table"
767        }
768
769        fn columns() -> &'static [ColumnDef] {
770            use wasm_dbms_api::prelude::DataTypeKind;
771
772            &[ColumnDef {
773                name: "id",
774                data_type: DataTypeKind::Uint32,
775                auto_increment: true,
776                nullable: false,
777                primary_key: true,
778                unique: true,
779                foreign_key: None,
780            }]
781        }
782
783        fn primary_key() -> &'static str {
784            "id"
785        }
786
787        fn indexes() -> &'static [IndexDef] {
788            &[IndexDef(&["id"])]
789        }
790
791        fn to_values(self) -> Vec<(ColumnDef, wasm_dbms_api::prelude::Value)> {
792            vec![]
793        }
794
795        fn sanitizer(
796            _column_name: &'static str,
797        ) -> Option<Box<dyn wasm_dbms_api::prelude::Sanitize>> {
798            None
799        }
800
801        fn validator(
802            _column_name: &'static str,
803        ) -> Option<Box<dyn wasm_dbms_api::prelude::Validate>> {
804            None
805        }
806    }
807}