From af424b782f10fd16e66592ed59567bd0d0eb3b0a Mon Sep 17 00:00:00 2001 From: Eve <92513238+Eve-z1@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:00:41 +0800 Subject: [PATCH] fix(query): preserve Oracle star projection editability --- .../sql/starProjectionColumnMapping.spec.ts | 18 +++++++++++++++++- apps/desktop/src/lib/sql/sqlAnalysis.ts | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/__tests__/sql/starProjectionColumnMapping.spec.ts b/apps/desktop/src/lib/__tests__/sql/starProjectionColumnMapping.spec.ts index 19f1721b34..46d225f173 100644 --- a/apps/desktop/src/lib/__tests__/sql/starProjectionColumnMapping.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/starProjectionColumnMapping.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { analyzeEditableQueryEditability, resolveSourceColumnsByOrdinal } from "@/lib/sql/sqlAnalysis"; +import { analyzeEditableQueryEditability, resolveSourceColumnsByOrdinal, sourceColumnsForResult, type EditableQueryInfo } from "@/lib/sql/sqlAnalysis"; /** * An unqualified `*` is unambiguous when the query has exactly one source, @@ -56,4 +56,20 @@ describe("single-source star projection column mapping", () => { { sourceKey: "orders:0", sourceColumn: "amount" }, ]); }); + + it("keeps a case-folded explicit column before an Oracle star aligned by ordinal", () => { + const analysis: EditableQueryInfo = { + schema: "APP", + tableName: "TABLE1", + tableAlias: "t1", + selectStar: false, + columns: [ + { sourceName: "ID", sourceNameQuoted: false, sourceQualifier: "t1", sourceKey: "t1:0", resultName: "id", expression: "t1.id" }, + { sourceName: "ID", sourceNameQuoted: false, sourceQualifier: "t1", sourceKey: "t1:0", resultName: "ID", expression: "t1.ID" }, + { sourceName: "NAME", sourceNameQuoted: false, sourceQualifier: "t1", sourceKey: "t1:0", resultName: "NAME", expression: "t1.NAME" }, + ], + }; + + expect(sourceColumnsForResult(analysis, ["ID", "ID", "NAME"], "t1:0", "oracle", ["ID"])).toEqual(["ID", "ID", "NAME"]); + }); }); diff --git a/apps/desktop/src/lib/sql/sqlAnalysis.ts b/apps/desktop/src/lib/sql/sqlAnalysis.ts index 677bf74e3f..fe5f35343d 100644 --- a/apps/desktop/src/lib/sql/sqlAnalysis.ts +++ b/apps/desktop/src/lib/sql/sqlAnalysis.ts @@ -862,6 +862,9 @@ export function allPrimaryKeysPresent(primaryKeys: string[], resultColumns: stri } function matchColumnsForResult(analysis: EditableQueryInfo, resultColumns: string[]): EditableQueryColumn[] | undefined { + // Preserve projection order before searching by label: folded duplicate names are not unique. + if (analysis.columns.length === resultColumns.length && analysis.columns.every((column, index) => column.resultName.toLowerCase() === resultColumns[index]!.toLowerCase())) return analysis.columns; + const matches: EditableQueryColumn[] = []; let searchFrom = 0; for (const resultColumn of resultColumns) {