@@ -272,12 +272,25 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
272
272
)
273
273
}
274
274
275
+ /// Get v3 of sqlite [tx_graph::ChangeSet] schema
276
+ pub fn schema_v3 ( ) -> String {
277
+ format ! (
278
+ "ALTER TABLE {} ADD COLUMN first_seen INTEGER" ,
279
+ Self :: TXS_TABLE_NAME ,
280
+ )
281
+ }
282
+
275
283
/// Initialize sqlite tables.
276
284
pub fn init_sqlite_tables ( db_tx : & rusqlite:: Transaction ) -> rusqlite:: Result < ( ) > {
277
285
migrate_schema (
278
286
db_tx,
279
287
Self :: SCHEMA_NAME ,
280
- & [ & Self :: schema_v0 ( ) , & Self :: schema_v1 ( ) , & Self :: schema_v2 ( ) ] ,
288
+ & [
289
+ & Self :: schema_v0 ( ) ,
290
+ & Self :: schema_v1 ( ) ,
291
+ & Self :: schema_v2 ( ) ,
292
+ & Self :: schema_v3 ( ) ,
293
+ ] ,
281
294
)
282
295
}
283
296
@@ -288,22 +301,26 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
288
301
let mut changeset = Self :: default ( ) ;
289
302
290
303
let mut statement = db_tx. prepare ( & format ! (
291
- "SELECT txid, raw_tx, last_seen, last_evicted FROM {}" ,
304
+ "SELECT txid, raw_tx, first_seen, last_seen, last_evicted FROM {}" ,
292
305
Self :: TXS_TABLE_NAME ,
293
306
) ) ?;
294
307
let row_iter = statement. query_map ( [ ] , |row| {
295
308
Ok ( (
296
309
row. get :: < _ , Impl < bitcoin:: Txid > > ( "txid" ) ?,
297
310
row. get :: < _ , Option < Impl < bitcoin:: Transaction > > > ( "raw_tx" ) ?,
311
+ row. get :: < _ , Option < u64 > > ( "first_seen" ) ?,
298
312
row. get :: < _ , Option < u64 > > ( "last_seen" ) ?,
299
313
row. get :: < _ , Option < u64 > > ( "last_evicted" ) ?,
300
314
) )
301
315
} ) ?;
302
316
for row in row_iter {
303
- let ( Impl ( txid) , tx, last_seen, last_evicted) = row?;
317
+ let ( Impl ( txid) , tx, first_seen , last_seen, last_evicted) = row?;
304
318
if let Some ( Impl ( tx) ) = tx {
305
319
changeset. txs . insert ( Arc :: new ( tx) ) ;
306
320
}
321
+ if let Some ( first_seen) = first_seen {
322
+ changeset. first_seen . insert ( txid, first_seen) ;
323
+ }
307
324
if let Some ( last_seen) = last_seen {
308
325
changeset. last_seen . insert ( txid, last_seen) ;
309
326
}
@@ -376,6 +393,18 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
376
393
} ) ?;
377
394
}
378
395
396
+ let mut statement = db_tx. prepare_cached ( & format ! (
397
+ "INSERT INTO {}(txid, first_seen) VALUES(:txid, :first_seen) ON CONFLICT(txid) DO UPDATE SET first_seen=:first_seen" ,
398
+ Self :: TXS_TABLE_NAME ,
399
+ ) ) ?;
400
+ for ( & txid, & first_seen) in & self . first_seen {
401
+ let checked_time = first_seen. to_sql ( ) ?;
402
+ statement. execute ( named_params ! {
403
+ ":txid" : Impl ( txid) ,
404
+ ":first_seen" : Some ( checked_time) ,
405
+ } ) ?;
406
+ }
407
+
379
408
let mut statement = db_tx
380
409
. prepare_cached ( & format ! (
381
410
"INSERT INTO {}(txid, last_seen) VALUES(:txid, :last_seen) ON CONFLICT(txid) DO UPDATE SET last_seen=:last_seen" ,
@@ -653,7 +682,7 @@ mod test {
653
682
}
654
683
655
684
#[ test]
656
- fn v0_to_v2_schema_migration_is_backward_compatible ( ) -> anyhow:: Result < ( ) > {
685
+ fn v0_to_v3_schema_migration_is_backward_compatible ( ) -> anyhow:: Result < ( ) > {
657
686
type ChangeSet = tx_graph:: ChangeSet < ConfirmationBlockTime > ;
658
687
let mut conn = rusqlite:: Connection :: open_in_memory ( ) ?;
659
688
@@ -722,7 +751,7 @@ mod test {
722
751
}
723
752
}
724
753
725
- // Apply v1 & v2 sqlite schema to tables with data
754
+ // Apply v1, v2, v3 sqlite schema to tables with data
726
755
{
727
756
let db_tx = conn. transaction ( ) ?;
728
757
migrate_schema (
@@ -732,6 +761,7 @@ mod test {
732
761
& ChangeSet :: schema_v0 ( ) ,
733
762
& ChangeSet :: schema_v1 ( ) ,
734
763
& ChangeSet :: schema_v2 ( ) ,
764
+ & ChangeSet :: schema_v3 ( ) ,
735
765
] ,
736
766
) ?;
737
767
db_tx. commit ( ) ?;
@@ -748,6 +778,45 @@ mod test {
748
778
Ok ( ( ) )
749
779
}
750
780
781
+ #[ test]
782
+ fn can_persist_first_seen ( ) -> anyhow:: Result < ( ) > {
783
+ use bitcoin:: hashes:: Hash ;
784
+
785
+ type ChangeSet = tx_graph:: ChangeSet < ConfirmationBlockTime > ;
786
+ let mut conn = rusqlite:: Connection :: open_in_memory ( ) ?;
787
+
788
+ // Init tables
789
+ {
790
+ let db_tx = conn. transaction ( ) ?;
791
+ ChangeSet :: init_sqlite_tables ( & db_tx) ?;
792
+ db_tx. commit ( ) ?;
793
+ }
794
+
795
+ let txid = bitcoin:: Txid :: all_zeros ( ) ;
796
+ let first_seen = 100 ;
797
+
798
+ // Persist `first_seen`
799
+ {
800
+ let changeset = ChangeSet {
801
+ first_seen : [ ( txid, first_seen) ] . into ( ) ,
802
+ ..Default :: default ( )
803
+ } ;
804
+ let db_tx = conn. transaction ( ) ?;
805
+ changeset. persist_to_sqlite ( & db_tx) ?;
806
+ db_tx. commit ( ) ?;
807
+ }
808
+
809
+ // Load from sqlite should succeed
810
+ {
811
+ let db_tx = conn. transaction ( ) ?;
812
+ let changeset = ChangeSet :: from_sqlite ( & db_tx) ?;
813
+ db_tx. commit ( ) ?;
814
+ assert_eq ! ( changeset. first_seen. get( & txid) , Some ( & first_seen) ) ;
815
+ }
816
+
817
+ Ok ( ( ) )
818
+ }
819
+
751
820
#[ test]
752
821
fn can_persist_last_evicted ( ) -> anyhow:: Result < ( ) > {
753
822
use bitcoin:: hashes:: Hash ;
0 commit comments