Release 1.5.13
This commit is contained in:
+151
-2
@@ -27,23 +27,168 @@ function normalizePageNumber(value) {
|
||||
return Math.max(1, pageNumber);
|
||||
}
|
||||
|
||||
function escapeLikeValue(value) {
|
||||
return String(value || '').replace(/[\\%_]/g, '\\$&');
|
||||
}
|
||||
|
||||
function normalizeSortDirection(value) {
|
||||
return String(value || '').trim().toLowerCase() === 'desc' ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
function buildSortOrderClause(sortColumns, sortKey, sortDirection) {
|
||||
const normalizedSortKey = String(sortKey || '').trim();
|
||||
const normalizedSortDirection = normalizeSortDirection(sortDirection);
|
||||
const sortColumnMap = sortColumns && typeof sortColumns === 'object' ? sortColumns : {};
|
||||
const sortExpression = normalizedSortKey ? sortColumnMap[normalizedSortKey] : null;
|
||||
|
||||
if (!sortExpression) {
|
||||
return {
|
||||
clause: '',
|
||||
sortKey: normalizedSortKey,
|
||||
sortDirection: normalizedSortDirection
|
||||
};
|
||||
}
|
||||
|
||||
const expressions = Array.isArray(sortExpression) ? sortExpression : [sortExpression];
|
||||
const orderBySql = expressions.map(function (expression) {
|
||||
return `${expression} ${normalizedSortDirection.toUpperCase()}`;
|
||||
}).join(', ');
|
||||
|
||||
return {
|
||||
clause: ` ORDER BY ${orderBySql}`,
|
||||
sortKey: normalizedSortKey,
|
||||
sortDirection: normalizedSortDirection
|
||||
};
|
||||
}
|
||||
|
||||
function findTopLevelOrderByIndex(sql) {
|
||||
const text = String(sql || '');
|
||||
let depth = 0;
|
||||
let inSingleQuote = false;
|
||||
let inDoubleQuote = false;
|
||||
let inBacktick = false;
|
||||
let lastOrderByIndex = -1;
|
||||
|
||||
for (let index = 0; index < text.length; index += 1) {
|
||||
const character = text[index];
|
||||
const previousCharacter = index > 0 ? text[index - 1] : '';
|
||||
|
||||
if (inSingleQuote) {
|
||||
if (character === '\'' && previousCharacter !== '\\') {
|
||||
inSingleQuote = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inDoubleQuote) {
|
||||
if (character === '"' && previousCharacter !== '\\') {
|
||||
inDoubleQuote = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBacktick) {
|
||||
if (character === '`') {
|
||||
inBacktick = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character === '\'') {
|
||||
inSingleQuote = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character === '"') {
|
||||
inDoubleQuote = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character === '`') {
|
||||
inBacktick = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character === '(') {
|
||||
depth += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character === ')' && depth > 0) {
|
||||
depth -= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depth === 0 && /[oO]/.test(character)) {
|
||||
const remaining = text.slice(index);
|
||||
if (/^order\s+by\b/i.test(remaining)) {
|
||||
lastOrderByIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastOrderByIndex;
|
||||
}
|
||||
|
||||
function buildSearchFilter(searchColumns, searchTerm) {
|
||||
const columns = Array.isArray(searchColumns) ? searchColumns.map(function (column) {
|
||||
return String(column || '').trim();
|
||||
}).filter(Boolean) : [];
|
||||
const normalizedSearchTerm = String(searchTerm || '').trim().toLowerCase();
|
||||
|
||||
if (!columns.length || !normalizedSearchTerm) {
|
||||
return {
|
||||
clause: '',
|
||||
params: []
|
||||
};
|
||||
}
|
||||
|
||||
const likeValue = `%${escapeLikeValue(normalizedSearchTerm)}%`;
|
||||
return {
|
||||
clause: ` WHERE (${columns.map(function (column) {
|
||||
return `LOWER(COALESCE(CAST(${column} AS CHAR), '')) LIKE ? ESCAPE '\\\\'`;
|
||||
}).join(' OR ')})`,
|
||||
params: columns.map(function () {
|
||||
return likeValue;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchPagedRows(pool, options) {
|
||||
const selectSql = String(options && options.selectSql || '').trim();
|
||||
const countSql = String(options && options.countSql || '').trim();
|
||||
const params = Array.isArray(options && options.params) ? options.params : [];
|
||||
const searchFilter = buildSearchFilter(options && options.searchColumns, options && options.searchTerm);
|
||||
const sortOrder = buildSortOrderClause(options && options.sortColumns, options && options.sortKey, options && options.sortDirection);
|
||||
const pageSize = Math.max(1, Number(options && options.pageSize) || 10);
|
||||
const currentPage = normalizePageNumber(options && options.page);
|
||||
|
||||
if (!selectSql || !countSql) {
|
||||
throw new Error('fetchPagedRows requires selectSql and countSql.');
|
||||
}
|
||||
const orderByIndex = findTopLevelOrderByIndex(selectSql);
|
||||
let baseSelectSql = selectSql;
|
||||
let orderBySql = '';
|
||||
|
||||
const [countRows] = await pool.query(countSql, params);
|
||||
if (orderByIndex >= 0) {
|
||||
baseSelectSql = selectSql.slice(0, orderByIndex).trim();
|
||||
orderBySql = selectSql.slice(orderByIndex).trim();
|
||||
}
|
||||
|
||||
const filteredSelectSql = `${baseSelectSql}${searchFilter.clause}`;
|
||||
const countQuery = searchFilter.clause
|
||||
? `SELECT COUNT(*) AS count FROM (${filteredSelectSql}) AS filtered_rows`
|
||||
: countSql;
|
||||
const countParams = searchFilter.clause ? params.concat(searchFilter.params) : params;
|
||||
const [countRows] = await pool.query(countQuery, countParams);
|
||||
const totalItems = Number(countRows && countRows[0] && countRows[0].count) || 0;
|
||||
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
||||
const safeCurrentPage = Math.min(currentPage, totalPages);
|
||||
const offset = (safeCurrentPage - 1) * pageSize;
|
||||
const [rows] = await pool.query(`${selectSql} LIMIT ? OFFSET ?`, params.concat([pageSize, offset]));
|
||||
|
||||
const activeOrderBySql = sortOrder.clause || (orderBySql ? ` ${orderBySql}` : '');
|
||||
const selectQuery = `${filteredSelectSql}${activeOrderBySql} LIMIT ? OFFSET ?`;
|
||||
const [rows] = await pool.query(selectQuery, params.concat(searchFilter.params, [pageSize, offset]));
|
||||
|
||||
return {
|
||||
rows: rows || [],
|
||||
@@ -77,6 +222,10 @@ module.exports = {
|
||||
parseJsonSafe,
|
||||
readFormArray,
|
||||
normalizePageNumber,
|
||||
normalizeSortDirection,
|
||||
buildSortOrderClause,
|
||||
findTopLevelOrderByIndex,
|
||||
buildSearchFilter,
|
||||
fetchPagedRows,
|
||||
fetchDuplicateName
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user