From 8d401629c5a73b90ad03a368b68d50d2d520c0bc Mon Sep 17 00:00:00 2001 From: Georgii Petrov Date: Mon, 3 Jun 2024 11:30:04 +0300 Subject: [PATCH 1/2] [fix] Added support of MySql ssl connection --- Common/config/default.json | 3 +- .../databaseConnectors/mysqlConnector.js | 202 +++++++++--------- 2 files changed, 105 insertions(+), 100 deletions(-) diff --git a/Common/config/default.json b/Common/config/default.json index 12f9f3d05..e1ffea52b 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -230,7 +230,8 @@ "pool": { "idleTimeoutMillis": 30000 } - } + }, + "mysqlExtraOptions": {} }, "redis": { "name": "redis", diff --git a/DocService/sources/databaseConnectors/mysqlConnector.js b/DocService/sources/databaseConnectors/mysqlConnector.js index 96535bd48..6078d9446 100644 --- a/DocService/sources/databaseConnectors/mysqlConnector.js +++ b/DocService/sources/databaseConnectors/mysqlConnector.js @@ -32,14 +32,14 @@ 'use strict'; -const mysql = require('mysql2'); +const mysql = require('mysql2/promise'); const connectorUtilities = require('./connectorUtilities'); const config = require('config'); const configSql = config.get('services.CoAuthoring.sql'); -const cfgTableResult = config.get('services.CoAuthoring.sql.tableResult'); +const cfgTableResult = configSql.get('tableResult'); -const pool = mysql.createPool({ +const connectionConfiguration = { host : configSql.get('dbHost'), port : parseInt(configSql.get('dbPort')), user : configSql.get('dbUser'), @@ -49,120 +49,124 @@ const pool = mysql.createPool({ connectionLimit : configSql.get('connectionlimit'), timezone : 'Z', flags : '-FOUND_ROWS' -}); +}; -function sqlQuery(ctx, sqlCommand, callbackFunction, opt_noModifyRes = false, opt_noLog = false, opt_values = []) { - pool.getConnection(function(connectionError, connection) { - if (connectionError) { - if (!opt_noLog) { - ctx.logger.error('pool.getConnection error: %s', connectionError); - } +const additionalOptions = configSql.get('mysqlExtraOptions'); +const configuration = Object.assign({}, connectionConfiguration, additionalOptions); + +let pool = null; - callbackFunction?.(connectionError, null); +function sqlQuery(ctx, sqlCommand, callbackFunction, opt_noModifyRes = false, opt_noLog = false, opt_values = []) { + return executeQuery(ctx, sqlCommand, opt_values, opt_noModifyRes, opt_noLog).then( + result => callbackFunction?.(null, result), + error => callbackFunction?.(error) + ); +} - return; +async function executeQuery(ctx, sqlCommand, values = [], noModifyRes = false, noLog = false) { + let connection = null; + try { + if (!pool) { + pool = mysql.createPool(configuration); } - let queryCallback = function (error, result) { - connection.release(); - if (error && !opt_noLog) { - ctx.logger.error('_______________________error______________________'); - ctx.logger.error('sqlQuery: %s sqlCommand: %s', error.code, sqlCommand); - ctx.logger.error(error); - ctx.logger.error('_____________________end_error____________________'); - } + connection = await pool.getConnection(); - let output; - if (!opt_noModifyRes) { - output = result?.affectedRows ? { affectedRows: result.affectedRows } : result; - } else { - output = result; - } + const result = await connection.query(sqlCommand, values); - output = output ?? { rows: [], affectedRows: 0 }; + let output; + if (!noModifyRes) { + output = result[0]?.affectedRows ? { affectedRows: result[0].affectedRows } : result[0]; + } else { + output = result[0]; + } - callbackFunction?.(error, output); - }; + return output ?? { rows: [], affectedRows: 0 }; + } catch (error) { + if (!noLog) { + ctx.logger.error(`sqlQuery() error while executing query: ${sqlCommand}\n${error.stack}`); + } - connection.query(sqlCommand, opt_values, queryCallback); - }); + throw error; + } finally { + if (connection) { + try { + // Put the connection back in the pool + connection.release(); + } catch (error) { + if (!noLog) { + ctx.logger.error(`connection.release() error while executing query: ${sqlCommand}\n${error.stack}`); + } + } + } + } } function closePool() { - return new Promise((resolve, reject) => { - pool.end((error) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); + return pool?.end(); } -function addSqlParameter(val, values) { - values.push(val); +function addSqlParameter(parameter, accumulatedArray) { + accumulatedArray.push(parameter); return '?'; } -function concatParams(val1, val2) { - return `CONCAT(COALESCE(${val1}, ''), COALESCE(${val2}, ''))`; +function concatParams(firstParameter, secondParameter) { + return `CONCAT(COALESCE(${firstParameter}, ''), COALESCE(${secondParameter}, ''))`; } -function upsert(ctx, task) { - return new Promise(function(resolve, reject) { - task.completeDefaults(); - let dateNow = new Date(); - let values = []; - let cbInsert = task.callback; - if (task.callback) { - let userCallback = new connectorUtilities.UserCallback(); - userCallback.fromValues(task.userIndex, task.callback); - cbInsert = userCallback.toSQLInsert(); - } - let p0 = addSqlParameter(task.tenant, values); - let p1 = addSqlParameter(task.key, values); - let p2 = addSqlParameter(task.status, values); - let p3 = addSqlParameter(task.statusInfo, values); - let p4 = addSqlParameter(dateNow, values); - let p5 = addSqlParameter(task.userIndex, values); - let p6 = addSqlParameter(task.changeId, values); - let p7 = addSqlParameter(cbInsert, values); - let p8 = addSqlParameter(task.baseurl, values); - let p9 = addSqlParameter(dateNow, values); - var sqlCommand = `INSERT INTO ${cfgTableResult} (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)`+ - ` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) ON DUPLICATE KEY UPDATE` + - ` last_open_date = ${p9}`; - if (task.callback) { - let p10 = addSqlParameter(JSON.stringify(task.callback), values); - sqlCommand += `, callback = CONCAT(callback , '${connectorUtilities.UserCallback.prototype.delimiter}{"userIndex":' , (user_index + 1) , ',"callback":', ${p10}, '}')`; - } - if (task.baseurl) { - let p11 = addSqlParameter(task.baseurl, values); - sqlCommand += `, baseurl = ${p11}`; - } - - sqlCommand += ', user_index = LAST_INSERT_ID(user_index + 1);'; - - sqlQuery(ctx, sqlCommand, function(error, result) { - if (error) { - reject(error); - } else { - const insertId = result.affectedRows === 1 ? task.userIndex : result.insertId; - //if CLIENT_FOUND_ROWS don't specify 1 row is inserted , 2 row is updated, and 0 row is set to its current values - //http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html - const isInsert = result.affectedRows === 1; - - resolve({ isInsert, insertId }); - } - }, true, false, values); - }); +async function upsert(ctx, task) { + task.completeDefaults(); + const dateNow = new Date(); + + let cbInsert = task.callback; + if (task.callback) { + const userCallback = new connectorUtilities.UserCallback(); + userCallback.fromValues(task.userIndex, task.callback); + cbInsert = userCallback.toSQLInsert(); + } + + const values = []; + const valuesPlaceholder = [ + addSqlParameter(task.tenant, values), + addSqlParameter(task.key, values), + addSqlParameter(task.status, values), + addSqlParameter(task.statusInfo, values), + addSqlParameter(dateNow, values), + addSqlParameter(task.userIndex, values), + addSqlParameter(task.changeId, values), + addSqlParameter(cbInsert, values), + addSqlParameter(task.baseurl, values) + ]; + + let updateStatement = `last_open_date = ${addSqlParameter(dateNow, values)}`; + if (task.callback) { + let callbackPlaceholder = addSqlParameter(JSON.stringify(task.callback), values); + updateStatement += `, callback = CONCAT(callback , '${connectorUtilities.UserCallback.prototype.delimiter}{"userIndex":' , (user_index + 1) , ',"callback":', ${callbackPlaceholder}, '}')`; + } + + if (task.baseurl) { + let baseUrlPlaceholder = addSqlParameter(task.baseurl, values); + updateStatement += `, baseurl = ${baseUrlPlaceholder}`; + } + + updateStatement += ', user_index = LAST_INSERT_ID(user_index + 1);'; + + const sqlCommand = `INSERT INTO ${cfgTableResult} (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl) `+ + `VALUES (${valuesPlaceholder.join(', ')}) ` + + `ON DUPLICATE KEY UPDATE ${updateStatement}`; + + const result = await executeQuery(ctx, sqlCommand, values, true); + const insertId = result.affectedRows === 1 ? task.userIndex : result.insertId; + //if CLIENT_FOUND_ROWS don't specify 1 row is inserted , 2 row is updated, and 0 row is set to its current values + //http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html + const isInsert = result.affectedRows === 1; + + return { isInsert, insertId }; } -module.exports = { - sqlQuery, - closePool, - addSqlParameter, - concatParams, - upsert -} \ No newline at end of file +module.exports.sqlQuery = sqlQuery; +module.exports.closePool = closePool; +module.exports.addSqlParameter = addSqlParameter; +module.exports.concatParams = concatParams; +module.exports.upsert = upsert; From d779c08027ce3b811b191c3419d5669783d536ca Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Mon, 3 Jun 2024 13:07:33 +0300 Subject: [PATCH 2/2] [fix] Make closePool async; Bump nodejs version for mysql test; For bug 68194 --- .github/workflows/mysqlDatabaseTests.yml | 2 +- .../sources/databaseConnectors/mysqlConnector.js | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/mysqlDatabaseTests.yml b/.github/workflows/mysqlDatabaseTests.yml index 3d4e8f188..c8e424e63 100644 --- a/.github/workflows/mysqlDatabaseTests.yml +++ b/.github/workflows/mysqlDatabaseTests.yml @@ -21,7 +21,7 @@ jobs: - name: Caching dependencies uses: actions/setup-node@v3 with: - node-version: '14' + node-version: '16' cache: 'npm' cache-dependency-path: | ./npm-shrinkwrap.json diff --git a/DocService/sources/databaseConnectors/mysqlConnector.js b/DocService/sources/databaseConnectors/mysqlConnector.js index 6078d9446..f295588ce 100644 --- a/DocService/sources/databaseConnectors/mysqlConnector.js +++ b/DocService/sources/databaseConnectors/mysqlConnector.js @@ -54,7 +54,7 @@ const connectionConfiguration = { const additionalOptions = configSql.get('mysqlExtraOptions'); const configuration = Object.assign({}, connectionConfiguration, additionalOptions); -let pool = null; +let pool = mysql.createPool(configuration); function sqlQuery(ctx, sqlCommand, callbackFunction, opt_noModifyRes = false, opt_noLog = false, opt_values = []) { return executeQuery(ctx, sqlCommand, opt_values, opt_noModifyRes, opt_noLog).then( @@ -66,10 +66,6 @@ function sqlQuery(ctx, sqlCommand, callbackFunction, opt_noModifyRes = false, op async function executeQuery(ctx, sqlCommand, values = [], noModifyRes = false, noLog = false) { let connection = null; try { - if (!pool) { - pool = mysql.createPool(configuration); - } - connection = await pool.getConnection(); const result = await connection.query(sqlCommand, values); @@ -102,8 +98,8 @@ async function executeQuery(ctx, sqlCommand, values = [], noModifyRes = false, n } } -function closePool() { - return pool?.end(); +async function closePool() { + return await pool.end(); } function addSqlParameter(parameter, accumulatedArray) {