You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-02 22:49:09 +02:00
All: Handle deletion of resources that are not linked to any note
This commit is contained in:
@@ -3,14 +3,13 @@ const { promiseChain } = require('lib/promise-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
class Database {
|
||||
|
||||
constructor(driver) {
|
||||
this.debugMode_ = false;
|
||||
this.driver_ = driver;
|
||||
this.inTransaction_ = false;
|
||||
|
||||
this.logger_ = new Logger();
|
||||
this.logExcludedQueryTypes_ = [];
|
||||
}
|
||||
@@ -113,29 +112,20 @@ class Database {
|
||||
return;
|
||||
}
|
||||
|
||||
// There can be only one transaction running at a time so queue
|
||||
// any new transaction here.
|
||||
if (this.inTransaction_) {
|
||||
while (true) {
|
||||
await time.msleep(100);
|
||||
if (!this.inTransaction_) {
|
||||
this.inTransaction_ = true;
|
||||
break;
|
||||
}
|
||||
// There can be only one transaction running at a time so use a mutex
|
||||
const release = await Database.batchTransactionMutex_.acquire();
|
||||
|
||||
try {
|
||||
queries.splice(0, 0, 'BEGIN TRANSACTION');
|
||||
queries.push('COMMIT'); // Note: ROLLBACK is currently not supported
|
||||
|
||||
for (let i = 0; i < queries.length; i++) {
|
||||
let query = this.wrapQuery(queries[i]);
|
||||
await this.exec(query.sql, query.params);
|
||||
}
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
|
||||
this.inTransaction_ = true;
|
||||
|
||||
queries.splice(0, 0, 'BEGIN TRANSACTION');
|
||||
queries.push('COMMIT'); // Note: ROLLBACK is currently not supported
|
||||
|
||||
for (let i = 0; i < queries.length; i++) {
|
||||
let query = this.wrapQuery(queries[i]);
|
||||
await this.exec(query.sql, query.params);
|
||||
}
|
||||
|
||||
this.inTransaction_ = false;
|
||||
}
|
||||
|
||||
static enumId(type, s) {
|
||||
@@ -310,6 +300,8 @@ class Database {
|
||||
|
||||
}
|
||||
|
||||
Database.batchTransactionMutex_ = new Mutex();
|
||||
|
||||
Database.TYPE_UNKNOWN = 0;
|
||||
Database.TYPE_INT = 1;
|
||||
Database.TYPE_TEXT = 2;
|
||||
|
||||
@@ -336,6 +336,9 @@ class JoplinDatabase extends Database {
|
||||
}
|
||||
|
||||
if (targetVersion == 11) {
|
||||
// This trick was needed because Electron Builder incorrectly released a dev branch containing v10 as it was
|
||||
// still being developed, and the db schema was not final at that time. So this v11 was created to
|
||||
// make sure any invalid db schema that was accidentally created was deleted and recreated.
|
||||
queries.push('DROP TABLE item_changes');
|
||||
queries.push('DROP TABLE note_resources');
|
||||
upgradeVersion10();
|
||||
|
||||
@@ -31,7 +31,17 @@ class NoteResource extends BaseModel {
|
||||
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: [noteId, notProcessedResourceIds[i], 1, Date.now()] });
|
||||
}
|
||||
|
||||
await this.db().transactionExecBatch(queries);
|
||||
await this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
static async addOrphanedResources() {
|
||||
const missingResources = await this.db().selectAll('SELECT id FROM resources WHERE id NOT IN (SELECT DISTINCT resource_id FROM note_resources)');
|
||||
const queries = [];
|
||||
for (let i = 0; i < missingResources.length; i++) {
|
||||
const id = missingResources[i];
|
||||
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: ["", id, 0, Date.now()] });
|
||||
}
|
||||
await this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
static async remove(noteId) {
|
||||
@@ -41,10 +51,20 @@ class NoteResource extends BaseModel {
|
||||
static async orphanResources(expiryDelay = null) {
|
||||
if (expiryDelay === null) expiryDelay = 1000 * 60 * 60 * 24;
|
||||
const cutOffTime = Date.now() - expiryDelay;
|
||||
const output = await this.modelSelectAll('SELECT DISTINCT resource_id FROM note_resources WHERE is_associated = 0 AND last_seen_time < ?', [cutOffTime]);
|
||||
const output = await this.modelSelectAll(`
|
||||
SELECT resource_id, sum(is_associated)
|
||||
FROM note_resources
|
||||
GROUP BY resource_id
|
||||
HAVING sum(is_associated) <= 0
|
||||
AND last_seen_time < ?
|
||||
`, [cutOffTime]);
|
||||
return output.map(r => r.resource_id);
|
||||
}
|
||||
|
||||
static async deleteByResource(resourceId) {
|
||||
await this.db().exec('DELETE FROM note_resources WHERE resource_id = ?', [resourceId]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NoteResource;
|
||||
@@ -1,5 +1,6 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const NoteResource = require('lib/models/NoteResource.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
@@ -152,7 +153,8 @@ class Resource extends BaseItem {
|
||||
const resource = await Resource.load(id);
|
||||
const path = Resource.fullPath(resource);
|
||||
await this.fsDriver().remove(path);
|
||||
await super.batchDelete([id], options)
|
||||
await super.batchDelete([id], options);
|
||||
await NoteResource.deleteByResource(id); // Clean up note/resource relationships
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ class ResourceService extends BaseService {
|
||||
await ItemChange.db().exec('DELETE FROM item_changes WHERE id <= ?', [lastId]);
|
||||
}
|
||||
|
||||
await NoteResource.addOrphanedResources();
|
||||
|
||||
this.logger().info('ResourceService::indexNoteResources: Completed');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user