From 63b642b2511207f435546802e197b6842f5c7aca Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Mon, 13 Jul 2026 13:30:52 +0800 Subject: [PATCH] fix(storage): safely batch folder deletion binds --- src/services/storage-folder-repo.ts | 23 +++++++++++++---------- src/services/storage.ts | 9 +++++++-- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/services/storage-folder-repo.ts b/src/services/storage-folder-repo.ts index 72dd251..254f343 100644 --- a/src/services/storage-folder-repo.ts +++ b/src/services/storage-folder-repo.ts @@ -68,20 +68,22 @@ export async function bulkDeleteFolders( db: D1Database, userId: string, ids: string[], - sqlChunkSize: (fixedBindCount: number) => number, + sqlChunkSize: (fixedBindCount: number, bindCountPerItem?: number) => number, updateRevisionDate: (userId: string) => Promise ): Promise { const uniqueIds = Array.from(new Set(ids.map((id) => String(id || '').trim()).filter(Boolean))); if (!uniqueIds.length) return null; const now = new Date().toISOString(); - const chunkSize = sqlChunkSize(2); + // Each folder ID is bound in all three compatibility predicates below. + const chunkSize = sqlChunkSize(2, 3); + const statements: D1PreparedStatement[] = []; for (let i = 0; i < uniqueIds.length; i += chunkSize) { const chunk = uniqueIds.slice(i, i + chunkSize); const placeholders = chunk.map(() => '?').join(','); - await db - .prepare( + statements.push( + db.prepare( `UPDATE ciphers SET folder_id = NULL, updated_at = ?, data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate') @@ -93,14 +95,15 @@ export async function bulkDeleteFolders( )` ) .bind(now, userId, ...chunk, ...chunk, ...chunk) - .run(); - - await db - .prepare(`DELETE FROM folders WHERE user_id = ? AND id IN (${placeholders})`) - .bind(userId, ...chunk) - .run(); + ); + statements.push( + db.prepare(`DELETE FROM folders WHERE user_id = ? AND id IN (${placeholders})`) + .bind(userId, ...chunk) + ); } + await db.batch(statements); + return updateRevisionDate(userId); } diff --git a/src/services/storage.ts b/src/services/storage.ts index a4d9647..fd21b41 100644 --- a/src/services/storage.ts +++ b/src/services/storage.ts @@ -209,10 +209,15 @@ export class StorageService { return REQUIRED_SCHEMA_TABLES.every((table) => found.has(table)); } - private sqlChunkSize(fixedBindCount: number): number { + private sqlChunkSize(fixedBindCount: number, bindCountPerItem = 1): number { + const safeFixedBindCount = Math.max(0, Math.floor(fixedBindCount)); + const safeBindCountPerItem = Math.max(1, Math.floor(bindCountPerItem)); return Math.max( 1, - Math.min(LIMITS.performance.bulkMoveChunkSize, StorageService.MAX_D1_SQL_VARIABLES - fixedBindCount) + Math.min( + LIMITS.performance.bulkMoveChunkSize, + Math.floor((StorageService.MAX_D1_SQL_VARIABLES - safeFixedBindCount) / safeBindCountPerItem) + ) ); }