fix(freight-api): quote fallback sort aliases

Sorting by a column with no explicit sortExpr fell back to the bare
select alias unquoted. Postgres folds unquoted identifiers to lowercase,
so any camelCase alias (utilizationPct, bookedTons) 42703'd. Quote the
fallback to match the case TypeORM's addSelect actually emitted.
This commit is contained in:
Nathnael
2026-08-13 07:54:44 +00:00
parent 081b9945cb
commit 9930bef8aa

View File

@@ -48,6 +48,15 @@ function coerceParams(
return params; return params;
} }
/**
* Sort expression for a column with no explicit `sortExpr`: the SELECT alias
* TypeORM emitted for it, quoted. TypeORM always double-quotes `addSelect`
* aliases in the generated SQL (preserving case) — ordering by the bare,
* unquoted key instead lets Postgres fold it to lowercase and 42703 on any
* camelCase alias (e.g. "utilizationPct" -> unquoted "utilizationpct").
*/
const aliasSortExpr = (key: string): string => `"${key.replace(/"/g, '""')}"`;
/** Resolve a client-requested sort column against the report's own whitelist. */ /** Resolve a client-requested sort column against the report's own whitelist. */
function resolveSort( function resolveSort(
def: ReportDefinition, def: ReportDefinition,
@@ -57,14 +66,14 @@ function resolveSort(
const dir = sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'; const dir = sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
const requested = sortBy && def.columns.find((c) => c.key === sortBy && c.sortable); const requested = sortBy && def.columns.find((c) => c.key === sortBy && c.sortable);
if (requested) { if (requested) {
return { key: requested.key, expr: requested.sortExpr ?? requested.key, dir }; return { key: requested.key, expr: requested.sortExpr ?? aliasSortExpr(requested.key), dir };
} }
if (!def.defaultSort) return null; if (!def.defaultSort) return null;
const fallback = def.columns.find((c) => c.key === def.defaultSort!.key); const fallback = def.columns.find((c) => c.key === def.defaultSort!.key);
if (!fallback) return null; if (!fallback) return null;
return { return {
key: fallback.key, key: fallback.key,
expr: fallback.sortExpr ?? fallback.key, expr: fallback.sortExpr ?? aliasSortExpr(fallback.key),
dir: def.defaultSort.dir, dir: def.defaultSort.dir,
}; };
} }