mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-31 16:24:33 +00:00
* fix(grid): 修复查询结果表头提示与排序 修复 MySQL 查询结果列元数据取错库名的问题,并补齐查询结果全量排序链路,避免表头提示与排序行为失效。 * fix(query): 修复结果排序重复列名报错
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import type { DatabaseType } from "../types/database.ts";
|
|
import { quoteTableIdentifier } from "./tableSelectSql.ts";
|
|
import { findStatementAtCursor } from "./sqlStatementSplit.ts";
|
|
|
|
export type QuerySortDirection = "asc" | "desc";
|
|
|
|
export interface SortedQuerySqlResult {
|
|
ok: true;
|
|
sql: string;
|
|
}
|
|
|
|
export interface SortedQuerySqlError {
|
|
ok: false;
|
|
reason: "empty" | "multi" | "not_select" | "with";
|
|
}
|
|
|
|
export function buildSortedQuerySql(
|
|
originalSql: string,
|
|
databaseType: DatabaseType | undefined,
|
|
resultColumns: string[],
|
|
columnIndex: number,
|
|
column: string,
|
|
direction: QuerySortDirection,
|
|
): SortedQuerySqlResult | SortedQuerySqlError {
|
|
const baseSql = originalSql.trim();
|
|
if (!baseSql) return { ok: false, reason: "empty" };
|
|
|
|
const statement = findStatementAtCursor(baseSql, 0)
|
|
.trim()
|
|
.replace(/;+\s*$/, "")
|
|
.trim();
|
|
if (!statement) return { ok: false, reason: "empty" };
|
|
if (statement.length !== baseSql.replace(/;+\s*$/, "").trim().length) {
|
|
return { ok: false, reason: "multi" };
|
|
}
|
|
if (/^\s*WITH\b/i.test(statement)) {
|
|
return { ok: false, reason: "with" };
|
|
}
|
|
if (!/^\s*SELECT\b/i.test(statement)) {
|
|
return { ok: false, reason: "not_select" };
|
|
}
|
|
|
|
const aliases = buildDerivedColumnAliases(resultColumns);
|
|
const sortAlias = aliases[columnIndex] ?? aliases[resultColumns.indexOf(column)] ?? fallbackAlias(columnIndex);
|
|
const quotedColumn = quoteTableIdentifier(databaseType, sortAlias);
|
|
const aliasList = aliases.map((alias) => quoteTableIdentifier(databaseType, alias)).join(", ");
|
|
return {
|
|
ok: true,
|
|
sql: `SELECT * FROM (${statement}) t(${aliasList}) ORDER BY ${quotedColumn} ${direction.toUpperCase()};`,
|
|
};
|
|
}
|
|
|
|
function buildDerivedColumnAliases(resultColumns: string[]): string[] {
|
|
const seen = new Map<string, number>();
|
|
return resultColumns.map((column, index) => {
|
|
const base = normalizeAliasBase(column, index);
|
|
const count = (seen.get(base) ?? 0) + 1;
|
|
seen.set(base, count);
|
|
return count === 1 ? base : `${base}_${count}`;
|
|
});
|
|
}
|
|
|
|
function normalizeAliasBase(column: string, index: number): string {
|
|
const compact = column.trim().replace(/\s+/g, "_");
|
|
const safe = compact.replace(/[^\p{L}\p{N}_$]/gu, "_").replace(/^_+|_+$/g, "");
|
|
return safe || fallbackAlias(index);
|
|
}
|
|
|
|
function fallbackAlias(index: number): string {
|
|
return `column_${index + 1}`;
|
|
}
|