Skip to content

Commit 2b96c80

Browse files
committed
Don't use expressions in the group by clause
1 parent b4b476e commit 2b96c80

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,37 @@ describe("WHERE transform (whereTransform)", () => {
987987
expect(Object.values(params)).toContain("a");
988988
expect(Object.values(params)).toContain("b");
989989
});
990+
991+
it("should use raw column in GROUP BY for columns with whereTransform", () => {
992+
const ctx = createPrefixedContext();
993+
const { sql } = printQuery(
994+
"SELECT batch_id, COUNT() as count FROM runs GROUP BY batch_id",
995+
ctx
996+
);
997+
998+
// SELECT should use the expression (adds prefix)
999+
expect(sql).toContain("if(batch_id = '', NULL, concat('batch_', batch_id))");
1000+
1001+
// GROUP BY should use table-qualified raw column (not the expression)
1002+
// This avoids the "not in GROUP BY keys" error from ClickHouse
1003+
expect(sql).toMatch(/GROUP BY.*`?runs`?\.`?batch_id`?/);
1004+
expect(sql).not.toMatch(/GROUP BY.*concat\('batch_'/);
1005+
});
1006+
1007+
it("should work with GROUP BY and WHERE together", () => {
1008+
const ctx = createPrefixedContext();
1009+
const { sql, params } = printQuery(
1010+
"SELECT batch_id, COUNT() as count FROM runs WHERE batch_id != NULL GROUP BY batch_id",
1011+
ctx
1012+
);
1013+
1014+
// SELECT should use expression
1015+
expect(sql).toContain("if(batch_id = '', NULL, concat('batch_', batch_id))");
1016+
1017+
// Both WHERE and GROUP BY should use raw column
1018+
expect(sql).toMatch(/WHERE.*`?runs`?\.`?batch_id`?/);
1019+
expect(sql).toMatch(/GROUP BY.*`?runs`?\.`?batch_id`?/);
1020+
});
9901021
});
9911022

9921023
describe("Edge cases", () => {

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ export class ClickHousePrinter {
107107
private tableContexts: Map<string, TableSchema> = new Map();
108108
/** Column metadata collected during SELECT processing */
109109
private outputColumns: OutputColumnMetadata[] = [];
110+
/** Whether we're currently processing GROUP BY expressions */
111+
private inGroupByContext = false;
110112

111113
constructor(
112114
private context: PrinterContext,
@@ -359,7 +361,15 @@ export class ClickHousePrinter {
359361
// Process other clauses
360362
const prewhere = node.prewhere ? this.visit(node.prewhere) : null;
361363
const whereStr = where ? this.visit(where) : null;
362-
const groupBy = node.group_by ? node.group_by.map((col) => this.visit(col)) : null;
364+
365+
// Process GROUP BY with context flag to use raw columns for whereTransform columns
366+
let groupBy: string[] | null = null;
367+
if (node.group_by) {
368+
this.inGroupByContext = true;
369+
groupBy = node.group_by.map((col) => this.visit(col));
370+
this.inGroupByContext = false;
371+
}
372+
363373
const having = node.having ? this.visit(node.having) : null;
364374
const orderBy = node.order_by ? node.order_by.map((col) => this.visit(col)) : null;
365375

@@ -1669,9 +1679,16 @@ export class ClickHousePrinter {
16691679
}
16701680

16711681
/**
1672-
* Check if we're currently inside a comparison operation (WHERE context)
1682+
* Check if we're in a context where we should use raw column names
1683+
* instead of virtual column expressions (WHERE comparisons or GROUP BY)
16731684
*/
16741685
private isInComparisonContext(): boolean {
1686+
// Check if we're in GROUP BY context
1687+
if (this.inGroupByContext) {
1688+
return true;
1689+
}
1690+
1691+
// Check if we're inside a comparison operation (WHERE/HAVING context)
16751692
for (const node of this.stack) {
16761693
if ((node as CompareOperation).expression_type === "compare_operation") {
16771694
return true;

0 commit comments

Comments
 (0)