p2panda_store/address_book/
sqlite.rs1use std::collections::HashSet;
4use std::fmt::Display;
5use std::str::FromStr;
6use std::time::Duration;
7
8use p2panda_core::cbor::{decode_cbor, encode_cbor};
9use p2panda_core::{Topic, VerifyingKey};
10use serde::{Deserialize, Serialize};
11use sqlx::{query, query_as, query_scalar};
12
13use crate::address_book::{AddressBookStore, NodeInfo};
14use crate::sqlite::{SqliteError, SqliteStore};
15
16impl<N> AddressBookStore<VerifyingKey, N> for SqliteStore
17where
18 N: NodeInfo<VerifyingKey> + Serialize + for<'de> Deserialize<'de>,
19{
20 type Error = SqliteError;
21
22 async fn insert_node_info(&self, info: N) -> Result<bool, Self::Error> {
23 let is_upsert = {
24 let row = self
25 .tx(async |tx| {
26 query_as::<_, (i32,)>("SELECT COUNT(*) FROM node_infos_v1 WHERE node_id = ?")
27 .bind(info.id().to_hex())
28 .fetch_one(&mut **tx)
29 .await
30 .map_err(SqliteError::Sqlite)
31 })
32 .await?;
33
34 row.0 == 1
35 };
36
37 self.tx(async |tx| {
38 query(
39 "
40 INSERT
41 INTO
42 node_infos_v1 (
43 node_id,
44 node_info,
45 bootstrap,
46 stale
47 )
48 VALUES
49 (?, ?, ?, ?)
50 ON CONFLICT(node_id)
51 DO UPDATE
52 SET
53 node_info = EXCLUDED.node_info,
54 bootstrap = EXCLUDED.bootstrap,
55 stale = EXCLUDED.stale
56 ",
57 )
58 .bind(info.id().to_hex())
59 .bind(
60 encode_cbor(&info)
61 .map_err(|err| SqliteError::Encode("node_info".to_string(), err))?,
62 )
63 .bind(info.is_bootstrap())
64 .bind(info.is_stale())
65 .execute(&mut **tx)
66 .await
67 .map_err(SqliteError::Sqlite)
68 })
69 .await?;
70
71 Ok(!is_upsert)
72 }
73
74 async fn remove_node_info(&self, id: &VerifyingKey) -> Result<bool, Self::Error> {
75 let result = self
77 .tx(async |tx| {
78 query(
79 "
80 DELETE FROM
81 node_infos_v1
82 WHERE
83 node_id = ?
84 ",
85 )
86 .bind(id.to_hex())
87 .execute(&mut **tx)
88 .await
89 .map_err(SqliteError::Sqlite)
90 })
91 .await?;
92
93 self.tx(async |tx| {
95 query(
96 "
97 DELETE FROM
98 topics2node_infos_v1
99 WHERE
100 node_id = ?
101 ",
102 )
103 .bind(id.to_hex())
104 .execute(&mut **tx)
105 .await
106 .map_err(SqliteError::Sqlite)
107 })
108 .await?;
109
110 Ok(result.rows_affected() > 0)
111 }
112
113 async fn remove_older_than(&self, duration: Duration) -> Result<usize, Self::Error> {
114 let result = self
115 .tx(async |tx| {
116 query_as::<_, (String,)>(
117 "
118 DELETE FROM
119 node_infos_v1
120 WHERE
121 updated_at < UNIXEPOCH() - ?
122 RETURNING
123 node_id
124 ",
125 )
126 .bind(duration.as_secs() as i64)
127 .fetch_all(&mut **tx)
128 .await
129 .map_err(SqliteError::Sqlite)
130 })
131 .await?;
132
133 let node_ids: Vec<&String> = result.iter().map(|item| &item.0).collect();
134
135 self.tx(async |tx| {
137 query(&format!(
138 "
139 DELETE FROM
140 topics2node_infos_v1
141 WHERE
142 node_id IN ({})
143 ",
144 in_op_str(&node_ids)
145 ))
146 .execute(&mut **tx)
147 .await
148 .map_err(SqliteError::Sqlite)
149 })
150 .await?;
151
152 Ok(node_ids.len())
153 }
154
155 async fn node_info(&self, id: &VerifyingKey) -> Result<Option<N>, Self::Error> {
156 let result = self
157 .execute(async |pool| {
158 query_as::<_, (Vec<u8>,)>(
159 "
160 SELECT
161 node_info
162 FROM
163 node_infos_v1
164 WHERE
165 node_id = ?
166 ",
167 )
168 .bind(id.to_hex())
169 .fetch_optional(pool)
170 .await
171 .map_err(SqliteError::Sqlite)
172 })
173 .await?;
174
175 decode_node_info(result)
176 }
177
178 async fn node_topics(&self, id: &VerifyingKey) -> Result<HashSet<Topic>, Self::Error> {
179 let result = self
180 .execute(async |pool| {
181 query_as::<_, (String,)>(
182 "
183 SELECT
184 topic_id
185 FROM
186 topics2node_infos_v1
187 WHERE
188 node_id = ?
189 ",
190 )
191 .bind(id.to_hex())
192 .fetch_all(pool)
193 .await
194 .map_err(SqliteError::Sqlite)
195 })
196 .await?;
197
198 result
199 .iter()
200 .map(|item| {
201 Topic::from_str(&item.0)
202 .map_err(|err| SqliteError::Decode("topic_id".to_string(), err.into()))
203 })
204 .collect()
205 }
206
207 async fn all_node_infos(&self) -> Result<Vec<N>, Self::Error> {
208 let result = self
209 .execute(async |pool| {
210 query_as::<_, (Vec<u8>,)>(
211 "
212 SELECT
213 node_info
214 FROM
215 node_infos_v1
216 WHERE
217 stale = FALSE
218 ",
219 )
220 .fetch_all(pool)
221 .await
222 .map_err(SqliteError::Sqlite)
223 })
224 .await?;
225
226 decode_node_infos(result)
227 }
228
229 async fn all_nodes_len(&self) -> Result<usize, Self::Error> {
230 let count: i64 = self
231 .execute(async |pool| {
232 query_scalar(
233 "
234 SELECT
235 COUNT(node_id)
236 FROM
237 node_infos_v1
238 WHERE
239 stale = FALSE
240 ",
241 )
242 .fetch_one(pool)
243 .await
244 .map_err(SqliteError::Sqlite)
245 })
246 .await?;
247
248 Ok(count as usize)
249 }
250
251 async fn all_bootstrap_nodes_len(&self) -> Result<usize, Self::Error> {
252 let count: i64 = self
253 .execute(async |pool| {
254 query_scalar(
255 "
256 SELECT
257 COUNT(node_id)
258 FROM
259 node_infos_v1
260 WHERE
261 bootstrap = TRUE
262 AND stale = FALSE
263 ",
264 )
265 .fetch_one(pool)
266 .await
267 .map_err(SqliteError::Sqlite)
268 })
269 .await?;
270
271 Ok(count as usize)
272 }
273
274 async fn selected_node_infos(&self, ids: &[VerifyingKey]) -> Result<Vec<N>, Self::Error> {
275 let result = self
276 .execute(async |pool| {
277 query_as::<_, (Vec<u8>,)>(&format!(
278 "
279 SELECT
280 node_info
281 FROM
282 node_infos_v1
283 WHERE
284 node_id IN ({})
285 ",
286 in_op_str(ids)
287 ))
288 .fetch_all(pool)
289 .await
290 .map_err(SqliteError::Sqlite)
291 })
292 .await?;
293
294 decode_node_infos(result)
295 }
296
297 async fn set_topics(
298 &self,
299 id: VerifyingKey,
300 topics: HashSet<Topic>,
301 ) -> Result<(), Self::Error> {
302 self.tx(async |tx| {
305 query(
306 "
307 DELETE FROM
308 topics2node_infos_v1
309 WHERE
310 node_id = ?
311 ",
312 )
313 .bind(id.to_hex())
314 .execute(&mut **tx)
315 .await
316 .map_err(SqliteError::Sqlite)
317 })
318 .await?;
319
320 for topic in topics {
321 self.tx(async |tx| {
322 query(
323 "
324 INSERT OR IGNORE
325 INTO
326 topics2node_infos_v1 (
327 node_id,
328 topic_id
329 )
330 VALUES
331 (?, ?)
332 ",
333 )
334 .bind(id.to_hex())
335 .bind(topic.to_string())
336 .execute(&mut **tx)
337 .await
338 .map_err(SqliteError::Sqlite)
339 })
340 .await?;
341 }
342
343 Ok(())
344 }
345
346 async fn node_infos_by_topics(&self, topics: &[Topic]) -> Result<Vec<N>, Self::Error> {
347 let result = self
348 .execute(async |pool| {
349 query_as::<_, (Vec<u8>,)>(&format!(
350 "
351 SELECT
352 node_infos_v1.node_info
353 FROM
354 node_infos_v1
355 LEFT JOIN topics2node_infos_v1
356 ON node_infos_v1.node_id = topics2node_infos_v1.node_id
357 WHERE
358 topics2node_infos_v1.topic_id IN ({})
359 AND node_infos_v1.stale = FALSE
360 GROUP BY
361 node_infos_v1.node_id
362 ",
363 in_op_str(topics)
364 ))
365 .fetch_all(pool)
366 .await
367 .map_err(SqliteError::Sqlite)
368 })
369 .await?;
370
371 decode_node_infos(result)
372 }
373
374 async fn random_node(&self) -> Result<Option<N>, Self::Error> {
375 let result = self
376 .execute(async |pool| {
377 query_as::<_, (Vec<u8>,)>(
378 "
379 SELECT
380 node_info
381 FROM
382 node_infos_v1
383 WHERE
384 stale = FALSE
385 ORDER BY RANDOM()
386 LIMIT 1
387 ",
388 )
389 .fetch_optional(pool)
390 .await
391 .map_err(SqliteError::Sqlite)
392 })
393 .await?;
394
395 decode_node_info(result)
396 }
397
398 async fn random_bootstrap_node(&self) -> Result<Option<N>, Self::Error> {
399 let result = self
400 .execute(async |pool| {
401 query_as::<_, (Vec<u8>,)>(
402 "
403 SELECT
404 node_info
405 FROM
406 node_infos_v1
407 WHERE
408 bootstrap = TRUE
409 AND stale = FALSE
410 ORDER BY RANDOM()
411 LIMIT 1
412 ",
413 )
414 .fetch_optional(pool)
415 .await
416 .map_err(SqliteError::Sqlite)
417 })
418 .await?;
419
420 decode_node_info(result)
421 }
422}
423
424#[cfg(any(test, feature = "test_utils"))]
425#[doc(hidden)]
426impl SqliteStore {
427 pub async fn set_last_changed(
428 &self,
429 id: &VerifyingKey,
430 timestamp: u64,
431 ) -> Result<(), SqliteError> {
432 self.tx(async |tx| {
433 query(
434 "
435 UPDATE
436 node_infos_v1
437 SET
438 updated_at = ?
439 WHERE
440 node_id = ?
441 ",
442 )
443 .bind(timestamp as i64)
444 .bind(id.to_hex())
445 .execute(&mut **tx)
446 .await
447 .map_err(SqliteError::Sqlite)
448 })
449 .await?;
450
451 Ok(())
452 }
453}
454
455fn in_op_str<T: Display>(list: &[T]) -> String {
464 list.iter()
465 .map(|item| format!("'{item}'"))
466 .collect::<Vec<String>>()
467 .join(",")
468}
469
470fn decode_node_infos<N>(result: Vec<(Vec<u8>,)>) -> Result<Vec<N>, SqliteError>
472where
473 N: NodeInfo<VerifyingKey> + Serialize + for<'a> Deserialize<'a>,
474{
475 result
476 .iter()
477 .map(|item| {
478 decode_cbor(&item.0[..])
479 .map_err(|err| SqliteError::Decode("node_info".to_string(), err.into()))
480 })
481 .collect()
482}
483
484fn decode_node_info<N>(result: Option<(Vec<u8>,)>) -> Result<Option<N>, SqliteError>
486where
487 N: NodeInfo<VerifyingKey> + Serialize + for<'a> Deserialize<'a>,
488{
489 match result {
490 Some((bytes,)) => {
491 Ok(Some(decode_cbor(&bytes[..]).map_err(|err| {
492 SqliteError::Decode("node_info".to_string(), err.into())
493 })?))
494 }
495 None => Ok(None),
496 }
497}