diff --git a/src/db/cauldron/tokenlist/db_utils.rs b/src/db/cauldron/tokenlist/db_utils.rs index e874ec8..e04cc0b 100644 --- a/src/db/cauldron/tokenlist/db_utils.rs +++ b/src/db/cauldron/tokenlist/db_utils.rs @@ -253,14 +253,12 @@ fn add_column_if_missing(conn: &Connection, table: &str, column: &str, decl: &st Ok(()) } -fn table_has_column(conn: &Connection, table: &str, column: &str) -> Result { +fn table_has_column(conn: &Connection, table: &str, column: &str) -> anyhow::Result { let mut stmt = conn.prepare(&format!("PRAGMA table_info({table});"))?; let mut rows = stmt.query([])?; while let Some(row) = rows.next()? { let name: String = row.get(1)?; - if name.eq_ignore_ascii_case(column) { - return Ok(true); - } + if name.eq_ignore_ascii_case(column) { return Ok(true); } } Ok(false) } @@ -296,25 +294,48 @@ pub fn cache_first_pool_ts_if_empty(conn: &Connection, token_id: &str, ts: i64) pub fn db_first_pool_creation_row( conn: &Connection, token_id: &str, -) -> Result> { - // Prefer height when available; otherwise fall back to timestamp-only ordering. - let has_height = table_has_column(conn, "tx", "block_height")?; - let sql = if has_height { +) -> anyhow::Result> { + let has_tx_height = table_has_column(conn, "tx", "block_height")?; + let has_blockhash = table_has_column(conn, "tx", "blockhash")?; + let has_headers_key = table_has_column(conn, "headers", "key")?; + let has_headers_ht = table_has_column(conn, "headers", "height")?; + + let sql = if has_tx_height { + // Native height column present r#" - SELECT p.creation_utxo, uf.txid, t.effective_timestamp, t.block_height + SELECT p.creation_utxo, uf.txid, t.effective_timestamp, t.block_height AS block_height FROM pool AS p JOIN utxo_funding AS uf ON uf.new_utxo_hash = p.creation_utxo JOIN tx AS t ON t.txid = uf.txid WHERE p.token_id = ? ORDER BY - (t.block_height IS NULL) ASC, - t.block_height ASC, + (t.block_height IS NULL) ASC, -- confirmed first + t.block_height ASC, -- earliest height + t.effective_timestamp ASC, + uf.txid ASC, + p.creation_utxo ASC + LIMIT 1; + "# + } else if has_blockhash && has_headers_key && has_headers_ht { + // Derive height from headers + r#" + SELECT p.creation_utxo, uf.txid, t.effective_timestamp, h.height AS block_height + FROM pool AS p + JOIN utxo_funding AS uf ON uf.new_utxo_hash = p.creation_utxo + JOIN tx AS t ON t.txid = uf.txid + LEFT JOIN headers AS h ON h.key = t.blockhash + WHERE p.token_id = ? + ORDER BY + (t.blockhash IS NULL) ASC, -- confirmed (has blockhash) first + (h.height IS NULL) ASC, -- known height before unknown + h.height ASC, t.effective_timestamp ASC, uf.txid ASC, p.creation_utxo ASC LIMIT 1; "# } else { + // Timestamp-only fallback r#" SELECT p.creation_utxo, uf.txid, t.effective_timestamp, NULL AS block_height FROM pool AS p @@ -332,10 +353,10 @@ pub fn db_first_pool_creation_row( let mut stmt = conn.prepare(sql)?; let mut rows = stmt.query(params![token_id])?; if let Some(row) = rows.next()? { - let creation_utxo: String = row.get(0)?; - let txid: String = row.get(1)?; - let ts: i64 = row.get(2)?; - let height: Option = row.get(3)?; + let creation_utxo: String = row.get(0)?; + let txid: String = row.get(1)?; + let ts: i64 = row.get(2)?; + let height: Option = row.get(3)?; Ok(Some((creation_utxo, txid, ts, height))) } else { Ok(None)