UNPKG

enhancer-data-bridge

Version:

A bridge between Enhancer Clould and user business datasource

174 lines (151 loc) 7.09 kB
/** * A simple compiler to translate e-sql(the sql contains variable placeholders) * to js-procedure. * @Author zyz. * @updated: 2021/03/30 */ // SQL result regex. var rReg = /\$\d+((\.\w+)|(\[\s*\d+\s*\])|(\[\s*'[^']+'\s*\])|(\[\s*"[^"]+"\s*\]))+/; // SQL identifier regex. var iReg = /\$(\d+-)?\w+(\.\w+)*\$/; // SQL parameter regex. var pReg = /\@(\d+-)?\w+(\.\w+)*\@/; // All types of placeholder regex var reg = /(\$(\d+-)?\w+(\.\w+)*\$)|(\@(\d+-)?\w+(\.\w+)*\@)|(\$\d+((\.\w+)|(\[\s*\d+\s*\])|(\[\s*'[^']+'\s*\])|(\[\s*"[^"]+"\s*\]))+)/g; var expReg = /\#([^\#])*\#/g; // Identifiers and parameters var ipReg = /(\@(\d+-)?\w+(\.\w+)*\@)|(\$(\d+-)?\w+(\.\w+)*\$)/g; var requireNocache = require('require-nocache')(module); var procedureTemplateTransaction = require('./procedure-template-transaction.js'); var procedureTemplate = require('./procedure-template.js'); var os = require('os'); var path = require('path'); var customModuleBase = path.resolve(__dirname, '../repository/project/custom-module'); if (os.platform() === 'win32') { customModuleBase = customModuleBase.replace(/\\/g, '/') + '/'; } else { customModuleBase = customModuleBase + '/'; } var compiler = { compile: function(eSql) { // Remove blank. eSql = eSql.replace(/(^\s+)|(\s+$)/g, ''); // Remove comments. eSql = eSql.replace(/\/\*(.|\s)+?\*\//g, '') .replace(/--[^'\n]*('[^'\n]*'[^'\n]*)+/g, '') .replace(/--[^'\n]+/g, ''); eSql = eSql.replace(expReg, function(s) { return s.replace(/\@/g, '{^}') .replace(/\$/g, '{&}') .replace(/\;/g, '{%}'); }); if (!eSql) { return function (connectionName, parameters, serverVariables, Enhancer, done) {done(null, {success: true, results: []})}; } var series = false; var sideEffectsSQLCount = 0; var esqlSet = eSql.split(/;/g) .map(function(sql) { return sql.replace(/^\s|\s$/g, ''); }) .filter(function(sql) { if (sql && !/^SELECT\s/i.test(sql)) { sideEffectsSQLCount++; } return sql ? true : false; }); var fragments = esqlSet.map(function(sql, index) { sql = sql.trim(); if (!sql) { return ''; } var fragment = []; var params = []; var dependencies = []; sql = sql.replace(reg, function(s) { return '#' + s + '#'; }) .replace(/\{\^\}/g, '@') .replace(/\{\&\}/g, '$') .replace(/\{\%\}/g, ';'); sql = sql .replace(expReg, function(s) { if (rReg.test(s)) { series = true; } var exp = s.replace(/\#/g, '').replace(/(^\s*)|(\s*$)/g, ''); // custom module exp = exp.replace(/require\(\s*(\'|\")@custom\//g, function(s, $1) { return 'require(' + $1 + customModuleBase; }); exp = exp.replace(ipReg, function(ss) { var name = ss.split(/\$|\@/)[1]; if (/^\w+(\.\w+)*$/.test(name)) { return ' Enhancer.getVariable(\'' + name.toUpperCase() + '\')'; } else { return ' parameters[\'' + name.toUpperCase() + '\']'; } }); try { new Function(exp); } catch(ex) { ex.message = 'Invalid parameter or JavaScript expression: ' + s + '. SQL index: ' + index + '. Reason: ' + ex.message; throw ex; } var p = '(function(){try{return ' + exp + '}catch(ex){ex.message += `. Javascript Expression: ' + s + '`; throw ex}})()'; // Check syntax if (iReg.test(s)) { return '{&}\n+ escapeId(' + p + ').replace(/`/g, \'\') +\n{&}' } params.push(p); return '?'; }) .replace(/\n\s*/g, ' '); sql = '"' + sql.replace(/"/g, '\\"') + '"'; sql = sql.replace(/\{\&\}/g, '"'); var TAB = ' '; fragment.push('/********'); fragment.push(' * SQL ' + index); fragment.push(' ********/'); fragment.push('transaction.push(function(callback) {'); fragment.push(' var sql'); fragment.push(' var params') fragment.push(' try {') fragment.push(' sql = ' + sql); fragment.push(' params = [' + params.join(',\n ') + ']'); fragment.push(' } catch (ex) {'); fragment.push(' ex.message = "Invalid Javascript expression. Caused by: " + ex.message'); fragment.push(' ex.debugInfo = {sqlIndex: ' + index + ', sql: sql, param: params}'); fragment.push(' return callback(ex)'); fragment.push(' }'); fragment.push(' logger.debug("SQL ' + index + ': " , "\\n", sql)'); fragment.push(' params.length && params[0] instanceof Array && params[0].length > 1000 ? logger.warn("PARAMS SIZE IS TOO LARGE TO PRINT.") : logger.debug("PARAMS: " , "\\n", JSON.stringify(params))'); fragment.push(' if (params.length === 1 && params[0] instanceof Array && !params[0].length) {return callback(null, {rows: [],insertId: null,affectedRows: 0});}'); fragment.push(' databaseConnection.execute(sql, params, function(err, result) {'); fragment.push(' if (err) {\n'); fragment.push(' err.debugInfo = {sqlIndex: ' + index + ', sql: sql, params: params, originErrorMessage: err.message}'); fragment.push(' err.message += " SQL index: ' + index + '"'); fragment.push(' return callback(err)'); fragment.push(' }'); fragment.push(' result.rows && result.rows.length > 1000 ? logger.warn("RESULT SIZE IS TOO LARGE TO PRINT") : logger.debug("RESULT: " , "\\n", result)'); fragment.push(' $' + index + ' = result'); fragment.push(' callback(null, result)'); fragment.push(' })'); fragment.push('})'); return TAB + fragment.join('\n' + TAB); }); var funcStr; var isTransaction = sideEffectsSQLCount > 1; if (isTransaction) { funcStr = procedureTemplateTransaction .replace('{{TRANSACTION-BLOCKS}}', fragments.join('\n\n')); } else { funcStr = procedureTemplate.replace('{{TRANSACTION-BLOCKS}}', fragments.join('\n\n')) .replace('{{SERIES}}', series ? 'series' : 'parallel'); } return eval( funcStr ); } }; module.exports = compiler;