Skip to content

Commit 76852cd

Browse files
committed
Improved alias matching case insensitivity
1 parent 5c236b9 commit 76852cd

File tree

2 files changed

+23
-13
lines changed

2 files changed

+23
-13
lines changed

internal-packages/tsql/src/query/printer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3442,15 +3442,15 @@ describe("timeBucket()", () => {
34423442
"SELECT timeBucket(), count() FROM runs GROUP BY timeBucket ORDER BY timeBucket"
34433443
);
34443444

3445-
expect(sql).toContain("ORDER BY timeBucket");
3445+
expect(sql).toContain("ORDER BY timebucket");
34463446
});
34473447

34483448
it("should allow ORDER BY timeBucket DESC", () => {
34493449
const { sql } = printTimeBucketQuery(
34503450
"SELECT timeBucket(), count() FROM runs GROUP BY timeBucket ORDER BY timeBucket DESC"
34513451
);
34523452

3453-
expect(sql).toContain("ORDER BY timeBucket DESC");
3453+
expect(sql).toContain("ORDER BY timebucket DESC");
34543454
});
34553455
});
34563456

internal-packages/tsql/src/query/printer.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,12 @@ export class ClickHousePrinter {
120120
/** Columns hidden when SELECT * is expanded to core columns only */
121121
private hiddenColumns: string[] = [];
122122
/**
123-
* Set of column aliases defined in the current SELECT clause.
123+
* Map of column aliases defined in the current SELECT clause.
124+
* Key is the lowercase alias (for case-insensitive lookup),
125+
* value is the canonical form (as it appears in the generated SQL).
124126
* Used to allow ORDER BY/HAVING to reference aliased columns.
125127
*/
126-
private selectAliases: Set<string> = new Set();
128+
private selectAliases: Map<string, string> = new Map();
127129
/**
128130
* Set of internal ClickHouse column names that are allowed (e.g., tenant columns).
129131
* These are populated from tableSchema.tenantColumns when processing joins.
@@ -388,7 +390,7 @@ export class ClickHousePrinter {
388390
// Extract SELECT column aliases BEFORE visiting columns
389391
// This allows ORDER BY/HAVING to reference aliased columns
390392
const savedAliases = this.selectAliases;
391-
this.selectAliases = new Set();
393+
this.selectAliases = new Map();
392394
if (node.select) {
393395
for (const col of node.select) {
394396
this.extractSelectAlias(col);
@@ -569,21 +571,26 @@ export class ClickHousePrinter {
569571
* (SELECT COUNT() → 'count') should be added.
570572
*/
571573
private extractSelectAlias(expr: Expression): void {
572-
// Handle explicit Alias: SELECT ... AS name (stored lowercase for case-insensitive lookup)
574+
// Handle explicit Alias: SELECT ... AS name
575+
// Key is lowercase for case-insensitive lookup, value is the original casing
576+
// so that ORDER BY/GROUP BY output matches the alias in the generated SQL.
573577
if ((expr as Alias).expression_type === "alias") {
574-
this.selectAliases.add((expr as Alias).alias.toLowerCase());
578+
const alias = (expr as Alias).alias;
579+
this.selectAliases.set(alias.toLowerCase(), alias);
575580
return;
576581
}
577582

578583
// Handle implicit names from function calls (e.g., COUNT() → 'count')
584+
// ClickHouse generates implicit aliases as lowercase
579585
if ((expr as Call).expression_type === "call") {
580586
const call = expr as Call;
581-
// Aggregations and functions get implicit lowercase names
582-
this.selectAliases.add(call.name.toLowerCase());
587+
const canonicalName = call.name.toLowerCase();
588+
this.selectAliases.set(canonicalName, canonicalName);
583589
return;
584590
}
585591

586592
// Handle implicit names from arithmetic operations (e.g., a + b → 'plus')
593+
// ClickHouse generates these as lowercase
587594
if ((expr as ArithmeticOperation).expression_type === "arithmetic_operation") {
588595
const op = expr as ArithmeticOperation;
589596
const opNames: Record<ArithmeticOperationOp, string> = {
@@ -593,7 +600,8 @@ export class ClickHousePrinter {
593600
[ArithmeticOperationOp.Div]: "divide",
594601
[ArithmeticOperationOp.Mod]: "modulo",
595602
};
596-
this.selectAliases.add(opNames[op.op]);
603+
const canonicalName = opNames[op.op];
604+
this.selectAliases.set(canonicalName, canonicalName);
597605
return;
598606
}
599607

@@ -2629,9 +2637,11 @@ export class ClickHousePrinter {
26292637
}
26302638

26312639
// Check if it's a SELECT alias (e.g., from COUNT() or explicit AS)
2632-
// Case-insensitive: aliases are stored lowercase in extractSelectAlias()
2633-
if (this.selectAliases.has(columnName.toLowerCase())) {
2634-
return chain; // Valid alias reference
2640+
// Case-insensitive lookup: map key is lowercase, value is the canonical form
2641+
// that matches the alias as it appears in the generated SQL
2642+
const canonicalAlias = this.selectAliases.get(columnName.toLowerCase());
2643+
if (canonicalAlias !== undefined) {
2644+
return [canonicalAlias, ...chain.slice(1)];
26352645
}
26362646

26372647
// Check if this is an internal-only column being accessed in a user projection context

0 commit comments

Comments
 (0)