mithril_persistence/database/query/cardano_transaction/
get_cardano_transaction.rs

1use std::ops::Range;
2
3use sqlite::Value;
4
5use mithril_common::entities::{BlockNumber, BlockRange, SlotNumber, TransactionHash};
6
7use crate::database::record::CardanoTransactionRecord;
8use crate::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};
9
10/// Simple queries to retrieve [CardanoTransaction] from the sqlite database.
11pub struct GetCardanoTransactionQuery {
12    condition: WhereCondition,
13}
14
15impl GetCardanoTransactionQuery {
16    pub fn all() -> Self {
17        Self {
18            condition: WhereCondition::default(),
19        }
20    }
21
22    // Useful in test and probably in the future.
23    pub fn by_transaction_hash(transaction_hash: &TransactionHash) -> Self {
24        Self {
25            condition: WhereCondition::new(
26                "transaction_hash = ?*",
27                vec![Value::String(transaction_hash.to_owned())],
28            ),
29        }
30    }
31
32    pub fn by_transaction_hashes(
33        transactions_hashes: Vec<TransactionHash>,
34        up_to_or_equal: BlockNumber,
35    ) -> Self {
36        let hashes_values = transactions_hashes.into_iter().map(Value::String).collect();
37        let condition = WhereCondition::where_in("transaction_hash", hashes_values).and_where(
38            WhereCondition::new(
39                "block_number <= ?*",
40                vec![Value::Integer(*up_to_or_equal as i64)],
41            ),
42        );
43
44        Self { condition }
45    }
46
47    pub fn by_block_ranges(block_ranges: Vec<BlockRange>) -> Self {
48        let mut condition = WhereCondition::default();
49        for block_range in block_ranges {
50            condition = condition.or_where(WhereCondition::new(
51                "(block_number >= ?* and block_number < ?*)",
52                vec![
53                    Value::Integer(*block_range.start as i64),
54                    Value::Integer(*block_range.end as i64),
55                ],
56            ))
57        }
58
59        Self { condition }
60    }
61
62    pub fn between_blocks(range: Range<BlockNumber>) -> Self {
63        let condition = WhereCondition::new(
64            "block_number >= ?*",
65            vec![Value::Integer(*range.start as i64)],
66        )
67        .and_where(WhereCondition::new(
68            "block_number < ?*",
69            vec![Value::Integer(*range.end as i64)],
70        ));
71
72        Self { condition }
73    }
74
75    pub fn with_highest_block_number_below_slot_number(slot_number: SlotNumber) -> Self {
76        Self {
77            condition: WhereCondition::new(
78                "block_number = (select max(block_number) from cardano_tx where slot_number <= ?*)",
79                vec![Value::Integer(*slot_number as i64)],
80            ),
81        }
82    }
83
84    pub fn with_highest_block_number() -> Self {
85        Self {
86            condition: WhereCondition::new(
87                "block_number = (select max(block_number) from cardano_tx)",
88                vec![],
89            ),
90        }
91    }
92}
93
94impl Query for GetCardanoTransactionQuery {
95    type Entity = CardanoTransactionRecord;
96
97    fn filters(&self) -> WhereCondition {
98        self.condition.clone()
99    }
100
101    fn get_definition(&self, condition: &str) -> String {
102        let aliases = SourceAlias::new(&[("{:cardano_tx:}", "cardano_tx")]);
103        let projection = Self::Entity::get_projection().expand(aliases);
104
105        format!("select {projection} from cardano_tx where {condition} order by block_number, transaction_hash")
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::database::query::InsertCardanoTransactionQuery;
112    use crate::database::test_helper::cardano_tx_db_connection;
113    use crate::sqlite::{ConnectionExtensions, SqliteConnection};
114
115    use super::*;
116
117    fn insert_transactions(connection: &SqliteConnection, records: Vec<CardanoTransactionRecord>) {
118        connection
119            .fetch_first(InsertCardanoTransactionQuery::insert_many(records).unwrap())
120            .unwrap();
121    }
122
123    fn transaction_record(
124        block_number: BlockNumber,
125        slot_number: SlotNumber,
126    ) -> CardanoTransactionRecord {
127        CardanoTransactionRecord::new(
128            format!("tx-hash-{}", slot_number),
129            block_number,
130            slot_number,
131            format!("block-hash-{}", block_number),
132        )
133    }
134
135    #[test]
136    fn with_highest_block_number() {
137        let connection = cardano_tx_db_connection().unwrap();
138
139        let cursor = connection
140            .fetch(GetCardanoTransactionQuery::with_highest_block_number())
141            .unwrap();
142        assert_eq!(0, cursor.count());
143
144        insert_transactions(
145            &connection,
146            vec![
147                transaction_record(BlockNumber(10), SlotNumber(50)),
148                transaction_record(BlockNumber(10), SlotNumber(51)),
149                transaction_record(BlockNumber(11), SlotNumber(54)),
150                transaction_record(BlockNumber(11), SlotNumber(55)),
151            ],
152        );
153
154        let records: Vec<CardanoTransactionRecord> = connection
155            .fetch_collect(GetCardanoTransactionQuery::with_highest_block_number())
156            .unwrap();
157        assert_eq!(
158            vec![
159                transaction_record(BlockNumber(11), SlotNumber(54)),
160                transaction_record(BlockNumber(11), SlotNumber(55)),
161            ],
162            records
163        );
164    }
165
166    #[test]
167    fn with_highest_block_number_below_slot_number() {
168        let connection = cardano_tx_db_connection().unwrap();
169
170        let cursor = connection
171            .fetch(
172                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
173                    SlotNumber(51),
174                ),
175            )
176            .unwrap();
177        assert_eq!(0, cursor.count());
178
179        insert_transactions(
180            &connection,
181            vec![transaction_record(BlockNumber(2), SlotNumber(5))],
182        );
183
184        let records: Vec<CardanoTransactionRecord> = connection
185            .fetch_collect(
186                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
187                    SlotNumber(5),
188                ),
189            )
190            .unwrap();
191        assert_eq!(
192            vec![transaction_record(BlockNumber(2), SlotNumber(5)),],
193            records
194        );
195
196        insert_transactions(
197            &connection,
198            vec![
199                transaction_record(BlockNumber(10), SlotNumber(50)),
200                transaction_record(BlockNumber(11), SlotNumber(51)),
201                transaction_record(BlockNumber(14), SlotNumber(54)),
202                transaction_record(BlockNumber(15), SlotNumber(55)),
203            ],
204        );
205
206        let records: Vec<CardanoTransactionRecord> = connection
207            .fetch_collect(
208                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
209                    SlotNumber(53),
210                ),
211            )
212            .unwrap();
213        assert_eq!(
214            vec![transaction_record(BlockNumber(11), SlotNumber(51)),],
215            records
216        );
217
218        let records: Vec<CardanoTransactionRecord> = connection
219            .fetch_collect(
220                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
221                    SlotNumber(54),
222                ),
223            )
224            .unwrap();
225        assert_eq!(
226            vec![transaction_record(BlockNumber(14), SlotNumber(54)),],
227            records
228        );
229    }
230}