@@ -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