riftenlabs-indexer/src/db/cauldron/tx.rs
Dagur Valberg Johannsson 3abbdbc177
Add timestamp to pools table
To speed up historical price queries, add timestamp to pools.
This denormalizes the database a bit for improved query performance.
2024-10-08 17:22:37 +02:00

105 lines
3.5 KiB
Rust

// Copyright (C) 2024 Riften Labs AS
//
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
use anyhow::Result;
use bitcoin_hashes::hex::{FromHex, ToHex};
use bitcoincash::{BlockHash, TokenID, Txid};
use rusqlite::{params, Connection};
pub fn create_table(conn: &Connection) {
let tbl = "CREATE TABLE tx (
txid TEXT PRIMARY KEY,
blockhash TEXT,
mtp_timestamp BIGINT,
first_seen_timestamp BIGINT
)";
conn.execute(tbl, []).expect("failed to create tx table");
}
pub fn insert_block_tx(
conn: &rusqlite::Connection,
txid: &Txid,
blockhash: &BlockHash,
mtp: i64,
) -> Result<()> {
let sql = "INSERT INTO tx (txid, blockhash, mtp_timestamp)
VALUES (?1, ?2, ?3)
ON CONFLICT(txid) DO UPDATE SET
blockhash = excluded.blockhash,
mtp_timestamp = excluded.mtp_timestamp";
conn.execute(sql, params![&txid.to_hex(), &blockhash.to_hex(), &mtp])?;
Ok(())
}
pub fn insert_mempool_tx(
conn: &rusqlite::Connection,
txid: &Txid,
first_seen_timestamp: u64,
) -> Result<()> {
let sql = "INSERT INTO tx (txid, first_seen_timestamp)
VALUES (?1, ?2)
ON CONFLICT(txid) DO UPDATE SET
first_seen_timestamp = excluded.first_seen_timestamp";
conn.execute(sql, params![&txid.to_hex(), first_seen_timestamp])?;
Ok(())
}
pub fn latest(
conn: &rusqlite::Connection,
limit: usize,
offset: usize,
token_id: Option<TokenID>,
) -> Result<Vec<(Txid, Option<BlockHash>, u64)>, rusqlite::Error> {
let (sql, params) = match token_id {
Some(tid) => (
"SELECT DISTINCT tx.txid, tx.blockhash, tx.mtp_timestamp, tx.first_seen_timestamp
FROM tx
JOIN utxo_funding ON tx.txid = utxo_funding.txid
WHERE utxo_funding.token_id = ?
ORDER BY COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) DESC
LIMIT ? OFFSET ?",
params![tid.to_hex(), limit, offset],
),
None => (
"SELECT tx.txid, tx.blockhash, tx.mtp_timestamp, tx.first_seen_timestamp
FROM tx
ORDER BY COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) DESC
LIMIT ? OFFSET ?",
params![limit, offset],
),
};
let mut stmt = conn.prepare(sql)?;
let tx_iter = stmt.query_map(params, |row| {
let txid_hex: String = row.get(0)?;
let blockhash_hex: Option<String> = row.get(1)?;
let mtp_timestamp: Option<u64> = row.get(2).ok(); // Handle potential NULL
let first_seen_timestamp: Option<u64> = row.get(3).ok(); // Handle potential NULL
// Use first_seen_timestamp if available, otherwise mtp_timestamp, or return an error if both are NULL
let timestamp = first_seen_timestamp.or(mtp_timestamp).ok_or_else(|| {
rusqlite::Error::InvalidColumnType(
2,
"timestamp".to_string(),
rusqlite::types::Type::Null,
)
})?;
Ok((
Txid::from_hex(&txid_hex).expect("Invalid Txid hex"),
blockhash_hex.map(|hex| BlockHash::from_hex(&hex).expect("Invalid BlockHash hex")),
timestamp,
))
})?;
let mut txs = Vec::with_capacity(limit);
for tx in tx_iter {
txs.push(tx?);
}
Ok(txs)
}