mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 00:47:00 +01:00
feat(core): node rows name their parent by local id
Schema 40 adds nodes.parent_lid, backfilled from parent_uid. An upsert and a new row's stub keep it in step, a folder's new row links the rows stored before it, and a row merged into a landing folder hands its children over. A restart places each node under its parent's row as it is now, so a child whose stored node still names a landed folder's placeholder lands under the folder's real uid.
This commit is contained in:
1 parent
ae6650f7e0
commit
9b2b9f24c5
5 files changed
+159
-20
No files matched your search
@@ -529,19 +529,30 @@ impl Db {
|
||||
}
|
||||
|
||||
/// Load every persisted node for cold-start hydration of the `State` maps.
|
||||
///
|
||||
/// Each node names its parent as the parent's row has it now, found by
|
||||
/// local id: a folder that landed has its new uid there, while the node
|
||||
/// stored below it may still hold the placeholder.
|
||||
pub fn load_all(&self) -> Result<Vec<StoredNode>> {
|
||||
let conn = self.read();
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT node_json, listed, lid FROM nodes WHERE node_json IS NOT NULL")?;
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT n.node_json, n.listed, n.lid, p.uid
|
||||
FROM nodes n LEFT JOIN nodes p ON p.lid = n.parent_lid
|
||||
WHERE n.node_json IS NOT NULL",
|
||||
)?;
|
||||
let rows = stmt.query_map([], |row| {
|
||||
let json: String = row.get(0)?;
|
||||
let listed: i64 = row.get(1)?;
|
||||
Ok((json, listed != 0, row.get(2)?))
|
||||
let parent: Option<String> = row.get(3)?;
|
||||
Ok((json, listed != 0, row.get(2)?, parent))
|
||||
})?;
|
||||
let mut out = Vec::new();
|
||||
for row in rows {
|
||||
let (json, listed, lid) = row?;
|
||||
let node: Node = serde_json::from_str(&json)?;
|
||||
let (json, listed, lid, parent) = row?;
|
||||
let mut node: Node = serde_json::from_str(&json)?;
|
||||
if let Some(parent) = parent.as_deref().and_then(parse_node_uid) {
|
||||
node.parent_uid = Some(parent);
|
||||
}
|
||||
out.push(StoredNode { node, listed, lid });
|
||||
}
|
||||
Ok(out)
|
||||
@@ -602,8 +613,8 @@ impl Db {
|
||||
{
|
||||
let mut known = tx.prepare_cached("SELECT lid FROM nodes WHERE uid = ?1")?;
|
||||
let mut stub = tx.prepare_cached(
|
||||
"INSERT INTO nodes (uid, parent_uid, name, is_dir, mtime, trashed)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||
"INSERT INTO nodes (uid, parent_uid, name, is_dir, mtime, trashed, parent_lid)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, (SELECT lid FROM nodes WHERE uid = ?2))",
|
||||
)?;
|
||||
for node in nodes {
|
||||
let uid = node.uid.to_string();
|
||||
@@ -618,7 +629,9 @@ impl Db {
|
||||
node.modification_time,
|
||||
node.trashed as i64,
|
||||
])?;
|
||||
tx.last_insert_rowid()
|
||||
let lid = tx.last_insert_rowid();
|
||||
adopt_children_tx(&tx, &uid, lid)?;
|
||||
lid
|
||||
}
|
||||
};
|
||||
lids.push(lid);
|
||||
@@ -791,6 +804,10 @@ pub(super) fn adopt_placeholder_row_tx(
|
||||
"UPDATE pending_op SET parent_lid = ?2 WHERE parent_lid = ?1",
|
||||
params![known_lid, lid],
|
||||
)?;
|
||||
tx.execute(
|
||||
"UPDATE nodes SET parent_lid = ?2 WHERE parent_lid = ?1",
|
||||
params![known_lid, lid],
|
||||
)?;
|
||||
tx.execute("DELETE FROM nodes WHERE lid = ?1", params![known_lid])?;
|
||||
tx.execute("DELETE FROM nodes_fts WHERE rowid = ?1", params![known_lid])?;
|
||||
}
|
||||
@@ -807,6 +824,16 @@ pub(super) fn adopt_placeholder_row_tx(
|
||||
upsert_node_tx(tx, &node)
|
||||
}
|
||||
|
||||
/// Link the rows that name `uid` as their parent to its new row `lid`: they
|
||||
/// were stored before it, or under a row of the same uid since dropped.
|
||||
fn adopt_children_tx(tx: &Transaction<'_>, uid: &str, lid: i64) -> Result<()> {
|
||||
tx.execute(
|
||||
"UPDATE nodes SET parent_lid = ?2 WHERE parent_uid = ?1 AND parent_lid IS NOT ?2",
|
||||
params![uid, lid],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn upsert_node_tx(tx: &Transaction<'_>, node: &Node) -> Result<()> {
|
||||
let json = serde_json::to_string(node)?;
|
||||
let uid = node.uid.to_string();
|
||||
@@ -831,26 +858,31 @@ fn upsert_node_tx(tx: &Transaction<'_>, node: &Node) -> Result<()> {
|
||||
// where resolving it from scratch is a recursive walk to the root. A node
|
||||
// whose parent is not cached (a device folder's root lives in `device`, never
|
||||
// in `nodes`) starts a path of its own, which is what the walk did too.
|
||||
let path = match &parent_uid {
|
||||
None => String::new(),
|
||||
let (parent_lid, path) = match &parent_uid {
|
||||
None => (None, String::new()),
|
||||
Some(parent) => {
|
||||
let parent_path: Option<Option<String>> = tx
|
||||
let parent_row: Option<(i64, Option<String>)> = tx
|
||||
.query_row(
|
||||
"SELECT path FROM nodes WHERE uid = ?1",
|
||||
"SELECT lid, path FROM nodes WHERE uid = ?1",
|
||||
params![parent],
|
||||
|row| row.get(0),
|
||||
|row| Ok((row.get(0)?, row.get(1)?)),
|
||||
)
|
||||
.optional()?;
|
||||
join_path(parent_path.flatten().as_deref().unwrap_or(""), &node.name)
|
||||
let (lid, parent_path) = parent_row.unzip();
|
||||
(
|
||||
lid,
|
||||
join_path(parent_path.flatten().as_deref().unwrap_or(""), &node.name),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
tx.execute(
|
||||
"INSERT INTO nodes
|
||||
(uid, parent_uid, name, is_dir, size, mtime, trashed, node_json, path)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
|
||||
(uid, parent_uid, name, is_dir, size, mtime, trashed, node_json, path, parent_lid)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
|
||||
ON CONFLICT(uid) DO UPDATE SET
|
||||
parent_uid = excluded.parent_uid,
|
||||
parent_lid = excluded.parent_lid,
|
||||
name = excluded.name,
|
||||
is_dir = excluded.is_dir,
|
||||
size = excluded.size,
|
||||
@@ -868,6 +900,7 @@ fn upsert_node_tx(tx: &Transaction<'_>, node: &Node) -> Result<()> {
|
||||
node.trashed as i64,
|
||||
json,
|
||||
path,
|
||||
parent_lid,
|
||||
],
|
||||
)?;
|
||||
let rowid: i64 = tx.query_row(
|
||||
@@ -875,6 +908,9 @@ fn upsert_node_tx(tx: &Transaction<'_>, node: &Node) -> Result<()> {
|
||||
params![uid],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
if prior.is_none() {
|
||||
adopt_children_tx(tx, &uid, rowid)?;
|
||||
}
|
||||
|
||||
// FTS5 has no UPSERT, so the node's own row is always replaced.
|
||||
let indexable = node_is_indexable_tx(tx, &uid)?;
|
||||
|
||||
Reference in new issue
Block a user