diff --git a/src/public/stylesheets/share.css b/src/public/stylesheets/share.css index 084056353..4adf7539b 100644 --- a/src/public/stylesheets/share.css +++ b/src/public/stylesheets/share.css @@ -43,7 +43,7 @@ body { padding: 20px; } -.type-image img { +img { max-width: 100%; } diff --git a/src/services/sql.js b/src/services/sql.js index ac07ad966..76ebb52ef 100644 --- a/src/services/sql.js +++ b/src/services/sql.js @@ -2,6 +2,8 @@ /** * @module sql + * + * TODO: some methods (like getValue()) could use raw rows */ const log = require('./log'); @@ -89,13 +91,7 @@ function getRowOrNull(query, params = []) { } function getValue(query, params = []) { - const row = getRowOrNull(query, params); - - if (!row) { - return null; - } - - return row[Object.keys(row)[0]]; + return wrap(query, s => s.pluck().get(params)); } // smaller values can result in better performance due to better usage of statement cache @@ -144,32 +140,17 @@ function iterateRows(query, params = []) { function getMap(query, params = []) { const map = {}; - const results = getRows(query, params); + const results = getRawRows(query, params); for (const row of results) { - const keys = Object.keys(row); - - map[row[keys[0]]] = row[keys[1]]; + map[row[0]] = row[1]; } return map; } function getColumn(query, params = []) { - const list = []; - const result = getRows(query, params); - - if (result.length === 0) { - return list; - } - - const key = Object.keys(result[0])[0]; - - for (const row of result) { - list.push(row[key]); - } - - return list; + return wrap(query, s => s.pluck().all(params)); } function execute(query, params = []) { diff --git a/src/share/shaca/shaca_loader.js b/src/share/shaca/shaca_loader.js index 39e541809..4f849eaf8 100644 --- a/src/share/shaca/shaca_loader.js +++ b/src/share/shaca/shaca_loader.js @@ -34,15 +34,28 @@ function load() { const noteIdStr = noteIds.map(noteId => `'${noteId}'`).join(","); - for (const row of sql.getRawRows(`SELECT noteId, title, type, mime, utcDateModified FROM notes WHERE isDeleted = 0 AND noteId IN (${noteIdStr})`)) { + const rawNoteRows = sql.getRawRows(` + SELECT noteId, title, type, mime, utcDateModified + FROM notes + WHERE isDeleted = 0 + AND noteId IN (${noteIdStr})`); + + for (const row of rawNoteRows) { new Note(row); } - for (const row of sql.getRawRows(`SELECT branchId, noteId, parentNoteId, prefix, isExpanded, utcDateModified FROM branches WHERE isDeleted = 0 AND parentNoteId IN (${noteIdStr}) ORDER BY notePosition`)) { + const rawBranchRows = sql.getRawRows(` + SELECT branchId, noteId, parentNoteId, prefix, isExpanded, utcDateModified + FROM branches + WHERE isDeleted = 0 + AND parentNoteId IN (${noteIdStr}) + ORDER BY notePosition`); + + for (const row of rawBranchRows) { new Branch(row); } - const attributes = sql.getRawRows(` + const rawAttributeRows = sql.getRawRows(` SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified FROM attributes WHERE isDeleted = 0 @@ -52,13 +65,13 @@ function load() { OR (type = 'relation' AND name IN ('imageLink', 'template', 'shareCss')) )`, []); - for (const row of attributes) { + for (const row of rawAttributeRows) { new Attribute(row); } shaca.loaded = true; - log.info(`Shaca load took ${Date.now() - start}ms`); + log.info(`Shaca loaded ${rawNoteRows.length} notes, ${rawBranchRows.length} branches, ${rawAttributeRows.length} attributes took ${Date.now() - start}ms`); } function ensureLoad() { diff --git a/src/share/sql.js b/src/share/sql.js index 49849da76..485c87921 100644 --- a/src/share/sql.js +++ b/src/share/sql.js @@ -1,6 +1,5 @@ "use strict"; -const log = require('../services/log'); const Database = require('better-sqlite3'); const dataDir = require('../services/data_dir'); @@ -16,152 +15,20 @@ const dbConnection = new Database(dataDir.DOCUMENT_PATH, { readonly: true }); }); }); -const statementCache = {}; - -function stmt(sql) { - if (!(sql in statementCache)) { - statementCache[sql] = dbConnection.prepare(sql); - } - - return statementCache[sql]; +function getRawRows(query, params = []) { + return dbConnection.prepare(query).raw().all(params); } function getRow(query, params = []) { - return wrap(query, s => s.get(params)); -} - -function getRowOrNull(query, params = []) { - const all = getRows(query, params); - - return all.length > 0 ? all[0] : null; -} - -function getValue(query, params = []) { - const row = getRowOrNull(query, params); - - if (!row) { - return null; - } - - return row[Object.keys(row)[0]]; -} - -// smaller values can result in better performance due to better usage of statement cache -const PARAM_LIMIT = 100; - -function getManyRows(query, params) { - let results = []; - - while (params.length > 0) { - const curParams = params.slice(0, Math.min(params.length, PARAM_LIMIT)); - params = params.slice(curParams.length); - - const curParamsObj = {}; - - let j = 1; - for (const param of curParams) { - curParamsObj['param' + j++] = param; - } - - let i = 1; - const questionMarks = curParams.map(() => ":param" + i++).join(","); - const curQuery = query.replace(/\?\?\?/g, questionMarks); - - const statement = curParams.length === PARAM_LIMIT - ? stmt(curQuery) - : dbConnection.prepare(curQuery); - - const subResults = statement.all(curParamsObj); - results = results.concat(subResults); - } - - return results; -} - -function getRows(query, params = []) { - return wrap(query, s => s.all(params)); -} - -function getRawRows(query, params = []) { - return wrap(query, s => s.raw().all(params)); -} - -function iterateRows(query, params = []) { - return stmt(query).iterate(params); -} - -function getMap(query, params = []) { - const map = {}; - const results = getRows(query, params); - - for (const row of results) { - const keys = Object.keys(row); - - map[row[keys[0]]] = row[keys[1]]; - } - - return map; + return dbConnection.prepare(query).get(params); } function getColumn(query, params = []) { - const list = []; - const result = getRows(query, params); - - if (result.length === 0) { - return list; - } - - const key = Object.keys(result[0])[0]; - - for (const row of result) { - list.push(row[key]); - } - - return list; -} - -function wrap(query, func) { - const startTimestamp = Date.now(); - let result; - - try { - result = func(stmt(query)); - } - catch (e) { - if (e.message.includes("The database connection is not open")) { - // this often happens on killing the app which puts these alerts in front of user - // in these cases error should be simply ignored. - console.log(e.message); - - return null - } - - throw e; - } - - const milliseconds = Date.now() - startTimestamp; - - if (milliseconds >= 20) { - if (query.includes("WITH RECURSIVE")) { - log.info(`Slow recursive query took ${milliseconds}ms.`); - } - else { - log.info(`Slow query took ${milliseconds}ms: ${query.trim().replace(/\s+/g, " ")}`); - } - } - - return result; + return dbConnection.prepare(query).pluck().all(params); } module.exports = { - dbConnection, - getValue, - getRow, - getRowOrNull, - getRows, getRawRows, - iterateRows, - getManyRows, - getMap, + getRow, getColumn };