@@ -326,14 +326,17 @@ def _execute_aggregate_select(
326326 )
327327
328328 output_columns : list [str ] = []
329+ required_aggregates : dict [str , tuple [str , str ]] = {}
329330 for column in columns :
330331 if self ._is_aggregate_column (column ):
331332 arg = str (column .get ("arg" ))
332333 if arg != "*" and arg not in headers :
333334 raise ValueError (
334335 f"Unknown column: { arg } . Available columns: { headers } "
335336 )
336- output_columns .append (self ._aggregate_label (column ))
337+ label = self ._aggregate_label (column )
338+ required_aggregates [label ] = (str (column .get ("func" )), arg )
339+ output_columns .append (label )
337340 continue
338341
339342 column_name = str (column )
@@ -351,6 +354,24 @@ def _execute_aggregate_select(
351354 )
352355 output_columns .append (column_name )
353356
357+ for clause in (having , parsed .get ("order_by" )):
358+ if clause is None :
359+ continue
360+ if "conditions" in clause :
361+ refs = [str (condition ["column" ]) for condition in clause ["conditions" ]]
362+ else :
363+ refs = [str (clause ["column" ])]
364+ for ref in refs :
365+ aggregate_spec = self ._aggregate_spec_from_label (ref )
366+ if aggregate_spec is None :
367+ continue
368+ func , arg = aggregate_spec
369+ if arg != "*" and arg not in headers :
370+ raise ValueError (
371+ f"Unknown column: { arg } . Available columns: { headers } "
372+ )
373+ required_aggregates [ref ] = (func , arg )
374+
354375 grouped_rows : list [dict [str , Any ]] = []
355376 if group_by :
356377 groups : dict [tuple [Any , ...], list [dict [str , Any ]]] = {}
@@ -360,33 +381,18 @@ def _execute_aggregate_select(
360381
361382 for group_key , group_values in groups .items ():
362383 group_map = dict (zip (group_by , group_key ))
363- output_row : dict [str , Any ] = {}
364- for column in columns :
365- if self ._is_aggregate_column (column ):
366- label = self ._aggregate_label (column )
367- output_row [label ] = self ._compute_aggregate (
368- str (column .get ("func" )),
369- str (column .get ("arg" )),
370- group_values ,
371- )
372- else :
373- col_name = str (column )
374- output_row [col_name ] = group_map .get (col_name )
375- if having and not self ._matches_where (output_row , having ):
384+ context_row : dict [str , Any ] = dict (group_map )
385+ for label , (func , arg ) in required_aggregates .items ():
386+ context_row [label ] = self ._compute_aggregate (func , arg , group_values )
387+ if having and not self ._matches_where (context_row , having ):
376388 continue
377- grouped_rows .append (output_row )
389+ grouped_rows .append (context_row )
378390 else :
379- output_row_single : dict [str , Any ] = {}
380- for column in columns :
381- if self ._is_aggregate_column (column ):
382- label = self ._aggregate_label (column )
383- output_row_single [label ] = self ._compute_aggregate (
384- str (column .get ("func" )),
385- str (column .get ("arg" )),
386- rows ,
387- )
388- if not having or self ._matches_where (output_row_single , having ):
389- grouped_rows .append (output_row_single )
391+ context_row_single : dict [str , Any ] = {}
392+ for label , (func , arg ) in required_aggregates .items ():
393+ context_row_single [label ] = self ._compute_aggregate (func , arg , rows )
394+ if not having or self ._matches_where (context_row_single , having ):
395+ grouped_rows .append (context_row_single )
390396
391397 distinct = parsed .get ("distinct" , False )
392398 if distinct :
@@ -402,9 +408,13 @@ def _execute_aggregate_select(
402408 order_by = parsed .get ("order_by" )
403409 if order_by :
404410 order_column = str (order_by ["column" ])
405- if order_column not in output_columns :
411+ available_order_columns = set (output_columns )
412+ if group_by :
413+ available_order_columns .update (group_by )
414+ available_order_columns .update (required_aggregates .keys ())
415+ if order_column not in available_order_columns :
406416 raise ValueError (
407- f"Unknown column: { order_column } . Available columns: { output_columns } "
417+ f"Unknown column: { order_column } . Available columns: { sorted ( available_order_columns ) } "
408418 )
409419 reverse = order_by ["direction" ] == "DESC"
410420 grouped_rows = sorted (
@@ -445,6 +455,16 @@ def _is_aggregate_column(self, column: Any) -> bool:
445455 def _aggregate_label (self , aggregate : dict [str , Any ]) -> str :
446456 return f"{ str (aggregate ['func' ]).upper ()} ({ aggregate ['arg' ]} )"
447457
458+ def _aggregate_spec_from_label (self , expression : str ) -> tuple [str , str ] | None :
459+ match = re .fullmatch (r"(?i)(COUNT|SUM|AVG|MIN|MAX)\(([^\)]+)\)" , expression .strip ())
460+ if not match :
461+ return None
462+ func = match .group (1 ).upper ()
463+ arg = match .group (2 ).strip ()
464+ if not arg :
465+ return None
466+ return func , arg
467+
448468 def _compute_aggregate (
449469 self , func : str , arg : str , rows : list [dict [str , Any ]]
450470 ) -> Any :
0 commit comments