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!(
106            "select {projection} from cardano_tx where {condition} order by block_number, transaction_hash"
107        )
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use crate::database::query::InsertCardanoTransactionQuery;
114    use crate::database::test_helper::cardano_tx_db_connection;
115    use crate::sqlite::{ConnectionExtensions, SqliteConnection};
116
117    use super::*;
118
119    fn insert_transactions(connection: &SqliteConnection, records: Vec<CardanoTransactionRecord>) {
120        connection
121            .fetch_first(InsertCardanoTransactionQuery::insert_many(records).unwrap())
122            .unwrap();
123    }
124
125    fn transaction_record(
126        block_number: BlockNumber,
127        slot_number: SlotNumber,
128    ) -> CardanoTransactionRecord {
129        CardanoTransactionRecord::new(
130            format!("tx-hash-{slot_number}"),
131            block_number,
132            slot_number,
133            format!("block-hash-{block_number}"),
134        )
135    }
136
137    #[test]
138    fn with_highest_block_number() {
139        let connection = cardano_tx_db_connection().unwrap();
140
141        let cursor = connection
142            .fetch(GetCardanoTransactionQuery::with_highest_block_number())
143            .unwrap();
144        assert_eq!(0, cursor.count());
145
146        insert_transactions(
147            &connection,
148            vec![
149                transaction_record(BlockNumber(10), SlotNumber(50)),
150                transaction_record(BlockNumber(10), SlotNumber(51)),
151                transaction_record(BlockNumber(11), SlotNumber(54)),
152                transaction_record(BlockNumber(11), SlotNumber(55)),
153            ],
154        );
155
156        let records: Vec<CardanoTransactionRecord> = connection
157            .fetch_collect(GetCardanoTransactionQuery::with_highest_block_number())
158            .unwrap();
159        assert_eq!(
160            vec![
161                transaction_record(BlockNumber(11), SlotNumber(54)),
162                transaction_record(BlockNumber(11), SlotNumber(55)),
163            ],
164            records
165        );
166    }
167
168    #[test]
169    fn with_highest_block_number_below_slot_number() {
170        let connection = cardano_tx_db_connection().unwrap();
171
172        let cursor = connection
173            .fetch(
174                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
175                    SlotNumber(51),
176                ),
177            )
178            .unwrap();
179        assert_eq!(0, cursor.count());
180
181        insert_transactions(
182            &connection,
183            vec![transaction_record(BlockNumber(2), SlotNumber(5))],
184        );
185
186        let records: Vec<CardanoTransactionRecord> = connection
187            .fetch_collect(
188                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
189                    SlotNumber(5),
190                ),
191            )
192            .unwrap();
193        assert_eq!(
194            vec![transaction_record(BlockNumber(2), SlotNumber(5)),],
195            records
196        );
197
198        insert_transactions(
199            &connection,
200            vec![
201                transaction_record(BlockNumber(10), SlotNumber(50)),
202                transaction_record(BlockNumber(11), SlotNumber(51)),
203                transaction_record(BlockNumber(14), SlotNumber(54)),
204                transaction_record(BlockNumber(15), SlotNumber(55)),
205            ],
206        );
207
208        let records: Vec<CardanoTransactionRecord> = connection
209            .fetch_collect(
210                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
211                    SlotNumber(53),
212                ),
213            )
214            .unwrap();
215        assert_eq!(
216            vec![transaction_record(BlockNumber(11), SlotNumber(51)),],
217            records
218        );
219
220        let records: Vec<CardanoTransactionRecord> = connection
221            .fetch_collect(
222                GetCardanoTransactionQuery::with_highest_block_number_below_slot_number(
223                    SlotNumber(54),
224                ),
225            )
226            .unwrap();
227        assert_eq!(
228            vec![transaction_record(BlockNumber(14), SlotNumber(54)),],
229            records
230        );
231    }
232}