Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 60 additions & 45 deletions lib/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,86 +407,101 @@ class Sql {
* @returns {String} - updated sql query
*/
addParameters({ query, request, parameters, forWhere = false }) {
if (!parameters) {
return query;
}
if (!parameters) return query;

const { buildParameterName, dataTypes, forceCaseInsensitive } = this;
const paramNames = Object.keys(parameters);
const whereClauses = [];
for (let index = 0, len = paramNames.length; index < len; index++) {
let paramName = paramNames[index];
const props = parameters[paramName];
if (props === undefined || (typeof props === 'number' && isNaN(props))) {
continue;
}
let operator = "=";
const paramNames = Object.keys(parameters);

for (let i = 0; i < paramNames.length; i++) {
const originalName = paramNames[i];
let paramName = originalName;
let props = parameters[paramName];

if (props === undefined || (typeof props === 'number' && isNaN(props))) continue;

let operator = '=';
let value = props;
let ignoreNull = true;
let fieldName = paramName;
let sqlType;
if (props !== undefined && props !== null && typeof props === 'object') {

// Extract object-style properties
if (props && typeof props === 'object' && props !== null) {
Copy link

Copilot AI Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'props' is of type date, object or regular expression, but it is compared to an expression of type null.

Suggested change
if (props && typeof props === 'object' && props !== null) {
if (props && typeof props === 'object' && props.constructor === Object) {

Copilot uses AI. Check for mistakes.
if (props.statement) {
whereClauses.push(props.statement);
continue;
}

operator = props.operator || operator;
value = props.value;
ignoreNull = props.ignoreNull !== false;
fieldName = props.fieldName || fieldName;
sqlType = props.sqlType;
}

if ((!isNullNotNullOperators.includes(operator)) && (value === undefined || (value === null && ignoreNull))) {
continue;
}
// Skip null/undefined values for normal operators
const isNullOp = isNullNotNullOperators.includes(operator);
if (!isNullOp && (value === undefined || (value === null && ignoreNull))) continue;

// Case-insensitive handling
if (forceCaseInsensitive) {
if (typeof value === 'string' || sqlType === dataTypes.string) {
value = value.toUpperCase();
fieldName = `UPPER(${fieldName})`;
}
if (Array.isArray(value)) {
value = value.map(val => typeof val === 'string' ? val.toUpperCase() : val);
// Only apply UPPER() to fieldName if the array contains at least one string
const hasString = value.some(val => typeof val === 'string');
if(hasString) {
fieldName = `UPPER(${fieldName})`;
}
} else if (Array.isArray(value)) {
const hasString = value.some(v => typeof v === 'string');
if (hasString) fieldName = `UPPER(${fieldName})`;
value = value.map(v => typeof v === 'string' ? v.toUpperCase() : v);
}
}

if (paramName.indexOf('.') > -1) {
const parts = paramName.split('.');
paramName = parts[parts.length - 1];
}
let statement = `${fieldName} ${operator}${!isNullNotNullOperators.includes(operator) ? ` ${buildParameterName(paramName)}` : ''}`;
const isBetweenOperator = operator.toLowerCase() === 'between' || operator.toLowerCase() === 'not between';
// Shorten param name if it's nested
const shortName = paramName.includes('.') ? paramName.split('.').pop() : paramName;

if (Array.isArray(value) || ["in", "not in"].includes(operator.toLowerCase())) {
const inResult = isBetweenOperator ? this.between({ request, fieldName, paramName, values: value, operator, sqlType: sqlType || dataTypes.integer }) : this.in({ request, fieldName, paramName, values: value, operator, sqlType: sqlType || dataTypes.integer });
if (inResult.paramNames.length === 0) {
continue;
}
const isBetween = /between/i.test(operator);
const isInOperator = ['in', 'not in'].includes(operator.toLowerCase());

let statement = `${fieldName} ${operator}${!isNullOp ? ` ${buildParameterName(shortName)}` : ''}`;

// Handle IN / BETWEEN operators
if (Array.isArray(value) || isInOperator) {
const fn = isBetween ? this.between : this.in;
const inResult = fn.call(this, {
request,
fieldName,
paramName: shortName,
values: value,
operator,
sqlType: sqlType || dataTypes.integer
});

if (!inResult || !inResult.paramNames?.length) continue;
statement = inResult.statement;
if (query && !isBetweenOperator) {
// ability to replace @{FieldName} if we have embedded parameter in inner query
query = query.replaceAll(`${this.parameterPrefix}{${paramName}}`, inResult.paramNames.join(', '));

// Prevent infinite replacements
const placeholder = `${this.parameterPrefix}{${shortName}}`;
const replacement = inResult.paramNames.join(', ');

if (typeof query === 'string' && !replacement.includes(placeholder) && !isBetween) {
Copy link

Copilot AI Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check '!replacement.includes(placeholder)' may not prevent all infinite replacement scenarios. If the placeholder appears in any of the generated parameter names (e.g., shortName contains '{shortName}'), this could still cause issues. Consider a more robust check or document this edge case.

Copilot uses AI. Check for mistakes.
query = query.replaceAll(placeholder, replacement);
}
} else {
if (sqlType !== undefined && sqlType !== null) {
request.input(paramName, sqlType, value);
// Add SQL parameter binding safely
if (sqlType != null) {
request.input(shortName, sqlType, value);
} else {
request.input(paramName, value);
request.input(shortName, value);
}
}

if (forWhere) {
whereClauses.push(statement);
}
if (forWhere) whereClauses.push(statement);
}
if (whereClauses.length > 0) {
query += " WHERE " + whereClauses.join(' AND ');

if (forWhere && whereClauses.length > 0) {
query += ' WHERE ' + whereClauses.join(' AND ');
}

return query;
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@durlabh/dframework",
"version": "1.0.48",
"version": "1.0.49",
"main": "index.js",
"license": "MIT",
"type": "module",
Expand Down