mithril_persistence/database/query/cardano_transaction/
delete_cardano_transaction.rs

1use anyhow::Context;
2use sqlite::Value;
3
4use mithril_common::entities::BlockNumber;
5use mithril_common::StdResult;
6
7use crate::database::record::CardanoTransactionRecord;
8use crate::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};
9
10/// Query to delete old [CardanoTransactionRecord] from the sqlite database
11pub struct DeleteCardanoTransactionQuery {
12    condition: WhereCondition,
13}
14
15impl Query for DeleteCardanoTransactionQuery {
16    type Entity = CardanoTransactionRecord;
17
18    fn filters(&self) -> WhereCondition {
19        self.condition.clone()
20    }
21
22    fn get_definition(&self, condition: &str) -> String {
23        // it is important to alias the fields with the same name as the table
24        // since the table cannot be aliased in a RETURNING statement in SQLite.
25        let projection = Self::Entity::get_projection()
26            .expand(SourceAlias::new(&[("{:cardano_tx:}", "cardano_tx")]));
27
28        format!("delete from cardano_tx where {condition} returning {projection}")
29    }
30}
31
32impl DeleteCardanoTransactionQuery {
33    pub fn below_block_number_threshold(block_number_threshold: BlockNumber) -> StdResult<Self> {
34        let threshold = Value::Integer(block_number_threshold.try_into().with_context(|| {
35            format!("Failed to convert threshold `{block_number_threshold}` to i64")
36        })?);
37
38        Ok(Self {
39            condition: WhereCondition::new("block_number < ?*", vec![threshold]),
40        })
41    }
42
43    pub fn above_block_number_threshold(block_number_threshold: BlockNumber) -> StdResult<Self> {
44        let threshold = Value::Integer(block_number_threshold.try_into().with_context(|| {
45            format!("Failed to convert threshold `{block_number_threshold}` to i64")
46        })?);
47
48        Ok(Self {
49            condition: WhereCondition::new("block_number > ?*", vec![threshold]),
50        })
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use crate::database::query::{GetCardanoTransactionQuery, InsertCardanoTransactionQuery};
57    use crate::database::test_helper::cardano_tx_db_connection;
58    use crate::sqlite::{ConnectionExtensions, SqliteConnection};
59
60    use super::*;
61
62    use mithril_common::entities::SlotNumber;
63
64    fn insert_transactions(connection: &SqliteConnection, records: Vec<CardanoTransactionRecord>) {
65        connection
66            .fetch_first(InsertCardanoTransactionQuery::insert_many(records).unwrap())
67            .unwrap();
68    }
69
70    fn test_transaction_set() -> Vec<CardanoTransactionRecord> {
71        vec![
72            CardanoTransactionRecord::new(
73                "tx-hash-0",
74                BlockNumber(10),
75                SlotNumber(50),
76                "block-hash-10",
77            ),
78            CardanoTransactionRecord::new(
79                "tx-hash-1",
80                BlockNumber(10),
81                SlotNumber(51),
82                "block-hash-10",
83            ),
84            CardanoTransactionRecord::new(
85                "tx-hash-2",
86                BlockNumber(11),
87                SlotNumber(52),
88                "block-hash-11",
89            ),
90            CardanoTransactionRecord::new(
91                "tx-hash-3",
92                BlockNumber(11),
93                SlotNumber(53),
94                "block-hash-11",
95            ),
96            CardanoTransactionRecord::new(
97                "tx-hash-4",
98                BlockNumber(12),
99                SlotNumber(54),
100                "block-hash-12",
101            ),
102            CardanoTransactionRecord::new(
103                "tx-hash-5",
104                BlockNumber(12),
105                SlotNumber(55),
106                "block-hash-12",
107            ),
108        ]
109    }
110
111    mod prune_below_threshold_tests {
112        use super::*;
113
114        #[test]
115        fn test_prune_work_even_without_transactions_in_db() {
116            let connection = cardano_tx_db_connection().unwrap();
117
118            let cursor = connection
119                .fetch(
120                    DeleteCardanoTransactionQuery::below_block_number_threshold(BlockNumber(100))
121                        .unwrap(),
122                )
123                .expect("pruning shouldn't crash without transactions stored");
124            assert_eq!(0, cursor.count());
125        }
126
127        #[test]
128        fn test_prune_all_data_if_given_block_number_is_larger_than_stored_number_of_block() {
129            let connection = cardano_tx_db_connection().unwrap();
130            insert_transactions(&connection, test_transaction_set());
131
132            let query =
133                DeleteCardanoTransactionQuery::below_block_number_threshold(BlockNumber(100_000))
134                    .unwrap();
135            let cursor = connection.fetch(query).unwrap();
136            assert_eq!(test_transaction_set().len(), cursor.count());
137
138            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
139            assert_eq!(0, cursor.count());
140        }
141
142        #[test]
143        fn test_prune_keep_all_tx_of_last_block_if_given_number_of_block_is_zero() {
144            let connection = cardano_tx_db_connection().unwrap();
145            insert_transactions(&connection, test_transaction_set());
146
147            let query = DeleteCardanoTransactionQuery::below_block_number_threshold(BlockNumber(0))
148                .unwrap();
149            let cursor = connection.fetch(query).unwrap();
150            assert_eq!(0, cursor.count());
151
152            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
153            assert_eq!(test_transaction_set().len(), cursor.count());
154        }
155
156        #[test]
157        fn test_prune_data_of_below_given_blocks() {
158            let connection = cardano_tx_db_connection().unwrap();
159            insert_transactions(&connection, test_transaction_set());
160
161            let query =
162                DeleteCardanoTransactionQuery::below_block_number_threshold(BlockNumber(12))
163                    .unwrap();
164            let cursor = connection.fetch(query).unwrap();
165            assert_eq!(4, cursor.count());
166
167            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
168            assert_eq!(2, cursor.count());
169        }
170    }
171
172    mod prune_above_threshold_tests {
173        use super::*;
174
175        #[test]
176        fn test_prune_work_even_without_transactions_in_db() {
177            let connection = cardano_tx_db_connection().unwrap();
178
179            let cursor = connection
180                .fetch(
181                    DeleteCardanoTransactionQuery::above_block_number_threshold(BlockNumber(100))
182                        .unwrap(),
183                )
184                .expect("pruning shouldn't crash without transactions stored");
185            assert_eq!(0, cursor.count());
186        }
187
188        #[test]
189        fn test_prune_all_data_if_given_block_number_is_lower_than_stored_number_of_block() {
190            let connection = cardano_tx_db_connection().unwrap();
191            insert_transactions(&connection, test_transaction_set());
192
193            let query = DeleteCardanoTransactionQuery::above_block_number_threshold(BlockNumber(0))
194                .unwrap();
195            let cursor = connection.fetch(query).unwrap();
196            assert_eq!(test_transaction_set().len(), cursor.count());
197
198            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
199            assert_eq!(0, cursor.count());
200        }
201
202        #[test]
203        fn test_prune_keep_all_tx_of_last_block_if_given_number_of_block_is_greater_than_the_highest_one(
204        ) {
205            let connection = cardano_tx_db_connection().unwrap();
206            insert_transactions(&connection, test_transaction_set());
207
208            let query =
209                DeleteCardanoTransactionQuery::above_block_number_threshold(BlockNumber(100_000))
210                    .unwrap();
211            let cursor = connection.fetch(query).unwrap();
212            assert_eq!(0, cursor.count());
213
214            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
215            assert_eq!(test_transaction_set().len(), cursor.count());
216        }
217
218        #[test]
219        fn test_prune_data_of_above_given_blocks() {
220            let connection = cardano_tx_db_connection().unwrap();
221            insert_transactions(&connection, test_transaction_set());
222
223            let query =
224                DeleteCardanoTransactionQuery::above_block_number_threshold(BlockNumber(10))
225                    .unwrap();
226            let cursor = connection.fetch(query).unwrap();
227            assert_eq!(4, cursor.count());
228
229            let cursor = connection.fetch(GetCardanoTransactionQuery::all()).unwrap();
230            assert_eq!(2, cursor.count());
231        }
232    }
233}