To speed up historical price queries, add timestamp to pools. This denormalizes the database a bit for improved query performance.
105 lines
3.5 KiB
Rust
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)
|
|
}
|