zcash_client_sqlite: Generalize SQLite-backed ShardStore impl to make it reusable for Orchard.
This commit is contained in:
parent
0a4236f725
commit
106669d977
|
@ -633,7 +633,7 @@ impl<P: consensus::Parameters> WalletCommitmentTrees for WalletDb<rusqlite::Conn
|
|||
.conn
|
||||
.transaction()
|
||||
.map_err(|e| ShardTreeError::Storage(Either::Right(e)))?;
|
||||
let shard_store = SqliteShardStore::from_connection(&tx)
|
||||
let shard_store = SqliteShardStore::from_connection(&tx, "sapling")
|
||||
.map_err(|e| ShardTreeError::Storage(Either::Right(e)))?;
|
||||
let result = {
|
||||
let mut shardtree = ShardTree::new(shard_store, 100);
|
||||
|
@ -662,7 +662,7 @@ impl<'conn, P: consensus::Parameters> WalletCommitmentTrees for WalletDb<SqlTran
|
|||
E: From<ShardTreeError<Either<io::Error, rusqlite::Error>>>,
|
||||
{
|
||||
let mut shardtree = ShardTree::new(
|
||||
SqliteShardStore::from_connection(self.conn.0)
|
||||
SqliteShardStore::from_connection(self.conn.0, "sapling")
|
||||
.map_err(|e| ShardTreeError::Storage(Either::Right(e)))?,
|
||||
100,
|
||||
);
|
||||
|
|
|
@ -94,6 +94,7 @@ impl RusqliteMigration for Migration {
|
|||
let shard_store =
|
||||
SqliteShardStore::<_, sapling::Node, SAPLING_SHARD_HEIGHT>::from_connection(
|
||||
transaction,
|
||||
"sapling",
|
||||
)?;
|
||||
let mut shard_tree: ShardTree<
|
||||
_,
|
||||
|
|
|
@ -15,15 +15,20 @@ use crate::serialization::{read_shard, write_shard_v1};
|
|||
|
||||
pub struct SqliteShardStore<C, H, const SHARD_HEIGHT: u8> {
|
||||
pub(crate) conn: C,
|
||||
table_prefix: &'static str,
|
||||
_hash_type: PhantomData<H>,
|
||||
}
|
||||
|
||||
impl<C, H, const SHARD_HEIGHT: u8> SqliteShardStore<C, H, SHARD_HEIGHT> {
|
||||
const SHARD_ROOT_LEVEL: Level = Level::new(SHARD_HEIGHT);
|
||||
|
||||
pub(crate) fn from_connection(conn: C) -> Result<Self, rusqlite::Error> {
|
||||
pub(crate) fn from_connection(
|
||||
conn: C,
|
||||
table_prefix: &'static str,
|
||||
) -> Result<Self, rusqlite::Error> {
|
||||
Ok(SqliteShardStore {
|
||||
conn,
|
||||
table_prefix,
|
||||
_hash_type: PhantomData,
|
||||
})
|
||||
}
|
||||
|
@ -40,39 +45,39 @@ impl<'conn, 'a: 'conn, H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
&self,
|
||||
shard_root: Address,
|
||||
) -> Result<Option<LocatedPrunableTree<Self::H>>, Self::Error> {
|
||||
get_shard(self.conn, shard_root)
|
||||
get_shard(self.conn, self.table_prefix, shard_root)
|
||||
}
|
||||
|
||||
fn last_shard(&self) -> Result<Option<LocatedPrunableTree<Self::H>>, Self::Error> {
|
||||
last_shard(self.conn, Self::SHARD_ROOT_LEVEL)
|
||||
last_shard(self.conn, self.table_prefix, Self::SHARD_ROOT_LEVEL)
|
||||
}
|
||||
|
||||
fn put_shard(&mut self, subtree: LocatedPrunableTree<Self::H>) -> Result<(), Self::Error> {
|
||||
put_shard(self.conn, subtree)
|
||||
put_shard(self.conn, self.table_prefix, subtree)
|
||||
}
|
||||
|
||||
fn get_shard_roots(&self) -> Result<Vec<Address>, Self::Error> {
|
||||
get_shard_roots(self.conn, Self::SHARD_ROOT_LEVEL)
|
||||
get_shard_roots(self.conn, self.table_prefix, Self::SHARD_ROOT_LEVEL)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, from: Address) -> Result<(), Self::Error> {
|
||||
truncate(self.conn, from)
|
||||
truncate(self.conn, self.table_prefix, from)
|
||||
}
|
||||
|
||||
fn get_cap(&self) -> Result<PrunableTree<Self::H>, Self::Error> {
|
||||
get_cap(self.conn)
|
||||
get_cap(self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn put_cap(&mut self, cap: PrunableTree<Self::H>) -> Result<(), Self::Error> {
|
||||
put_cap(self.conn, cap)
|
||||
put_cap(self.conn, self.table_prefix, cap)
|
||||
}
|
||||
|
||||
fn min_checkpoint_id(&self) -> Result<Option<Self::CheckpointId>, Self::Error> {
|
||||
min_checkpoint_id(self.conn)
|
||||
min_checkpoint_id(self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn max_checkpoint_id(&self) -> Result<Option<Self::CheckpointId>, Self::Error> {
|
||||
max_checkpoint_id(self.conn)
|
||||
max_checkpoint_id(self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn add_checkpoint(
|
||||
|
@ -80,32 +85,32 @@ impl<'conn, 'a: 'conn, H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
checkpoint_id: Self::CheckpointId,
|
||||
checkpoint: Checkpoint,
|
||||
) -> Result<(), Self::Error> {
|
||||
add_checkpoint(self.conn, checkpoint_id, checkpoint)
|
||||
add_checkpoint(self.conn, self.table_prefix, checkpoint_id, checkpoint)
|
||||
}
|
||||
|
||||
fn checkpoint_count(&self) -> Result<usize, Self::Error> {
|
||||
checkpoint_count(self.conn)
|
||||
checkpoint_count(self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn get_checkpoint_at_depth(
|
||||
&self,
|
||||
checkpoint_depth: usize,
|
||||
) -> Result<Option<(Self::CheckpointId, Checkpoint)>, Self::Error> {
|
||||
get_checkpoint_at_depth(self.conn, checkpoint_depth)
|
||||
get_checkpoint_at_depth(self.conn, self.table_prefix, checkpoint_depth)
|
||||
}
|
||||
|
||||
fn get_checkpoint(
|
||||
&self,
|
||||
checkpoint_id: &Self::CheckpointId,
|
||||
) -> Result<Option<Checkpoint>, Self::Error> {
|
||||
get_checkpoint(self.conn, *checkpoint_id)
|
||||
get_checkpoint(self.conn, self.table_prefix, *checkpoint_id)
|
||||
}
|
||||
|
||||
fn with_checkpoints<F>(&mut self, limit: usize, callback: F) -> Result<(), Self::Error>
|
||||
where
|
||||
F: FnMut(&Self::CheckpointId, &Checkpoint) -> Result<(), Self::Error>,
|
||||
{
|
||||
with_checkpoints(self.conn, limit, callback)
|
||||
with_checkpoints(self.conn, self.table_prefix, limit, callback)
|
||||
}
|
||||
|
||||
fn update_checkpoint_with<F>(
|
||||
|
@ -116,18 +121,18 @@ impl<'conn, 'a: 'conn, H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
where
|
||||
F: Fn(&mut Checkpoint) -> Result<(), Self::Error>,
|
||||
{
|
||||
update_checkpoint_with(self.conn, *checkpoint_id, update)
|
||||
update_checkpoint_with(self.conn, self.table_prefix, *checkpoint_id, update)
|
||||
}
|
||||
|
||||
fn remove_checkpoint(&mut self, checkpoint_id: &Self::CheckpointId) -> Result<(), Self::Error> {
|
||||
remove_checkpoint(self.conn, *checkpoint_id)
|
||||
remove_checkpoint(self.conn, self.table_prefix, *checkpoint_id)
|
||||
}
|
||||
|
||||
fn truncate_checkpoints(
|
||||
&mut self,
|
||||
checkpoint_id: &Self::CheckpointId,
|
||||
) -> Result<(), Self::Error> {
|
||||
truncate_checkpoints(self.conn, *checkpoint_id)
|
||||
truncate_checkpoints(self.conn, self.table_prefix, *checkpoint_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,42 +147,42 @@ impl<H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
&self,
|
||||
shard_root: Address,
|
||||
) -> Result<Option<LocatedPrunableTree<Self::H>>, Self::Error> {
|
||||
get_shard(&self.conn, shard_root)
|
||||
get_shard(&self.conn, self.table_prefix, shard_root)
|
||||
}
|
||||
|
||||
fn last_shard(&self) -> Result<Option<LocatedPrunableTree<Self::H>>, Self::Error> {
|
||||
last_shard(&self.conn, Self::SHARD_ROOT_LEVEL)
|
||||
last_shard(&self.conn, self.table_prefix, Self::SHARD_ROOT_LEVEL)
|
||||
}
|
||||
|
||||
fn put_shard(&mut self, subtree: LocatedPrunableTree<Self::H>) -> Result<(), Self::Error> {
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
put_shard(&tx, subtree)?;
|
||||
put_shard(&tx, self.table_prefix, subtree)?;
|
||||
tx.commit().map_err(Either::Right)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_shard_roots(&self) -> Result<Vec<Address>, Self::Error> {
|
||||
get_shard_roots(&self.conn, Self::SHARD_ROOT_LEVEL)
|
||||
get_shard_roots(&self.conn, self.table_prefix, Self::SHARD_ROOT_LEVEL)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, from: Address) -> Result<(), Self::Error> {
|
||||
truncate(&self.conn, from)
|
||||
truncate(&self.conn, self.table_prefix, from)
|
||||
}
|
||||
|
||||
fn get_cap(&self) -> Result<PrunableTree<Self::H>, Self::Error> {
|
||||
get_cap(&self.conn)
|
||||
get_cap(&self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn put_cap(&mut self, cap: PrunableTree<Self::H>) -> Result<(), Self::Error> {
|
||||
put_cap(&self.conn, cap)
|
||||
put_cap(&self.conn, self.table_prefix, cap)
|
||||
}
|
||||
|
||||
fn min_checkpoint_id(&self) -> Result<Option<Self::CheckpointId>, Self::Error> {
|
||||
min_checkpoint_id(&self.conn)
|
||||
min_checkpoint_id(&self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn max_checkpoint_id(&self) -> Result<Option<Self::CheckpointId>, Self::Error> {
|
||||
max_checkpoint_id(&self.conn)
|
||||
max_checkpoint_id(&self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn add_checkpoint(
|
||||
|
@ -186,26 +191,26 @@ impl<H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
checkpoint: Checkpoint,
|
||||
) -> Result<(), Self::Error> {
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
add_checkpoint(&tx, checkpoint_id, checkpoint)?;
|
||||
add_checkpoint(&tx, self.table_prefix, checkpoint_id, checkpoint)?;
|
||||
tx.commit().map_err(Either::Right)
|
||||
}
|
||||
|
||||
fn checkpoint_count(&self) -> Result<usize, Self::Error> {
|
||||
checkpoint_count(&self.conn)
|
||||
checkpoint_count(&self.conn, self.table_prefix)
|
||||
}
|
||||
|
||||
fn get_checkpoint_at_depth(
|
||||
&self,
|
||||
checkpoint_depth: usize,
|
||||
) -> Result<Option<(Self::CheckpointId, Checkpoint)>, Self::Error> {
|
||||
get_checkpoint_at_depth(&self.conn, checkpoint_depth)
|
||||
get_checkpoint_at_depth(&self.conn, self.table_prefix, checkpoint_depth)
|
||||
}
|
||||
|
||||
fn get_checkpoint(
|
||||
&self,
|
||||
checkpoint_id: &Self::CheckpointId,
|
||||
) -> Result<Option<Checkpoint>, Self::Error> {
|
||||
get_checkpoint(&self.conn, *checkpoint_id)
|
||||
get_checkpoint(&self.conn, self.table_prefix, *checkpoint_id)
|
||||
}
|
||||
|
||||
fn with_checkpoints<F>(&mut self, limit: usize, callback: F) -> Result<(), Self::Error>
|
||||
|
@ -213,7 +218,7 @@ impl<H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
F: FnMut(&Self::CheckpointId, &Checkpoint) -> Result<(), Self::Error>,
|
||||
{
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
with_checkpoints(&tx, limit, callback)?;
|
||||
with_checkpoints(&tx, self.table_prefix, limit, callback)?;
|
||||
tx.commit().map_err(Either::Right)
|
||||
}
|
||||
|
||||
|
@ -226,14 +231,14 @@ impl<H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
F: Fn(&mut Checkpoint) -> Result<(), Self::Error>,
|
||||
{
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
let result = update_checkpoint_with(&tx, *checkpoint_id, update)?;
|
||||
let result = update_checkpoint_with(&tx, self.table_prefix, *checkpoint_id, update)?;
|
||||
tx.commit().map_err(Either::Right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn remove_checkpoint(&mut self, checkpoint_id: &Self::CheckpointId) -> Result<(), Self::Error> {
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
remove_checkpoint(&tx, *checkpoint_id)?;
|
||||
remove_checkpoint(&tx, self.table_prefix, *checkpoint_id)?;
|
||||
tx.commit().map_err(Either::Right)
|
||||
}
|
||||
|
||||
|
@ -242,7 +247,7 @@ impl<H: HashSer, const SHARD_HEIGHT: u8> ShardStore
|
|||
checkpoint_id: &Self::CheckpointId,
|
||||
) -> Result<(), Self::Error> {
|
||||
let tx = self.conn.transaction().map_err(Either::Right)?;
|
||||
truncate_checkpoints(&tx, *checkpoint_id)?;
|
||||
truncate_checkpoints(&tx, self.table_prefix, *checkpoint_id)?;
|
||||
tx.commit().map_err(Either::Right)
|
||||
}
|
||||
}
|
||||
|
@ -251,12 +256,16 @@ type Error = Either<io::Error, rusqlite::Error>;
|
|||
|
||||
pub(crate) fn get_shard<H: HashSer>(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
shard_root: Address,
|
||||
) -> Result<Option<LocatedPrunableTree<H>>, Error> {
|
||||
conn.query_row(
|
||||
"SELECT shard_data
|
||||
FROM sapling_tree_shards
|
||||
WHERE shard_index = :shard_index",
|
||||
&format!(
|
||||
"SELECT shard_data
|
||||
FROM {}_tree_shards
|
||||
WHERE shard_index = :shard_index",
|
||||
table_prefix
|
||||
),
|
||||
named_params![":shard_index": shard_root.index()],
|
||||
|row| row.get::<_, Vec<u8>>(0),
|
||||
)
|
||||
|
@ -271,13 +280,17 @@ pub(crate) fn get_shard<H: HashSer>(
|
|||
|
||||
pub(crate) fn last_shard<H: HashSer>(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
shard_root_level: Level,
|
||||
) -> Result<Option<LocatedPrunableTree<H>>, Error> {
|
||||
conn.query_row(
|
||||
"SELECT shard_index, shard_data
|
||||
FROM sapling_tree_shards
|
||||
ORDER BY shard_index DESC
|
||||
LIMIT 1",
|
||||
&format!(
|
||||
"SELECT shard_index, shard_data
|
||||
FROM {}_tree_shards
|
||||
ORDER BY shard_index DESC
|
||||
LIMIT 1",
|
||||
table_prefix
|
||||
),
|
||||
[],
|
||||
|row| {
|
||||
let shard_index: u64 = row.get(0)?;
|
||||
|
@ -297,6 +310,7 @@ pub(crate) fn last_shard<H: HashSer>(
|
|||
|
||||
pub(crate) fn put_shard<H: HashSer>(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
subtree: LocatedPrunableTree<H>,
|
||||
) -> Result<(), Error> {
|
||||
let subtree_root_hash = subtree
|
||||
|
@ -316,13 +330,14 @@ pub(crate) fn put_shard<H: HashSer>(
|
|||
write_shard_v1(&mut subtree_data, subtree.root()).map_err(Either::Left)?;
|
||||
|
||||
let mut stmt_put_shard = conn
|
||||
.prepare_cached(
|
||||
"INSERT INTO sapling_tree_shards (shard_index, root_hash, shard_data)
|
||||
.prepare_cached(&format!(
|
||||
"INSERT INTO {}_tree_shards (shard_index, root_hash, shard_data)
|
||||
VALUES (:shard_index, :root_hash, :shard_data)
|
||||
ON CONFLICT (shard_index) DO UPDATE
|
||||
SET root_hash = :root_hash,
|
||||
shard_data = :shard_data",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
stmt_put_shard
|
||||
|
@ -338,10 +353,14 @@ pub(crate) fn put_shard<H: HashSer>(
|
|||
|
||||
pub(crate) fn get_shard_roots(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
shard_root_level: Level,
|
||||
) -> Result<Vec<Address>, Error> {
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT shard_index FROM sapling_tree_shards ORDER BY shard_index")
|
||||
.prepare(&format!(
|
||||
"SELECT shard_index FROM {}_tree_shards ORDER BY shard_index",
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
let mut rows = stmt.query([]).map_err(Either::Right)?;
|
||||
|
||||
|
@ -355,19 +374,31 @@ pub(crate) fn get_shard_roots(
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) fn truncate(conn: &rusqlite::Connection, from: Address) -> Result<(), Error> {
|
||||
pub(crate) fn truncate(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
from: Address,
|
||||
) -> Result<(), Error> {
|
||||
conn.execute(
|
||||
"DELETE FROM sapling_tree_shards WHERE shard_index >= ?",
|
||||
&format!(
|
||||
"DELETE FROM {}_tree_shards WHERE shard_index >= ?",
|
||||
table_prefix
|
||||
),
|
||||
[from.index()],
|
||||
)
|
||||
.map_err(Either::Right)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub(crate) fn get_cap<H: HashSer>(conn: &rusqlite::Connection) -> Result<PrunableTree<H>, Error> {
|
||||
conn.query_row("SELECT cap_data FROM sapling_tree_cap", [], |row| {
|
||||
row.get::<_, Vec<u8>>(0)
|
||||
})
|
||||
pub(crate) fn get_cap<H: HashSer>(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
) -> Result<PrunableTree<H>, Error> {
|
||||
conn.query_row(
|
||||
&format!("SELECT cap_data FROM {}_tree_cap", table_prefix),
|
||||
[],
|
||||
|row| row.get::<_, Vec<u8>>(0),
|
||||
)
|
||||
.optional()
|
||||
.map_err(Either::Right)?
|
||||
.map_or_else(
|
||||
|
@ -378,15 +409,17 @@ pub(crate) fn get_cap<H: HashSer>(conn: &rusqlite::Connection) -> Result<Prunabl
|
|||
|
||||
pub(crate) fn put_cap<H: HashSer>(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
cap: PrunableTree<H>,
|
||||
) -> Result<(), Error> {
|
||||
let mut stmt = conn
|
||||
.prepare_cached(
|
||||
"INSERT INTO sapling_tree_cap (cap_id, cap_data)
|
||||
VALUES (0, :cap_data)
|
||||
ON CONFLICT (cap_id) DO UPDATE
|
||||
SET cap_data = :cap_data",
|
||||
)
|
||||
.prepare_cached(&format!(
|
||||
"INSERT INTO {}_tree_cap (cap_id, cap_data)
|
||||
VALUES (0, :cap_data)
|
||||
ON CONFLICT (cap_id) DO UPDATE
|
||||
SET cap_data = :cap_data",
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
let mut cap_data = vec![];
|
||||
|
@ -396,9 +429,15 @@ pub(crate) fn put_cap<H: HashSer>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn min_checkpoint_id(conn: &rusqlite::Connection) -> Result<Option<BlockHeight>, Error> {
|
||||
pub(crate) fn min_checkpoint_id(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
) -> Result<Option<BlockHeight>, Error> {
|
||||
conn.query_row(
|
||||
"SELECT MIN(checkpoint_id) FROM sapling_tree_checkpoints",
|
||||
&format!(
|
||||
"SELECT MIN(checkpoint_id) FROM {}_tree_checkpoints",
|
||||
table_prefix
|
||||
),
|
||||
[],
|
||||
|row| {
|
||||
row.get::<_, Option<u32>>(0)
|
||||
|
@ -408,9 +447,15 @@ pub(crate) fn min_checkpoint_id(conn: &rusqlite::Connection) -> Result<Option<Bl
|
|||
.map_err(Either::Right)
|
||||
}
|
||||
|
||||
pub(crate) fn max_checkpoint_id(conn: &rusqlite::Connection) -> Result<Option<BlockHeight>, Error> {
|
||||
pub(crate) fn max_checkpoint_id(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
) -> Result<Option<BlockHeight>, Error> {
|
||||
conn.query_row(
|
||||
"SELECT MAX(checkpoint_id) FROM sapling_tree_checkpoints",
|
||||
&format!(
|
||||
"SELECT MAX(checkpoint_id) FROM {}_tree_checkpoints",
|
||||
table_prefix
|
||||
),
|
||||
[],
|
||||
|row| {
|
||||
row.get::<_, Option<u32>>(0)
|
||||
|
@ -422,14 +467,16 @@ pub(crate) fn max_checkpoint_id(conn: &rusqlite::Connection) -> Result<Option<Bl
|
|||
|
||||
pub(crate) fn add_checkpoint(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_id: BlockHeight,
|
||||
checkpoint: Checkpoint,
|
||||
) -> Result<(), Error> {
|
||||
let mut stmt_insert_checkpoint = conn
|
||||
.prepare_cached(
|
||||
"INSERT INTO sapling_tree_checkpoints (checkpoint_id, position)
|
||||
.prepare_cached(&format!(
|
||||
"INSERT INTO {}_tree_checkpoints (checkpoint_id, position)
|
||||
VALUES (:checkpoint_id, :position)",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
stmt_insert_checkpoint
|
||||
|
@ -439,10 +486,13 @@ pub(crate) fn add_checkpoint(
|
|||
])
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
let mut stmt_insert_mark_removed = conn.prepare_cached(
|
||||
"INSERT INTO sapling_tree_checkpoint_marks_removed (checkpoint_id, mark_removed_position)
|
||||
VALUES (:checkpoint_id, :position)",
|
||||
).map_err(Either::Right)?;
|
||||
let mut stmt_insert_mark_removed = conn
|
||||
.prepare_cached(&format!(
|
||||
"INSERT INTO {}_tree_checkpoint_marks_removed (checkpoint_id, mark_removed_position)
|
||||
VALUES (:checkpoint_id, :position)",
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
for pos in checkpoint.marks_removed() {
|
||||
stmt_insert_mark_removed
|
||||
|
@ -456,22 +506,31 @@ pub(crate) fn add_checkpoint(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn checkpoint_count(conn: &rusqlite::Connection) -> Result<usize, Error> {
|
||||
conn.query_row("SELECT COUNT(*) FROM sapling_tree_checkpoints", [], |row| {
|
||||
row.get::<_, usize>(0)
|
||||
})
|
||||
pub(crate) fn checkpoint_count(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
) -> Result<usize, Error> {
|
||||
conn.query_row(
|
||||
&format!("SELECT COUNT(*) FROM {}_tree_checkpoints", table_prefix),
|
||||
[],
|
||||
|row| row.get::<_, usize>(0),
|
||||
)
|
||||
.map_err(Either::Right)
|
||||
}
|
||||
|
||||
pub(crate) fn get_checkpoint(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_id: BlockHeight,
|
||||
) -> Result<Option<Checkpoint>, Error> {
|
||||
let checkpoint_position = conn
|
||||
.query_row(
|
||||
"SELECT position
|
||||
FROM sapling_tree_checkpoints
|
||||
&format!(
|
||||
"SELECT position
|
||||
FROM {}_tree_checkpoints
|
||||
WHERE checkpoint_id = ?",
|
||||
table_prefix
|
||||
),
|
||||
[u32::from(checkpoint_id)],
|
||||
|row| {
|
||||
row.get::<_, Option<u64>>(0)
|
||||
|
@ -485,11 +544,12 @@ pub(crate) fn get_checkpoint(
|
|||
.map(|pos_opt| {
|
||||
let mut marks_removed = BTreeSet::new();
|
||||
let mut stmt = conn
|
||||
.prepare_cached(
|
||||
.prepare_cached(&format!(
|
||||
"SELECT mark_removed_position
|
||||
FROM sapling_tree_checkpoint_marks_removed
|
||||
FROM {}_tree_checkpoint_marks_removed
|
||||
WHERE checkpoint_id = ?",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
let mut mark_removed_rows = stmt
|
||||
.query([u32::from(checkpoint_id)])
|
||||
|
@ -513,6 +573,7 @@ pub(crate) fn get_checkpoint(
|
|||
|
||||
pub(crate) fn get_checkpoint_at_depth(
|
||||
conn: &rusqlite::Connection,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_depth: usize,
|
||||
) -> Result<Option<(BlockHeight, Checkpoint)>, Error> {
|
||||
if checkpoint_depth == 0 {
|
||||
|
@ -521,11 +582,14 @@ pub(crate) fn get_checkpoint_at_depth(
|
|||
|
||||
let checkpoint_parts = conn
|
||||
.query_row(
|
||||
"SELECT checkpoint_id, position
|
||||
FROM sapling_tree_checkpoints
|
||||
ORDER BY checkpoint_id DESC
|
||||
LIMIT 1
|
||||
OFFSET :offset",
|
||||
&format!(
|
||||
"SELECT checkpoint_id, position
|
||||
FROM {}_tree_checkpoints
|
||||
ORDER BY checkpoint_id DESC
|
||||
LIMIT 1
|
||||
OFFSET :offset",
|
||||
table_prefix
|
||||
),
|
||||
named_params![":offset": checkpoint_depth - 1],
|
||||
|row| {
|
||||
let checkpoint_id: u32 = row.get(0)?;
|
||||
|
@ -543,11 +607,12 @@ pub(crate) fn get_checkpoint_at_depth(
|
|||
.map(|(checkpoint_id, pos_opt)| {
|
||||
let mut marks_removed = BTreeSet::new();
|
||||
let mut stmt = conn
|
||||
.prepare_cached(
|
||||
.prepare_cached(&format!(
|
||||
"SELECT mark_removed_position
|
||||
FROM sapling_tree_checkpoint_marks_removed
|
||||
FROM {}_tree_checkpoint_marks_removed
|
||||
WHERE checkpoint_id = ?",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
let mut mark_removed_rows = stmt
|
||||
.query([u32::from(checkpoint_id)])
|
||||
|
@ -574,6 +639,7 @@ pub(crate) fn get_checkpoint_at_depth(
|
|||
|
||||
pub(crate) fn with_checkpoints<F>(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
limit: usize,
|
||||
mut callback: F,
|
||||
) -> Result<(), Error>
|
||||
|
@ -581,19 +647,21 @@ where
|
|||
F: FnMut(&BlockHeight, &Checkpoint) -> Result<(), Error>,
|
||||
{
|
||||
let mut stmt_get_checkpoints = conn
|
||||
.prepare_cached(
|
||||
.prepare_cached(&format!(
|
||||
"SELECT checkpoint_id, position
|
||||
FROM sapling_tree_checkpoints
|
||||
FROM {}_tree_checkpoints
|
||||
LIMIT :limit",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
let mut stmt_get_checkpoint_marks_removed = conn
|
||||
.prepare_cached(
|
||||
.prepare_cached(&format!(
|
||||
"SELECT mark_removed_position
|
||||
FROM sapling_tree_checkpoint_marks_removed
|
||||
FROM {}_tree_checkpoint_marks_removed
|
||||
WHERE checkpoint_id = :checkpoint_id",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
let mut rows = stmt_get_checkpoints
|
||||
|
@ -630,16 +698,17 @@ where
|
|||
|
||||
pub(crate) fn update_checkpoint_with<F>(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_id: BlockHeight,
|
||||
update: F,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
F: Fn(&mut Checkpoint) -> Result<(), Error>,
|
||||
{
|
||||
if let Some(mut c) = get_checkpoint(conn, checkpoint_id)? {
|
||||
if let Some(mut c) = get_checkpoint(conn, table_prefix, checkpoint_id)? {
|
||||
update(&mut c)?;
|
||||
remove_checkpoint(conn, checkpoint_id)?;
|
||||
add_checkpoint(conn, checkpoint_id, c)?;
|
||||
remove_checkpoint(conn, table_prefix, checkpoint_id)?;
|
||||
add_checkpoint(conn, table_prefix, checkpoint_id, c)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
|
@ -648,14 +717,17 @@ where
|
|||
|
||||
pub(crate) fn remove_checkpoint(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_id: BlockHeight,
|
||||
) -> Result<(), Error> {
|
||||
// sapling_tree_checkpoints is constructed with `ON DELETE CASCADE`
|
||||
// cascading delete here obviates the need to manually delete from
|
||||
// `tree_checkpoint_marks_removed`
|
||||
let mut stmt_delete_checkpoint = conn
|
||||
.prepare_cached(
|
||||
"DELETE FROM sapling_tree_checkpoints
|
||||
.prepare_cached(&format!(
|
||||
"DELETE FROM {}_tree_checkpoints
|
||||
WHERE checkpoint_id = :checkpoint_id",
|
||||
)
|
||||
table_prefix
|
||||
))
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
stmt_delete_checkpoint
|
||||
|
@ -667,19 +739,20 @@ pub(crate) fn remove_checkpoint(
|
|||
|
||||
pub(crate) fn truncate_checkpoints(
|
||||
conn: &rusqlite::Transaction<'_>,
|
||||
table_prefix: &'static str,
|
||||
checkpoint_id: BlockHeight,
|
||||
) -> Result<(), Error> {
|
||||
// cascading delete here obviates the need to manually delete from
|
||||
// `tree_checkpoint_marks_removed`
|
||||
conn.execute(
|
||||
"DELETE FROM sapling_tree_checkpoints WHERE checkpoint_id >= ?",
|
||||
&format!(
|
||||
"DELETE FROM {}_tree_checkpoints WHERE checkpoint_id >= ?",
|
||||
table_prefix
|
||||
),
|
||||
[u32::from(checkpoint_id)],
|
||||
)
|
||||
.map_err(Either::Right)?;
|
||||
|
||||
conn.execute(
|
||||
"DELETE FROM sapling_tree_checkpoint_marks_removed WHERE checkpoint_id >= ?",
|
||||
[u32::from(checkpoint_id)],
|
||||
)
|
||||
.map_err(Either::Right)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -702,7 +775,8 @@ mod tests {
|
|||
data_file.keep().unwrap();
|
||||
|
||||
init_wallet_db(&mut db_data, None).unwrap();
|
||||
let store = SqliteShardStore::<_, String, 3>::from_connection(db_data.conn).unwrap();
|
||||
let store =
|
||||
SqliteShardStore::<_, String, 3>::from_connection(db_data.conn, "sapling").unwrap();
|
||||
ShardTree::new(store, m)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue