@@ -945,6 +945,114 @@ impl BurnchainDB {
945945 }
946946 }
947947
948+ /// Count the burn header hashes yielded by `canonical_sql` (a SELECT
949+ /// producing one TEXT column) that have no `burnchain_db_block_headers`
950+ /// row in `headers_schema`. Both arguments are interpolated into SQL;
951+ /// pass only trusted fixed fragments.
952+ pub ( crate ) fn count_canonical_burn_hashes_missing_from (
953+ conn : & Connection ,
954+ headers_schema : & str ,
955+ canonical_sql : & str ,
956+ ) -> Result < u64 , DBError > {
957+ conn. query_row (
958+ & format ! (
959+ "SELECT COUNT(*) FROM ({canonical_sql}) \
960+ WHERE burn_header_hash NOT IN \
961+ (SELECT block_hash FROM {headers_schema}.burnchain_db_block_headers)"
962+ ) ,
963+ NO_PARAMS ,
964+ |row| row. get ( 0 ) ,
965+ )
966+ . map_err ( DBError :: from)
967+ }
968+ }
969+
970+ // Raw-row test fixture writers. Each helper owns its table's column
971+ // list so fixtures can't drift from the schema; values are raw
972+ // TEXT/ints because fixtures use readable labels, not valid hashes
973+ // (which the typed write paths would reject).
974+ #[ cfg( test) ]
975+ impl BurnchainDB {
976+ /// Insert a `burnchain_db_block_headers` row with no transactions.
977+ pub fn test_insert_block_header_row (
978+ conn : & Connection ,
979+ block_height : u64 ,
980+ block_hash : & str ,
981+ parent_block_hash : & str ,
982+ ) -> Result < ( ) , DBError > {
983+ conn. execute (
984+ "INSERT INTO burnchain_db_block_headers \
985+ (block_height, block_hash, parent_block_hash, num_txs, timestamp) \
986+ VALUES (?1, ?2, ?3, 0, 0)",
987+ params ! [ u64_to_sql( block_height) ?, block_hash, parent_block_hash] ,
988+ ) ?;
989+ Ok ( ( ) )
990+ }
991+
992+ /// Insert a `burnchain_db_block_ops` row with an opaque op payload.
993+ pub fn test_insert_block_ops_row (
994+ conn : & Connection ,
995+ block_hash : & str ,
996+ op : & str ,
997+ txid : & str ,
998+ ) -> rusqlite:: Result < ( ) > {
999+ conn. execute (
1000+ "INSERT INTO burnchain_db_block_ops (block_hash, op, txid) VALUES (?1, ?2, ?3)" ,
1001+ params ! [ block_hash, op, txid] ,
1002+ ) ?;
1003+ Ok ( ( ) )
1004+ }
1005+
1006+ /// Insert a minimal `block_commit_metadata` row.
1007+ pub fn test_insert_block_commit_metadata_row (
1008+ conn : & Connection ,
1009+ burn_block_hash : & str ,
1010+ txid : & str ,
1011+ block_height : u64 ,
1012+ anchor_block : Option < u64 > ,
1013+ ) -> Result < ( ) , DBError > {
1014+ conn. execute (
1015+ "INSERT INTO block_commit_metadata \
1016+ (burn_block_hash, txid, block_height, vtxindex, anchor_block, \
1017+ anchor_block_descendant) \
1018+ VALUES (?1, ?2, ?3, 0, ?4, NULL)",
1019+ params ! [
1020+ burn_block_hash,
1021+ txid,
1022+ u64_to_sql( block_height) ?,
1023+ opt_u64_to_sql( anchor_block) ?,
1024+ ] ,
1025+ ) ?;
1026+ Ok ( ( ) )
1027+ }
1028+
1029+ /// Insert an `anchor_blocks` row.
1030+ pub fn test_insert_anchor_block_row (
1031+ conn : & Connection ,
1032+ reward_cycle : u64 ,
1033+ ) -> Result < ( ) , DBError > {
1034+ conn. execute (
1035+ "INSERT INTO anchor_blocks (reward_cycle) VALUES (?1)" ,
1036+ params ! [ u64_to_sql( reward_cycle) ?] ,
1037+ ) ?;
1038+ Ok ( ( ) )
1039+ }
1040+
1041+ /// Insert an `overrides` row.
1042+ pub fn test_insert_override_row (
1043+ conn : & Connection ,
1044+ reward_cycle : u64 ,
1045+ affirmation_map : & str ,
1046+ ) -> Result < ( ) , DBError > {
1047+ conn. execute (
1048+ "INSERT INTO overrides (reward_cycle, affirmation_map) VALUES (?1, ?2)" ,
1049+ params ! [ u64_to_sql( reward_cycle) ?, affirmation_map] ,
1050+ ) ?;
1051+ Ok ( ( ) )
1052+ }
1053+ }
1054+
1055+ impl BurnchainDB {
9481056 // do NOT call directly; only call directly in tests.
9491057 // This is only `pub` because the tests for it live in a different file.
9501058 pub fn store_new_burnchain_block_ops_unchecked (
0 commit comments