seshat/database/connection.rs
1// Copyright 2019 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/// A Seshat database connection.
16/// The connection can be used to read data out of the database using a
17/// separate thread.
18use std::ops::{Deref, DerefMut};
19use std::path::PathBuf;
20
21use fs_extra::dir;
22use r2d2::PooledConnection;
23use r2d2_sqlite::SqliteConnectionManager;
24
25use crate::{
26 config::LoadConfig,
27 error::Result,
28 events::{CrawlerCheckpoint, Profile, SerializedEvent},
29 Database,
30};
31
32/// Statistical information about the database.
33#[derive(Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct DatabaseStats {
36 /// The number number of bytes the database is using on disk.
37 pub size: u64,
38 /// The number of events that the database knows about.
39 pub event_count: u64,
40 /// The number of rooms that the database knows about.
41 pub room_count: u64,
42}
43
44/// A Seshat database connection that can be used for reading.
45pub struct Connection {
46 pub(crate) inner: PooledConnection<SqliteConnectionManager>,
47 pub(crate) path: PathBuf,
48}
49
50impl Connection {
51 /// Load all the previously stored crawler checkpoints from the database.
52 /// # Arguments
53 pub fn load_checkpoints(&self) -> Result<Vec<CrawlerCheckpoint>> {
54 let mut stmt = self.prepare(
55 "SELECT room_id, token, full_crawl, direction
56 FROM crawlercheckpoints",
57 )?;
58
59 let rows = stmt.query_map([], |row| {
60 Ok(CrawlerCheckpoint {
61 room_id: row.get(0)?,
62 token: row.get(1)?,
63 full_crawl: row.get(2)?,
64 direction: row.get(3)?,
65 })
66 })?;
67
68 let mut checkpoints = Vec::new();
69
70 for row in rows {
71 let checkpoint: CrawlerCheckpoint = row?;
72 checkpoints.push(checkpoint);
73 }
74 Ok(checkpoints)
75 }
76
77 /// Is the database empty.
78 /// Returns true if the database is empty, false otherwise.
79 pub fn is_empty(&self) -> Result<bool> {
80 let event_count: i64 = Database::get_event_count(&self.inner)?;
81 let checkpoint_count: i64 =
82 self.query_row("SELECT COUNT(*) FROM crawlercheckpoints", [], |row| {
83 row.get(0)
84 })?;
85
86 Ok(event_count == 0 && checkpoint_count == 0)
87 }
88
89 /// Is a room already indexed.
90 ///
91 /// Returns true if the database contains events from a room, false
92 /// otherwise.
93 pub fn is_room_indexed(&self, room_id: &str) -> Result<bool> {
94 let event_count: i64 = Database::get_event_count_for_room(&self.inner, room_id)?;
95 let checkpoint_count: i64 = self.query_row(
96 "SELECT COUNT(*) FROM crawlercheckpoints WHERE room_id=?1",
97 [room_id],
98 |row| row.get(0),
99 )?;
100
101 Ok(event_count != 0 || checkpoint_count != 0)
102 }
103
104 /// Get statistical information of the database.
105 pub fn get_stats(&self) -> Result<DatabaseStats> {
106 let event_count = Database::get_event_count(&self.inner)? as u64;
107 let room_count = Database::get_room_count(&self.inner)? as u64;
108 let size = dir::get_size(&self.path)?;
109 Ok(DatabaseStats {
110 size,
111 event_count,
112 room_count,
113 })
114 }
115
116 /// Load events that contain an mxc URL to a file.
117 /// # Arguments
118 ///
119 /// * `load_config` - Configuration deciding which events and how many of
120 /// them should be loaded.
121 ///
122 /// # Examples
123 ///
124 /// ```noexecute
125 /// let config = LoadConfig::new("!testroom:localhost").limit(10);
126 /// let result = connection.load_file_events(&config);
127 /// ```
128 ///
129 /// Returns a list of tuples containing the serialized events and the
130 /// profile of the sender at the time when the event was sent.
131 pub fn load_file_events(
132 &self,
133 load_config: &LoadConfig,
134 ) -> Result<Vec<(SerializedEvent, Profile)>> {
135 Ok(Database::load_file_events(
136 self,
137 &load_config.room_id,
138 load_config.limit,
139 load_config.from_event.as_deref(),
140 &load_config.direction,
141 )?)
142 }
143
144 /// Get the user version stored in the database.
145 ///
146 /// This version isn't used anywhere internally and can be set by the user
147 /// to signal changes between the JSON that gets stored inside of Seshat.
148 pub fn get_user_version(&self) -> Result<i64> {
149 Database::get_user_version(self)
150 }
151
152 /// Set the user version to the given version.
153 ///
154 /// # Arguments
155 ///
156 /// * `version` - The new version that will be stored in the database.
157 pub fn set_user_version(&self, version: i64) -> Result<()> {
158 Database::set_user_version(self, version)
159 }
160}
161
162impl Deref for Connection {
163 type Target = PooledConnection<SqliteConnectionManager>;
164
165 fn deref(&self) -> &Self::Target {
166 &self.inner
167 }
168}
169
170impl DerefMut for Connection {
171 fn deref_mut(&mut self) -> &mut Self::Target {
172 &mut self.inner
173 }
174}