Skip to content

Commit 4a393b5

Browse files
committed
fix: Respect nullable columns of tx table
Before, reading the tx_graph ChangeSet might have failed or returned incorrect data by attempting to decode a column having a NULL value. This is fixed by introducing a `TxRow` struct with optional fields and deriving `FromRow` for it. The `txid` field is currently the only non-nullable column. Now we only parse a value and update the tx_graph ChangeSet if the column actually contains a value.
1 parent 1c260fe commit 4a393b5

File tree

1 file changed

+35
-17
lines changed

1 file changed

+35
-17
lines changed

src/async_store.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -201,24 +201,27 @@ impl Store {
201201
pub async fn read_tx_graph(&self) -> Result<tx_graph::ChangeSet<ConfirmationBlockTime>, Error> {
202202
let mut changeset = tx_graph::ChangeSet::default();
203203

204-
let rows = sqlx::query("SELECT txid, tx, first_seen, last_seen, last_evicted FROM tx")
205-
.fetch_all(&self.pool)
206-
.await?;
204+
let rows: Vec<TxRow> =
205+
sqlx::query_as("SELECT txid, tx, first_seen, last_seen, last_evicted FROM tx")
206+
.fetch_all(&self.pool)
207+
.await?;
207208
for row in rows {
208-
let txid: String = row.get("txid");
209-
let txid: Txid = txid.parse()?;
210-
let data: Vec<u8> = row.get("tx");
211-
let tx: Transaction = consensus::encode::deserialize(&data)?;
212-
let first_seen: i64 = row.get("first_seen");
213-
let last_seen: i64 = row.get("last_seen");
214-
let last_evicted: i64 = row.get("last_evicted");
215-
216-
changeset.txs.insert(Arc::new(tx));
217-
changeset.first_seen.insert(txid, first_seen.try_into()?);
218-
changeset.last_seen.insert(txid, last_seen.try_into()?);
219-
changeset
220-
.last_evicted
221-
.insert(txid, last_evicted.try_into()?);
209+
let txid: Txid = row.txid.parse()?;
210+
if let Some(data) = row.tx {
211+
let tx: Transaction = consensus::encode::deserialize(&data)?;
212+
changeset.txs.insert(Arc::new(tx));
213+
}
214+
if let Some(first_seen) = row.first_seen {
215+
changeset.first_seen.insert(txid, first_seen.try_into()?);
216+
}
217+
if let Some(last_seen) = row.last_seen {
218+
changeset.last_seen.insert(txid, last_seen.try_into()?);
219+
}
220+
if let Some(last_evicted) = row.last_evicted {
221+
changeset
222+
.last_evicted
223+
.insert(txid, last_evicted.try_into()?);
224+
}
222225
}
223226

224227
let rows = sqlx::query("SELECT txid, vout, value, script FROM txout")
@@ -315,6 +318,21 @@ impl Store {
315318
}
316319
}
317320

321+
/// Represents a row in the tx table.
322+
#[derive(Debug, sqlx::FromRow)]
323+
struct TxRow {
324+
/// Txid
325+
txid: String,
326+
/// Raw transaction
327+
tx: Option<Vec<u8>>,
328+
/// First seen
329+
first_seen: Option<i64>,
330+
/// Last seen
331+
last_seen: Option<i64>,
332+
/// Last evicted
333+
last_evicted: Option<i64>,
334+
}
335+
318336
#[cfg(test)]
319337
mod test {
320338
use super::*;

0 commit comments

Comments
 (0)