From 5f699cc28cf0c3e8793e81778f77ddcbc1db87e3 Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 20 Jun 2020 23:24:34 +0200 Subject: [PATCH] converted most dynamic SQL queries into prepared statement to avoid excessive statement caching --- src/routes/api/autocomplete.js | 6 ++++-- src/routes/api/note_revisions.js | 10 ++++------ src/services/attributes.js | 2 +- src/services/backup.js | 4 +--- src/services/sql.js | 5 +++-- src/services/utils.js | 6 ------ 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/routes/api/autocomplete.js b/src/routes/api/autocomplete.js index f89d25014..59739b6fc 100644 --- a/src/routes/api/autocomplete.js +++ b/src/routes/api/autocomplete.js @@ -33,10 +33,12 @@ function getAutocomplete(req) { function getRecentNotes(activeNoteId) { let extraCondition = ''; + const params = [activeNoteId]; const hoistedNoteId = optionService.getOption('hoistedNoteId'); if (hoistedNoteId !== 'root') { - extraCondition = `AND recent_notes.notePath LIKE '%${utils.sanitizeSql(hoistedNoteId)}%'`; + extraCondition = `AND recent_notes.notePath LIKE ?`; + params.push(hoistedNoteId + '%'); } const recentNotes = repository.getEntities(` @@ -52,7 +54,7 @@ function getRecentNotes(activeNoteId) { ${extraCondition} ORDER BY utcDateCreated DESC - LIMIT 200`, [activeNoteId]); + LIMIT 200`, params); return recentNotes.map(rn => { const title = noteCacheService.getNoteTitleForPath(rn.notePath.split('/')); diff --git a/src/routes/api/note_revisions.js b/src/routes/api/note_revisions.js index 2836a810d..c4b05a2b9 100644 --- a/src/routes/api/note_revisions.js +++ b/src/routes/api/note_revisions.js @@ -119,21 +119,19 @@ function restoreNoteRevision(req) { } function getEditedNotesOnDate(req) { - const date = utils.sanitizeSql(req.params.date); - const notes = repository.getEntities(` SELECT notes.* FROM notes WHERE noteId IN ( SELECT noteId FROM notes - WHERE notes.dateCreated LIKE '${date}%' - OR notes.dateModified LIKE '${date}%' + WHERE notes.dateCreated LIKE :date + OR notes.dateModified LIKE :date UNION ALL SELECT noteId FROM note_revisions - WHERE note_revisions.dateLastEdited LIKE '${date}%' + WHERE note_revisions.dateLastEdited LIKE :date ) ORDER BY isDeleted - LIMIT 50`); + LIMIT 50`, {date: req.params.date + '%'}); for (const note of notes) { const notePath = noteCacheService.getNotePath(note.noteId); diff --git a/src/services/attributes.js b/src/services/attributes.js index 37a76f0de..bb227314c 100644 --- a/src/services/attributes.js +++ b/src/services/attributes.js @@ -97,7 +97,7 @@ function getAttributeNames(type, nameLike) { FROM attributes WHERE isDeleted = 0 AND type = ? - AND name LIKE '%${utils.sanitizeSql(nameLike)}%'`, [type]); + AND name LIKE ?`, [type, '%' + nameLike + '%']); for (const attr of BUILTIN_ATTRIBUTES) { if (attr.type === type && attr.name.toLowerCase().includes(nameLike) && !names.includes(attr.name)) { diff --git a/src/services/backup.js b/src/services/backup.js index 133b13e7b..1b09137a2 100644 --- a/src/services/backup.js +++ b/src/services/backup.js @@ -31,8 +31,6 @@ function periodBackup(optionName, fileName, periodInSeconds) { } } -const COPY_ATTEMPT_COUNT = 50; - async function copyFile(backupFile) { const sql = require('./sql'); @@ -78,7 +76,7 @@ async function anonymize() { // on the other hand builtin/system attrs should not contain any sensitive info const builtinAttrs = attributeService .getBuiltinAttributeNames() - .map(name => "'" + utils.sanitizeSql(name) + "'").join(', '); + .map(name => "'" + name + "'").join(', '); db.prepare(`UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrs})`).run(); db.prepare(`UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrs})`).run(); diff --git a/src/services/sql.js b/src/services/sql.js index c67ddce1f..f1c2d6cfb 100644 --- a/src/services/sql.js +++ b/src/services/sql.js @@ -127,7 +127,8 @@ function getManyRows(query, params) { const questionMarks = curParams.map(() => ":param" + i++).join(","); const curQuery = query.replace(/\?\?\?/g, questionMarks); - results = results.concat(getRows(curQuery, curParamsObj)); + const subResults = dbConnection.prepare(curQuery).all(curParamsObj); + results = results.concat(subResults); } return results; @@ -200,7 +201,7 @@ function wrap(query, func) { const milliseconds = Date.now() - startTimestamp; - if (milliseconds >= 100) { + if (milliseconds >= 20) { if (query.includes("WITH RECURSIVE")) { log.info(`Slow recursive query took ${milliseconds}ms.`); } diff --git a/src/services/utils.js b/src/services/utils.js index decbd8239..2e7c39bf0 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -50,11 +50,6 @@ function isEmptyOrWhitespace(str) { return str === null || str.match(/^ *$/) !== null; } -function sanitizeSql(str) { - // should be improved or usage eliminated - return str.replace(/'/g, "''"); -} - function sanitizeSqlIdentifier(str) { return str.replace(/[^A-Za-z0-9_]/g, ""); } @@ -286,7 +281,6 @@ module.exports = { isElectron, hash, isEmptyOrWhitespace, - sanitizeSql, sanitizeSqlIdentifier, prepareSqlForLike, stopWatch,