mithril_persistence/database/query/block_range_root/
delete_block_range_root.rs1use anyhow::Context;
2use sqlite::Value;
3
4use mithril_common::entities::{BlockNumber, BlockRange};
5use mithril_common::StdResult;
6
7use crate::database::record::BlockRangeRootRecord;
8use crate::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};
9
10pub struct DeleteBlockRangeRootQuery {
12 condition: WhereCondition,
13}
14
15impl Query for DeleteBlockRangeRootQuery {
16 type Entity = BlockRangeRootRecord;
17
18 fn filters(&self) -> WhereCondition {
19 self.condition.clone()
20 }
21
22 fn get_definition(&self, condition: &str) -> String {
23 let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]);
26 let projection = Self::Entity::get_projection().expand(aliases);
27
28 format!("delete from block_range_root where {condition} returning {projection}")
29 }
30}
31
32impl DeleteBlockRangeRootQuery {
33 pub fn contains_or_above_block_number_threshold(
34 block_number_threshold: BlockNumber,
35 ) -> StdResult<Self> {
36 let block_range = BlockRange::from_block_number(block_number_threshold);
37 let threshold = Value::Integer(block_range.start.try_into().with_context(|| {
38 format!("Failed to convert threshold `{block_number_threshold}` to i64")
39 })?);
40
41 Ok(Self {
42 condition: WhereCondition::new("start >= ?*", vec![threshold]),
43 })
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use mithril_common::crypto_helper::MKTreeNode;
50 use mithril_common::entities::BlockRange;
51
52 use crate::database::query::block_range_root::test_helper::insert_block_range_roots;
53 use crate::database::query::GetBlockRangeRootQuery;
54 use crate::database::test_helper::cardano_tx_db_connection;
55 use crate::sqlite::ConnectionExtensions;
56
57 use super::*;
58
59 fn block_range_root_dataset() -> Vec<BlockRangeRootRecord> {
60 [
61 (
62 BlockRange::from_block_number(BlockRange::LENGTH),
63 MKTreeNode::from_hex("AAAA").unwrap(),
64 ),
65 (
66 BlockRange::from_block_number(BlockRange::LENGTH * 2),
67 MKTreeNode::from_hex("BBBB").unwrap(),
68 ),
69 (
70 BlockRange::from_block_number(BlockRange::LENGTH * 3),
71 MKTreeNode::from_hex("CCCC").unwrap(),
72 ),
73 ]
74 .into_iter()
75 .map(BlockRangeRootRecord::from)
76 .collect()
77 }
78
79 #[test]
80 fn test_prune_work_even_without_block_range_root_in_db() {
81 let connection = cardano_tx_db_connection().unwrap();
82
83 let cursor = connection
84 .fetch(
85 DeleteBlockRangeRootQuery::contains_or_above_block_number_threshold(BlockNumber(
86 100,
87 ))
88 .unwrap(),
89 )
90 .expect("pruning shouldn't crash without block range root stored");
91 assert_eq!(0, cursor.count());
92 }
93
94 #[test]
95 fn test_prune_all_data_if_given_block_number_is_lower_than_stored_number_of_block() {
96 parameterized_test_prune_block_range(BlockNumber(0), block_range_root_dataset().len());
97 }
98
99 #[test]
100 fn test_prune_keep_all_block_range_root_if_given_number_of_block_is_greater_than_the_highest_one(
101 ) {
102 parameterized_test_prune_block_range(BlockNumber(100_000), 0);
103 }
104
105 #[test]
106 fn test_prune_block_range_when_block_number_is_block_range_start() {
107 parameterized_test_prune_block_range(BlockRange::LENGTH * 2, 2);
108 }
109
110 #[test]
111 fn test_prune_block_range_when_block_number_is_in_block_range() {
112 parameterized_test_prune_block_range(BlockRange::LENGTH * 2 + 1, 2);
113 }
114
115 #[test]
116 fn test_keep_block_range_when_block_number_is_just_before_range_start() {
117 parameterized_test_prune_block_range(BlockRange::LENGTH * 2 - 1, 3);
118 }
119
120 fn parameterized_test_prune_block_range(
121 block_threshold: BlockNumber,
122 delete_record_number: usize,
123 ) {
124 let connection = cardano_tx_db_connection().unwrap();
125 let dataset = block_range_root_dataset();
126 insert_block_range_roots(&connection, dataset.clone());
127
128 let query =
129 DeleteBlockRangeRootQuery::contains_or_above_block_number_threshold(block_threshold)
130 .unwrap();
131 let cursor = connection.fetch(query).unwrap();
132 assert_eq!(delete_record_number, cursor.count());
133
134 let cursor = connection.fetch(GetBlockRangeRootQuery::all()).unwrap();
135 assert_eq!(dataset.len() - delete_record_number, cursor.count());
136 }
137}