181 lines
6.5 KiB
JavaScript
181 lines
6.5 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2024
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const mysql = require('mysql2/promise');
|
|
const connectorUtilities = require('./connectorUtilities');
|
|
const config = require('config');
|
|
|
|
const configSql = config.get('services.CoAuthoring.sql');
|
|
const cfgTableResult = configSql.get('tableResult');
|
|
|
|
const connectionConfiguration = {
|
|
host: configSql.get('dbHost'),
|
|
port: parseInt(configSql.get('dbPort')),
|
|
user: configSql.get('dbUser'),
|
|
password: configSql.get('dbPass'),
|
|
database: configSql.get('dbName'),
|
|
charset: configSql.get('charset'),
|
|
connectionLimit: configSql.get('connectionlimit'),
|
|
timezone: 'Z',
|
|
flags: '-FOUND_ROWS'
|
|
};
|
|
|
|
const additionalOptions = config.util.cloneDeep(configSql.get('mysqlExtraOptions'));
|
|
const configuration = Object.assign({}, connectionConfiguration, additionalOptions);
|
|
let queryTimeout = undefined;
|
|
if (configuration.queryTimeout) {
|
|
queryTimeout = configuration.queryTimeout;
|
|
delete configuration.queryTimeout;
|
|
}
|
|
|
|
const 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(
|
|
result => callbackFunction?.(null, result),
|
|
error => callbackFunction?.(error)
|
|
);
|
|
}
|
|
|
|
async function executeQuery(ctx, sqlCommand, values = [], noModifyRes = false, noLog = false) {
|
|
let connection = null;
|
|
try {
|
|
connection = await pool.getConnection();
|
|
|
|
// Ensure session autocommit=1 once per physical connection; avoids pool 'connection' race and per-query overhead
|
|
if (!connection.__autocommitSet) {
|
|
await connection.query('SET autocommit=1');
|
|
connection.__autocommitSet = true;
|
|
}
|
|
|
|
const result = await connection.query({sql: sqlCommand, timeout: queryTimeout, values});
|
|
|
|
let output;
|
|
if (!noModifyRes) {
|
|
output = result[0]?.affectedRows ? {affectedRows: result[0].affectedRows} : result[0];
|
|
} else {
|
|
output = result[0];
|
|
}
|
|
|
|
return output ?? {rows: [], affectedRows: 0};
|
|
} catch (error) {
|
|
if (!noLog) {
|
|
ctx.logger.error(`sqlQuery() error while executing query: ${sqlCommand}\n${error.stack}`);
|
|
}
|
|
|
|
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}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function closePool() {
|
|
return await pool.end();
|
|
}
|
|
|
|
function addSqlParameter(parameter, accumulatedArray) {
|
|
accumulatedArray.push(parameter);
|
|
return '?';
|
|
}
|
|
|
|
function concatParams(firstParameter, secondParameter) {
|
|
return `CONCAT(COALESCE(${firstParameter}, ''), COALESCE(${secondParameter}, ''))`;
|
|
}
|
|
|
|
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) {
|
|
const callbackPlaceholder = addSqlParameter(JSON.stringify(task.callback), values);
|
|
updateStatement += `, callback = CONCAT(callback , '${connectorUtilities.UserCallback.prototype.delimiter}{"userIndex":' , (user_index + 1) , ',"callback":', ${callbackPlaceholder}, '}')`;
|
|
}
|
|
|
|
if (task.baseurl) {
|
|
const 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 = sqlQuery;
|
|
module.exports.closePool = closePool;
|
|
module.exports.addSqlParameter = addSqlParameter;
|
|
module.exports.concatParams = concatParams;
|
|
module.exports.upsert = upsert;
|