Skip to content

Commit d83eadf

Browse files
committed
much better error when query has 'having' but no 'group by'
1 parent 22305de commit d83eadf

File tree

2 files changed

+28
-15
lines changed

2 files changed

+28
-15
lines changed

hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,14 @@ limitOffset
185185
*
186186
* - The 'select' clause may come first, in which case 'from' is optional
187187
* - The 'from' clause may come first, in which case 'select' is optional, and comes last
188+
* - If both 'select' and 'from' are missing, a 'where' clause on its own is allowed
189+
*
190+
* Note that 'having' is only allowed with 'group by', but we don't enforce
191+
* that in the grammar.
188192
*/
189193
query
190-
// TODO: add with clause
191-
: selectClause fromClause? whereClause? (groupByClause havingClause?)?
192-
| fromClause whereClause? (groupByClause havingClause?)? selectClause?
194+
: selectClause fromClause? whereClause? groupByClause? havingClause?
195+
| fromClause whereClause? groupByClause? havingClause? selectClause?
193196
| whereClause
194197
;
195198

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,30 +1175,40 @@ else if ( fetchClauseContext == null ) {
11751175
public SqmQuerySpec<?> visitQuery(HqlParser.QueryContext ctx) {
11761176
final SqmQuerySpec<?> sqmQuerySpec = currentQuerySpec();
11771177

1178+
final HqlParser.FromClauseContext fromClauseContext = ctx.fromClause();
1179+
final HqlParser.WhereClauseContext whereClauseContext = ctx.whereClause();
1180+
final HqlParser.GroupByClauseContext groupByClauseContext = ctx.groupByClause();
1181+
final HqlParser.HavingClauseContext havingClauseContext = ctx.havingClause();
1182+
final HqlParser.SelectClauseContext selectClauseContext = ctx.selectClause();
1183+
1184+
if ( havingClauseContext != null && groupByClauseContext == null ) {
1185+
throw new SemanticException( "Query has 'having' but no 'group by'", query );
1186+
}
1187+
11781188
// visit from clause first!!!
11791189
final SqmFromClause fromClause =
1180-
ctx.fromClause() == null
1181-
? buildInferredFromClause( ctx.selectClause() )
1182-
: visitFromClause( ctx.fromClause() );
1190+
fromClauseContext == null
1191+
? buildInferredFromClause( selectClauseContext )
1192+
: visitFromClause( fromClauseContext );
11831193
sqmQuerySpec.setFromClause( fromClause );
11841194

11851195
final SqmSelectClause selectClause =
1186-
ctx.selectClause() == null
1196+
selectClauseContext == null
11871197
? buildInferredSelectClause( fromClause )
1188-
: visitSelectClause( ctx.selectClause() );
1198+
: visitSelectClause( selectClauseContext );
11891199
sqmQuerySpec.setSelectClause( selectClause );
11901200

1191-
final SqmWhereClause whereClause = new SqmWhereClause( creationContext.getNodeBuilder() );
1192-
if ( ctx.whereClause() != null ) {
1193-
whereClause.setPredicate( (SqmPredicate) ctx.whereClause().accept( this ) );
1201+
final SqmWhereClause whereClause = new SqmWhereClause( nodeBuilder() );
1202+
if ( whereClauseContext != null ) {
1203+
whereClause.setPredicate( (SqmPredicate) whereClauseContext.accept( this ) );
11941204
}
11951205
sqmQuerySpec.setWhereClause( whereClause );
11961206

1197-
if ( ctx.groupByClause() != null ) {
1198-
sqmQuerySpec.setGroupByClauseExpressions( visitGroupByClause( ctx.groupByClause() ) );
1207+
if ( groupByClauseContext != null ) {
1208+
sqmQuerySpec.setGroupByClauseExpressions( visitGroupByClause( groupByClauseContext ) );
11991209
}
1200-
if ( ctx.havingClause() != null ) {
1201-
sqmQuerySpec.setHavingClausePredicate( visitHavingClause( ctx.havingClause() ) );
1210+
if ( havingClauseContext != null ) {
1211+
sqmQuerySpec.setHavingClausePredicate( visitHavingClause( havingClauseContext ) );
12021212
}
12031213

12041214
return sqmQuerySpec;

0 commit comments

Comments
 (0)