def _format_groupby(query: AbstractQuery, formatter: ExpressionVisitor[str]) -> Optional[StringNode]: group_clause: Optional[StringNode] = None ast_groupby = query.get_groupby() if ast_groupby: groupby_expressions = [e.accept(formatter) for e in ast_groupby] group_clause_str = f"{', '.join(groupby_expressions)}" if query.has_totals(): group_clause_str = f"{group_clause_str} WITH TOTALS" group_clause = StringNode(f"GROUP BY {group_clause_str}") return group_clause
def _validate_groupby_fields_have_matching_conditions( query: Query, alias: Optional[str] = None) -> None: """ Method that insures that for every field in the group by clause, there should be a matching a condition. For example, if we had in our groupby clause [project_id, tags[3]], we should have the following conditions in the where clause `project_id = 3 AND tags[3] IN array(1,2,3)`. This is necessary because we want to avoid the case where an unspecified number of buckets is returned. """ condition = query.get_condition() top_level = get_first_level_and_conditions( condition) if condition else [] for exp in query.get_groupby(): key: Optional[str] = None if isinstance(exp, SubscriptableReferenceExpr): column_name = str(exp.column.column_name) key = str(exp.key.value) elif isinstance(exp, Column): column_name = exp.column_name else: raise InvalidQueryException( "Unhandled column type in group by validation") match = build_match( col=column_name, ops=[ConditionFunctions.EQ], param_type=int, alias=alias, key=key, ) found = any(match.match(cond) for cond in top_level) if not found: raise InvalidQueryException( f"Every field in groupby must have a corresponding condition in " f"where clause. missing condition for field {exp}")