native_db_32bit/
database.rs

1use crate::database_builder::ModelBuilder;
2use crate::db_type::Result;
3use crate::stats::{Stats, StatsTable};
4use crate::table_definition::PrimaryTableDefinition;
5use crate::transaction::internal::r_transaction::InternalRTransaction;
6use crate::transaction::internal::rw_transaction::InternalRwTransaction;
7use crate::transaction::RTransaction;
8use crate::transaction::RwTransaction;
9use crate::watch;
10use crate::watch::query::{InternalWatch, Watch};
11#[cfg(not(target_has_atomic = "64"))]
12use portable_atomic::AtomicU64;
13use redb::TableHandle;
14use std::cell::RefCell;
15use std::collections::HashMap;
16#[cfg(target_has_atomic = "64")]
17use std::sync::atomic::AtomicU64;
18use std::sync::{Arc, RwLock};
19use std::u64;
20
21/// The database instance. Allows you to create [rw_transaction](database/struct.Database.html#method.rw_transaction) and [r_transaction](database/struct.Database.html#method.r_transaction), [watch](database/struct.Database.html#method.watch) queries, and [unwatch](database/struct.Database.html#method.unwatch) etc.
22///
23/// # Example
24/// ```rust
25/// use native_db::*;
26///
27/// fn main() -> Result<(), db_type::Error> {
28///    let builder = DatabaseBuilder::new();
29///    // Define models ...
30///    let db = builder.create_in_memory()?;
31///    // Open transactions
32///    // Watch data
33///    // Create snapshots
34///    // etc...
35///    Ok(())
36/// }
37pub struct Database<'a> {
38    pub(crate) instance: redb::Database,
39    pub(crate) primary_table_definitions: HashMap<String, PrimaryTableDefinition<'a>>,
40    pub(crate) watchers: Arc<RwLock<watch::Watchers>>,
41    pub(crate) watchers_counter_id: AtomicU64,
42}
43
44impl Database<'_> {
45    /// Creates a new read-write transaction.
46    pub fn rw_transaction(&self) -> Result<RwTransaction> {
47        let rw = self.instance.begin_write()?;
48        let write_txn = RwTransaction {
49            watcher: &self.watchers,
50            batch: RefCell::new(watch::Batch::new()),
51            internal: InternalRwTransaction {
52                redb_transaction: rw,
53                primary_table_definitions: &self.primary_table_definitions,
54            },
55        };
56        Ok(write_txn)
57    }
58
59    /// Creates a new read-only transaction.
60    pub fn r_transaction(&self) -> Result<RTransaction> {
61        let txn = self.instance.begin_read()?;
62        let read_txn = RTransaction {
63            internal: InternalRTransaction {
64                redb_transaction: txn,
65                table_definitions: &self.primary_table_definitions,
66            },
67        };
68        Ok(read_txn)
69    }
70}
71
72impl Database<'_> {
73    /// Watch queries.
74    pub fn watch(&self) -> Watch {
75        Watch {
76            internal: InternalWatch {
77                watchers: &self.watchers,
78                watchers_counter_id: &self.watchers_counter_id,
79            },
80        }
81    }
82
83    /// Unwatch the given `id`.
84    /// You can get the `id` from the return value of [`watch`](Self::watch).
85    /// If the `id` is not valid anymore, this function will do nothing.
86    /// If the `id` is valid, the corresponding watcher will be removed.
87    pub fn unwatch(&self, id: u64) -> Result<()> {
88        let mut watchers = self.watchers.write().unwrap();
89        watchers.remove_sender(id);
90        Ok(())
91    }
92}
93
94impl<'a> Database<'a> {
95    pub(crate) fn seed_model(&mut self, model_builder: &'a ModelBuilder) -> Result<()> {
96        let main_table_definition =
97            redb::TableDefinition::new(model_builder.model.primary_key.unique_table_name.as_str());
98        let mut primary_table_definition: PrimaryTableDefinition =
99            (model_builder, main_table_definition).into();
100
101        let rw = self.instance.begin_write()?;
102        rw.open_table(primary_table_definition.redb.clone())?;
103
104        for secondary_key in model_builder.model.secondary_keys.iter() {
105            primary_table_definition.secondary_tables.insert(
106                secondary_key.clone(),
107                redb::TableDefinition::new(secondary_key.unique_table_name.as_str()).into(),
108            );
109            rw.open_table(
110                primary_table_definition.secondary_tables[&secondary_key]
111                    .redb
112                    .clone(),
113            )?;
114        }
115        rw.commit()?;
116
117        self.primary_table_definitions.insert(
118            model_builder.model.primary_key.unique_table_name.clone(),
119            primary_table_definition,
120        );
121
122        Ok(())
123    }
124
125    pub fn redb_stats(&self) -> Result<Stats> {
126        use redb::ReadableTable;
127        let rx = self.instance.begin_read()?;
128        let mut stats_primary_tables = vec![];
129        for primary_table in self.primary_table_definitions.values() {
130            let result_table_open = rx.open_table(primary_table.redb.clone());
131            let stats_table = match result_table_open {
132                Err(redb::TableError::TableDoesNotExist(_)) => StatsTable {
133                    name: primary_table.redb.name().to_string(),
134                    n_entries: None,
135                },
136                Ok(table_open) => {
137                    let num_raw = table_open.len()?;
138                    StatsTable {
139                        name: primary_table.redb.name().to_string(),
140                        n_entries: Some(num_raw),
141                    }
142                }
143                Err(err) => {
144                    return Err(err.into());
145                }
146            };
147            stats_primary_tables.push(stats_table);
148        }
149        let mut stats_secondary_tables = vec![];
150        for primary_table in self.primary_table_definitions.values() {
151            for secondary_table in primary_table.secondary_tables.values() {
152                let result_table_open = rx.open_table(secondary_table.redb.clone());
153                let stats_table = match result_table_open {
154                    Err(redb::TableError::TableDoesNotExist(_)) => StatsTable {
155                        name: secondary_table.redb.name().to_string(),
156                        n_entries: None,
157                    },
158                    Ok(table_open) => {
159                        let num_raw = table_open.len()?;
160                        StatsTable {
161                            name: secondary_table.redb.name().to_string(),
162                            n_entries: Some(num_raw),
163                        }
164                    }
165                    Err(err) => {
166                        return Err(err.into());
167                    }
168                };
169                stats_secondary_tables.push(stats_table);
170            }
171        }
172        stats_primary_tables.sort_by(|a, b| a.name.cmp(&b.name));
173        stats_secondary_tables.sort_by(|a, b| a.name.cmp(&b.name));
174        Ok(Stats {
175            primary_tables: stats_primary_tables,
176            secondary_tables: stats_secondary_tables,
177        })
178    }
179}