Compare commits

...
Author SHA1 Message Date
Calum H. (IMB11) f5a7ce6700 fix: race on renaming instance file 2026-06-29 16:11:40 +01:00
6 changed files with 136 additions and 21 deletions
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n\t\t\t\tUPDATE instance_content_entries\n\t\t\t\tSET file_id = ?, modified_at = ?\n\t\t\t\tWHERE instance_id = ? AND file_id = ?\n\t\t\t\t",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "028eb6e4c6e87388ea7a371bd7b41e2b10c828ab93140f6d9091fbc76e03cdec"
}
@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "\n\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT id\n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM instances\n\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE path = ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t",
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "5363aa346fdead6a331a5a209d0505315b9cbb5b842216db5585cc2f7f739180"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n\t\t\t\tDELETE FROM instance_files\n\t\t\t\tWHERE id = ?\n\t\t\t\t",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "7a912eb3fd9b7da37cce1d8153241aba899e679a6248c8a1de21b7b65155843e"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n\t\tSELECT id\n\t\tFROM instance_files\n\t\tWHERE instance_id = ? AND relative_path = ?\n\t\t",
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false
]
},
"hash": "b4885df7ebd5300533179c0ed98cbca8ca982489597f413638c6af8ccbbe2bfa"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n\t\t\t\tDELETE FROM instance_content_entries\n\t\t\t\tWHERE id IN (\n\t\t\t\t\tSELECT target_entry.id\n\t\t\t\t\tFROM instance_content_entries target_entry\n\t\t\t\t\tWHERE target_entry.instance_id = ?\n\t\t\t\t\t\tAND target_entry.file_id = ?\n\t\t\t\t\t\tAND EXISTS (\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM instance_content_entries source_entry\n\t\t\t\t\t\t\tWHERE source_entry.instance_id = target_entry.instance_id\n\t\t\t\t\t\t\t\tAND source_entry.content_set_id = target_entry.content_set_id\n\t\t\t\t\t\t\t\tAND source_entry.file_id = ?\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "edab0097706c394608dbc8c65670159a6d568cb4d408876d874d218d83bc8caf"
}
@@ -619,6 +619,83 @@ pub(crate) async fn rename_instance_file(
) -> crate::Result<Option<InstanceFile>> {
let enabled = i64::from(enabled);
let modified_at = Utc::now().timestamp();
let mut tx = pool.begin().await?;
let source_id = sqlx::query_scalar!(
"
SELECT id
FROM instance_files
WHERE instance_id = ? AND relative_path = ?
",
instance_id,
old_relative_path,
)
.fetch_optional(&mut *tx)
.await?;
let target_id = sqlx::query_scalar!(
"
SELECT id
FROM instance_files
WHERE instance_id = ? AND relative_path = ?
",
instance_id,
new_relative_path,
)
.fetch_optional(&mut *tx)
.await?;
if let (Some(source_id), Some(target_id)) =
(source_id.as_deref(), target_id.as_deref())
&& source_id != target_id
{
sqlx::query!(
"
DELETE FROM instance_content_entries
WHERE id IN (
SELECT target_entry.id
FROM instance_content_entries target_entry
WHERE target_entry.instance_id = ?
AND target_entry.file_id = ?
AND EXISTS (
SELECT 1
FROM instance_content_entries source_entry
WHERE source_entry.instance_id = target_entry.instance_id
AND source_entry.content_set_id = target_entry.content_set_id
AND source_entry.file_id = ?
)
)
",
instance_id,
target_id,
source_id,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"
UPDATE instance_content_entries
SET file_id = ?, modified_at = ?
WHERE instance_id = ? AND file_id = ?
",
source_id,
modified_at,
instance_id,
target_id,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"
DELETE FROM instance_files
WHERE id = ?
",
target_id,
)
.execute(&mut *tx)
.await?;
}
sqlx::query!(
"
@@ -638,9 +715,11 @@ pub(crate) async fn rename_instance_file(
instance_id,
old_relative_path,
)
.execute(pool)
.execute(&mut *tx)
.await?;
tx.commit().await?;
get_instance_file_by_relative_path(instance_id, new_relative_path, pool)
.await
}