orange-orm
Version:
Object Relational Mapper
136 lines (112 loc) • 3.85 kB
JavaScript
var log = require('../table/log');
var getSessionSingleton = require('../table/getSessionSingleton');
function wrapQuery(_context, connection) {
var runOriginalQuery = connection.query;
return runQuery;
function runQuery(query, onCompleted) {
var params = query.parameters;
var sql = query.sql();
log.emitQuery({ sql, parameters: params });
const replacements = [];
const parametersToRemove = [];
const engine = getSessionSingleton(_context, 'engine');
if (engine === 'sap') {
const sap = connection.msnodesqlv8;
// Check if this is a stored procedure call
const isStoredProcCall = /EXECUTE\s+/i.test(sql) || /EXEC\s+/i.test(sql);
let hexVariables = [];
// Non-ASCII UTF-8 characters workaround
for (let i = 0; i < params.length; i++) {
const parameter = params[i];
if (typeof parameter === 'string') {
const byteLength = Buffer.from(parameter, 'utf8').length;
if (hasNonAsciiCharacters(parameter)) {
const hexValue = stringToHex(parameter);
if (isStoredProcCall) {
// For stored procedures, create a variable with exact lengths
const varName = `@hex_param_${i}`;
const convertClause = `CONVERT(VARCHAR(${byteLength}), CONVERT(VARBINARY(${byteLength}), 0x${hexValue}))`;
hexVariables.push({
declaration: `DECLARE ${varName} VARCHAR(${byteLength})`,
assignment: `SET ${varName} = ${convertClause}`
});
replacements.push({
index: i,
replacement: varName
});
} else {
// For regular queries, use inline conversion with exact lengths
const convertClause = `CONVERT(VARCHAR(${byteLength}), CONVERT(VARBINARY(${byteLength}), 0x${hexValue}))`;
replacements.push({
index: i,
replacement: convertClause
});
}
parametersToRemove.push(i);
} else {
// For ASCII strings, use VarChar with exact byte length
params[i] = sap.VarChar(parameter, byteLength);
}
}
}
// Apply replacements
if (replacements.length > 0) {
let questionMarkIndex = 0;
sql = sql.replace(/\?/g, (match) => {
const replacement = replacements.find(r => r.index === questionMarkIndex);
questionMarkIndex++;
if (replacement) {
return replacement.replacement;
}
return match;
});
// For stored procedures, inject hex variable declarations
if (isStoredProcCall && hexVariables.length > 0) {
const lines = sql.split('\n');
let insertIndex = 0;
// Find the last DECLARE statement
for (let i = 0; i < lines.length; i++) {
if (/^\s*DECLARE\s+/i.test(lines[i])) {
insertIndex = i + 1;
}
}
// Insert hex variable declarations and assignments
const hexDeclarations = hexVariables.map(v => v.declaration);
const hexAssignments = hexVariables.map(v => v.assignment);
lines.splice(insertIndex, 0, ...hexDeclarations, ...hexAssignments);
sql = lines.join('\n');
}
}
// Remove parameters in reverse order to maintain correct indices
parametersToRemove.reverse().forEach(index => {
params.splice(index, 1);
});
}
runOriginalQuery.call(connection, sql, params, onInnerCompleted);
let result = [];
function onInnerCompleted(err, rows, hasMore) {
if (err) {
if (err.code && err.code !== 3604)
onCompleted(err);
if (rows)
result.push(rows);
return;
}
result.push(rows);
if (!hasMore) {
if (result.length === 1)
onCompleted(null, result[0]);
else
onCompleted(null, result);
}
}
}
}
function hasNonAsciiCharacters(str) {
// Check if string contains any character with code point > 127 (non-ASCII)
return /[\u0080-\uFFFF]/.test(str);
}
function stringToHex(str) {
return Buffer.from(str, 'utf8').toString('hex');
}
module.exports = wrapQuery;