Skip to content

Commit aa6fb4d

Browse files
author
Mike Mannion
committed
HHH-19704 Added handling of LISTAGG function variants.
1 parent bddc144 commit aa6fb4d

File tree

2 files changed

+405
-1
lines changed

2 files changed

+405
-1
lines changed

hibernate-core/src/main/java/org/hibernate/sql/Template.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ public final class Template {
7878
"minus",
7979
"except",
8080
"intersect",
81-
"partition");
81+
"partition",
82+
"within");
8283
private static final Set<String> BEFORE_TABLE_KEYWORDS
8384
= Set.of("from", "join");
8485
private static final Set<String> FUNCTION_KEYWORDS
@@ -93,6 +94,13 @@ public final class Template {
9394
= Set.of("first", "next");
9495
private static final Set<String> CURRENT_BIGRAMS
9596
= Set.of("date", "time", "timestamp");
97+
// Ordered-set aggregate function names we want to recognize
98+
private static final Set<String> ORDERED_SET_AGGREGATES
99+
= Set.of("listagg", "percentile_cont", "percentile_disc", "mode");
100+
// Soft keywords that are only treated as keywords in the LISTAGG extension immediately
101+
// following the argument list and up to and including GROUP
102+
private static final Set<String> LISTAGG_EXTENSION_KEYWORDS
103+
= Set.of("on", "overflow", "error", "truncate", "without", "count", "within", "with", "group");
96104

97105
private static final String PUNCTUATION = "=><!+-*/()',|&`";
98106

@@ -172,6 +180,12 @@ public static String renderWhereStringTemplate(
172180
boolean afterCastAs = false;
173181
boolean afterFetch = false;
174182
boolean afterCurrent = false;
183+
// State for ordered-set aggregates / LISTAGG extension handling
184+
boolean inOrderedSetFunction = false;
185+
int orderedSetParenDepth = 0;
186+
boolean afterOrderedSetArgs = false;
187+
boolean inListaggExtension = false;
188+
boolean lastWasListagg = false;
175189

176190
boolean hasMore = tokens.hasMoreTokens();
177191
String nextToken = hasMore ? tokens.nextToken() : null;
@@ -232,6 +246,7 @@ else if ( quotedIdentifier && dialect.closeQuote()==token.charAt(0) ) {
232246
final String processedToken;
233247
final boolean isQuoted =
234248
quoted || quotedIdentifier || isQuoteCharacter;
249+
235250
if ( isQuoted || isWhitespace ) {
236251
processedToken = token;
237252
}
@@ -245,12 +260,23 @@ else if ( afterFromTable ) {
245260
processedToken = token;
246261
}
247262
else if ( "(".equals(lcToken) ) {
263+
if ( inOrderedSetFunction ) {
264+
orderedSetParenDepth++;
265+
}
248266
processedToken = token;
249267
}
250268
else if ( ")".equals(lcToken) ) {
251269
inExtractOrTrim = false;
252270
inCast = false;
253271
afterCastAs = false;
272+
if ( inOrderedSetFunction ) {
273+
orderedSetParenDepth--;
274+
if ( orderedSetParenDepth == 0 ) {
275+
inOrderedSetFunction = false;
276+
afterOrderedSetArgs = true;
277+
inListaggExtension = lastWasListagg;
278+
}
279+
}
254280
processedToken = token;
255281
}
256282
else if ( ",".equals(lcToken) ) {
@@ -303,11 +329,31 @@ else if ( isFunctionCall( nextToken, sql, symbols, tokens ) ) {
303329
if ( "cast".equals( lcToken ) ) {
304330
inCast = true;
305331
}
332+
if ( ORDERED_SET_AGGREGATES.contains( lcToken ) ) {
333+
inOrderedSetFunction = true;
334+
orderedSetParenDepth = 0;
335+
lastWasListagg = "listagg".equals( lcToken );
336+
}
337+
processedToken = token;
338+
}
339+
else if ( afterOrderedSetArgs && (inListaggExtension
340+
? ( LISTAGG_EXTENSION_KEYWORDS.contains( lcToken ) )
341+
: "within".equals( lcToken )) ) {
342+
if ( "group".equals( lcToken ) ) {
343+
// end special handling after GROUP (inclusive)
344+
afterOrderedSetArgs = false;
345+
inListaggExtension = false;
346+
}
306347
processedToken = token;
307348
}
308349
else if ( isAliasableIdentifier( token, lcToken, nextToken,
309350
sql, symbols, tokens, wasAfterCurrent,
310351
dialect, typeConfiguration ) ) {
352+
// Any aliasable identifier here cannot be one of the soft keywords allowed in the
353+
// ordered-set/LISTAGG post-args region. We've left that region so must end special handling.
354+
// (It's irrelevant at this point whether the dialect supports ordered-set/LISTAGG.)
355+
afterOrderedSetArgs = false;
356+
inListaggExtension = false;
311357
processedToken = alias + '.' + dialect.quote(token);
312358
}
313359
else {

0 commit comments

Comments
 (0)