76 lines
3.9 KiB
TypeScript
Vendored
76 lines
3.9 KiB
TypeScript
Vendored
import { describe, expect, it } from "vitest";
|
|
import { analyzeEditableQueryEditability, resolveSourceColumnsByOrdinal, sourceColumnsForResult, type EditableQueryInfo } from "@/lib/sql/sqlAnalysis";
|
|
|
|
/**
|
|
* An unqualified `*` is unambiguous when the query has exactly one source,
|
|
* so it must bind to that source the same way a qualified `alias.*` does.
|
|
* Regression coverage for #8015: `SELECT *, col FROM t` (bare star mixed
|
|
* with an explicit column, single source, no alias) was dropping the star
|
|
* expansion entirely because the bare-star projection carried no
|
|
* `sourceKey`, misaligning every result column from that point on.
|
|
*/
|
|
describe("single-source star projection column mapping", () => {
|
|
it("expands a bare unqualified star mixed with an explicit column", () => {
|
|
const result = analyzeEditableQueryEditability("SELECT *, amount FROM orders");
|
|
expect(result.editable).toBe(true);
|
|
if (!result.editable) return;
|
|
|
|
const source = { key: "orders:0", tableName: "orders" };
|
|
const resolved = resolveSourceColumnsByOrdinal("mysql", result.analysis, [{ source, columns: [{ name: "id" }, { name: "user_id" }, { name: "amount" }] }], 4);
|
|
expect(resolved).toEqual([
|
|
{ sourceKey: "orders:0", sourceColumn: "id" },
|
|
{ sourceKey: "orders:0", sourceColumn: "user_id" },
|
|
{ sourceKey: "orders:0", sourceColumn: "amount" },
|
|
{ sourceKey: "orders:0", sourceColumn: "amount" },
|
|
]);
|
|
});
|
|
|
|
it("expands a bare unqualified star mixed with an aliased source and leading explicit columns", () => {
|
|
// Matches the issue's own repro shape: qualified explicit columns before
|
|
// a trailing bare `*`, single aliased source, no primary key.
|
|
const result = analyzeEditableQueryEditability("SELECT fl.sqlicenseno, fl.sqtypetext, * FROM flow_licenseapprove AS fl");
|
|
expect(result.editable).toBe(true);
|
|
if (!result.editable) return;
|
|
|
|
const source = { key: "fl:0", tableName: "flow_licenseapprove" };
|
|
const resolved = resolveSourceColumnsByOrdinal("kingbase", result.analysis, [{ source, columns: [{ name: "sqlicenseno" }, { name: "sqtypetext" }, { name: "uniformid" }, { name: "sqtypeid" }] }], 5);
|
|
expect(resolved).toEqual([
|
|
{ sourceKey: "fl:0", sourceColumn: "sqlicenseno" },
|
|
{ sourceKey: "fl:0", sourceColumn: "sqtypetext" },
|
|
{ sourceKey: "fl:0", sourceColumn: "sqlicenseno" },
|
|
{ sourceKey: "fl:0", sourceColumn: "sqtypetext" },
|
|
{ sourceKey: "fl:0", sourceColumn: "uniformid" },
|
|
]);
|
|
});
|
|
|
|
it("still expands a plain unqualified star with no other columns (baseline, unchanged)", () => {
|
|
const result = analyzeEditableQueryEditability("SELECT * FROM orders");
|
|
expect(result.editable).toBe(true);
|
|
if (!result.editable) return;
|
|
expect(result.analysis.selectStar).toBe(true);
|
|
|
|
const source = { key: "orders:0", tableName: "orders" };
|
|
const resolved = resolveSourceColumnsByOrdinal("mysql", result.analysis, [{ source, columns: [{ name: "id" }, { name: "amount" }] }], 2);
|
|
expect(resolved).toEqual([
|
|
{ sourceKey: "orders:0", sourceColumn: "id" },
|
|
{ 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"]);
|
|
});
|
|
});
|