From e5c5fc3b1beb41b5b34f598a6f46176fce93c0ac Mon Sep 17 00:00:00 2001 From: Linden <65407488+thelindat@users.noreply.github.com> Date: Thu, 23 Dec 2021 13:56:21 +1100 Subject: [PATCH] refactor(execute): split into execute and query query refers to the old execute function. preparedStatement renamed to execute. Parse arguments before establishing connection. Utilise response function to determine and return correct values to the export. execute now accepts an array of values when performing a single query, rather than requiring an array within an array. --- src/execute.js | 123 --------------------------------------- src/server/db/execute.js | 59 +++++++++++++++++++ src/server/db/query.js | 27 +++++++++ 3 files changed, 86 insertions(+), 123 deletions(-) delete mode 100644 src/execute.js create mode 100644 src/server/db/execute.js create mode 100644 src/server/db/query.js diff --git a/src/execute.js b/src/execute.js deleted file mode 100644 index 1b3487b..0000000 --- a/src/execute.js +++ /dev/null @@ -1,123 +0,0 @@ -import { pool } from './pool'; -import { parseParameters } from './parser'; -import { slowQueryWarning, debug, resourceName, isolationLevel } from './config'; -import { FormatError } from './errors'; - -let isReady = false; - -const serverReady = async () => { - return new Promise((resolve) => { - const id = setInterval(() => { - if (GetResourceState(resourceName) == 'started') resolve(id); - }, 50); - }).then((id) => { - clearInterval(id); - isReady = true; - }); -}; - -setImmediate(async () => { - try { - await pool.query(isolationLevel); - console.log(`^2Database server connection established!^0`); - } catch (error) { - console.log(`^3Unable to establish a connection to the database! [${error.code}]\n${error.message}^0`); - } -}); - -const execute = async (query, parameters, resource) => { - if (!isReady) await serverReady(); - try { - [query, parameters] = parseParameters(query, parameters); - - ScheduleResourceTick(resourceName); - const startTime = process.hrtime(); - const [rows] = await pool.query(query, parameters); - const executionTime = process.hrtime(startTime)[1] / 1000000; // nanosecond to millisecond - - if (executionTime >= slowQueryWarning || debug) - console.log( - `^3[${debug ? 'DEBUG' : 'WARNING'}] ${resource} took ${executionTime}ms to execute a query! - ${query} ${JSON.stringify(parameters)}^0` - ); - - return rows; - } catch (error) { - console.log( - `^1[ERROR] ${resource} was unable to execute a query! - ${error.message} - ${error.sql || `${query} ${JSON.stringify(parameters)}`}^0` - ); - debug && console.trace(error); - } -}; - -const queryType = (query) => { - switch (query.replace(/\s.*/, '')) { - case 'SELECT': - return 1; - case 'INSERT': - return 2; - case 'UPDATE': - return 3; - case 'DELETE': - return 3; - default: - return false; - } -}; - -const preparedStatement = async (query, parameters, resource) => { - if (!isReady) await serverReady(); - ScheduleResourceTick(resourceName); - const connection = await pool.getConnection(); - try { - if (!Array.isArray(parameters)) - throw new FormatError(`Placeholders were defined, but query received no parameters!`, query); - - if (typeof query !== 'string') throw new FormatError(`Prepared statements must utilise a single query`); - - const type = queryType(query); - if (!type) throw new FormatError(`Prepared statements only accept SELECT, INSERT, UPDATE, and DELETE methods!`); - - const results = []; - let queryCount = parameters.length; - const startTime = process.hrtime(); - - for (let i = 0; i < queryCount; i++) { - const [rows] = await connection.execute(query, parameters[i]); - results[i] = rows && (type === 3 ? rows.affectedRows : type === 2 ? rows.insertId : rows); - } - - const executionTime = process.hrtime(startTime)[1] / 1000000; // nanosecond to millisecond - if (executionTime >= slowQueryWarning || debug) - console.log( - `^3[${debug ? 'DEBUG' : 'WARNING'}] ${resource} took ${executionTime}ms to execute ${ - queryCount > 1 ? queryCount + ' queries' : 'a query' - }! - ${query} ${JSON.stringify(parameters)}^0` - ); - - if (results.length === 1) { - if (type === 1) { - if (results[0][0] && Object.keys(results[0][0]).length === 1) { - return Object.values(results[0][0])[0]; - } - return results[0][0]; - } - return results[0]; - } - return results; - } catch (error) { - console.log( - `^1[ERROR] ${resource} was unable to execute a query! - ${error.message} - ${error.sql || `${query} ${JSON.stringify(parameters)}`}^0` - ); - debug && console.trace(error); - } finally { - connection.release(); - } -}; - -export { execute, preparedStatement }; diff --git a/src/server/db/execute.js b/src/server/db/execute.js new file mode 100644 index 0000000..3278349 --- /dev/null +++ b/src/server/db/execute.js @@ -0,0 +1,59 @@ +const hrtime = require('process').hrtime; +import { mysql_debug, mysql_slow_query_warning } from '../config.js'; +import { isReady, scheduleTick, serverReady, preparedTypes, response } from '../utils.js'; +import pool from './pool.js'; + +export default async (invokingResource, query, parameters, cb) => { + if (!isReady) serverReady(); + const type = preparedTypes(query); + if (!type) throw new Error(`Prepared statements only accept SELECT, INSERT, UPDATE, and DELETE methods!`); + + scheduleTick(); + const connection = await pool.getConnection(); + try { + const queryCount = parameters.length; + let results = []; + let executionTime = hrtime(); + + if (typeof parameters[0] !== 'object') { + const [result] = await connection.execute(query, parameters); + results[0] = response(type, result); + } else { + for (let i = 0; i < queryCount; i++) { + const [result] = await connection.execute(query, parameters[i]); + results[i] = response(type, result); + } + } + + executionTime = hrtime(executionTime)[1] / 1000000; + + if (results.length === 1) { + if (type === 'execute') { + if (results[0][0] && Object.keys(results[0][0]).length === 1) results = Object.values(results[0][0])[0]; + else results = results[0][0]; + } else { + results = results[0]; + } + } + + if (executionTime >= mysql_slow_query_warning || mysql_debug) + console.log( + `^3[${mysql_debug ? 'DEBUG' : 'WARNING'}] ${invokingResource} took ${executionTime}ms to execute ${ + queryCount > 1 ? queryCount + ' queries' : 'a query' + }! + ${query} ${JSON.stringify(parameters)}^0` + ); + + if (cb) { + cb(results); + } else { + return results; + } + } catch (err) { + throw new Error(`${invokingResource} was unable to execute a query! + ${err.message} + ${err.sql || `${query} ${JSON.stringify(parameters)}`}`); + } finally { + connection.release(); + } +}; diff --git a/src/server/db/query.js b/src/server/db/query.js new file mode 100644 index 0000000..0529bc6 --- /dev/null +++ b/src/server/db/query.js @@ -0,0 +1,27 @@ +import { mysql_debug, mysql_slow_query_warning } from '../config.js'; +import { isReady, scheduleTick, serverReady, parseArguments, response } from '../utils.js'; +import pool from './pool.js'; + +export default async (type, invokingResource, query, parameters, cb) => { + if (!isReady) serverReady(); + [query, parameters, cb] = parseArguments(invokingResource, query, parameters, cb); + scheduleTick(); + try { + const [result, _, executionTime] = await pool.query(query, parameters); + + if (executionTime >= mysql_slow_query_warning || mysql_debug) + console.log( + `^3[${mysql_debug ? 'DEBUG' : 'WARNING'}] ${invokingResource} took ${executionTime}ms to execute a query! + ${query} ${JSON.stringify(parameters)}^0` + ); + if (cb) { + cb(response(type, result)); + } else { + return response(type, result); + } + } catch (err) { + throw new Error(`${invokingResource} was unable to execute a query! + ${err.message} + ${err.sql || `${query} ${JSON.stringify(parameters)}`}`); + } +};