orange-orm
Version:
Object Relational Mapper
206 lines (184 loc) • 5.18 kB
JavaScript
var log = require('../table/log');
function wrapQuery(_context, connection) {
let CachedRequest = null;
let CachedTypes = null;
return runQuery;
function runQuery(query, onCompleted) {
enqueue(function(done) {
function safeCompleted(err, rows) {
try {
onCompleted(err, rows);
} finally {
done();
}
}
if (!CachedRequest || !CachedTypes) {
import('tedious')
.then(({ Request, TYPES }) => {
CachedRequest = Request;
CachedTypes = TYPES;
doQuery(query, safeCompleted);
})
.catch(err => safeCompleted(extractError(err), []));
}
else {
doQuery(query, safeCompleted);
}
});
}
function doQuery(query, onCompleted) {
const results = []; // Array to hold multiple result sets
let currentResult = []; // Current result set being built
let hasResultSet = false; // Track if we're in an actual result set
log.emitQuery({ sql: query.sql(), parameters: query.parameters });
const sql = replaceParamChar(query.sql(), query.parameters);
// Transaction statements
if (sql.length < 18 && query.parameters.length === 0) {
if (sql === 'BEGIN TRANSACTION') {
connection.beginTransaction((err) => {
onCompleted(extractError(err), []);
});
return;
}
else if (sql === 'COMMIT') {
connection.commitTransaction((err) => {
onCompleted(extractError(err), []);
});
return;
}
else if (sql === 'ROLLBACK') {
connection.rollbackTransaction((err) => {
onCompleted(extractError(err), []);
});
return;
}
}
let keys;
var request = new CachedRequest(sql, onInnerCompleted);
addParameters(request, query.parameters, CachedTypes);
request.on('row', rows => {
const tmp = {};
if (!keys) {
keys = Object.keys(rows);
}
keys.forEach(cols => {
tmp[cols] = rows[cols].value;
});
currentResult.push(tmp);
hasResultSet = true; // We're definitely in a result set
});
// Handle column metadata - indicates a result set is starting
request.on('columnMetadata', (_columns) => {
hasResultSet = true; // A result set is starting (even if it ends up empty)
});
// Handle end of each result set
request.on('doneInProc', (_rowCount, _more) => {
// End of a result set within a stored procedure
// Add to results if we had a result set (even if empty)
if (hasResultSet) {
results.push(currentResult);
currentResult = [];
keys = null; // Reset keys for next result set
hasResultSet = false; // Reset for next potential result set
}
});
request.on('doneProc', (_rowCount, _more) => {
// End of stored procedure execution
// Add to results if we had a result set (even if empty)
if (hasResultSet) {
results.push(currentResult);
currentResult = [];
hasResultSet = false; // Reset for next potential result set
}
});
connection.execSql(request);
function onInnerCompleted(err) {
if (err) {
onCompleted(extractError(err));
} else {
// If we have any remaining result set, add it
if (hasResultSet) {
results.push(currentResult);
}
// Return based on number of actual result sets
if (results.length === 0) {
// No result sets - return empty array
onCompleted(null, []);
} else if (results.length === 1) {
// Single result set - return as single-depth array (even if empty)
onCompleted(null, results[0]);
} else {
// Multiple result sets - return as array of arrays
onCompleted(null, results);
}
}
}
}
function enqueue(task) {
if (!connection.__orangeOrmQueue)
connection.__orangeOrmQueue = Promise.resolve();
connection.__orangeOrmQueue = connection.__orangeOrmQueue.then(() => new Promise((resolve) => {
try {
task(resolve);
}
catch (_e) {
resolve();
}
})).catch(() => {});
}
}
// Helper functions remain the same
function extractError(e) {
if (e && e.errors) {
return e.errors[0];
}
else {
return e;
}
}
function replaceParamChar(sql, params) {
if (params.length === 0)
return sql;
var splitted = sql.split('?');
sql = '';
var lastIndex = splitted.length - 1;
for (var i = 0; i < lastIndex; i++) {
sql += splitted[i] + '@' + i;
}
sql += splitted[lastIndex];
return sql;
}
function addParameters(request, params, TYPES) {
const res = [];
for (let i = 0; i < params.length; i++) {
const p = [`${i}`, toType(params[i]), params[i]];
request.addParameter.apply(request, p);
res.push(p);
}
return res;
function toType(p) {
if (typeof p === 'string')
return TYPES.VarChar;
else if (Number.isInteger(p)) {
// Check if the integer is within the 32-bit signed integer range
if (p >= -2147483648 && p <= 2147483647) {
return TYPES.Int;
} else {
return TYPES.BigInt;
}
}
else if (typeof p === 'number')
return TYPES.Money;
else if (p instanceof Date && !isNaN(p))
return TYPES.Date;
else if (Array.isArray(p))
return TYPES.NVarChar;
else if (Buffer.isBuffer(p))
return TYPES.VarBinary;
else if (typeof p === 'object' && p instanceof Object)
return TYPES.NVarChar;
else
throw new Error('Unknown data type');
}
}
module.exports = wrapQuery;