48
48
import java .util .List ;
49
49
import java .util .Set ;
50
50
import java .util .StringJoiner ;
51
+ import java .util .function .Predicate ;
52
+ import java .util .function .Supplier ;
51
53
52
54
import org .jspecify .annotations .Nullable ;
53
55
54
56
import org .springframework .data .domain .Sort ;
57
+ import org .springframework .data .util .Predicates ;
55
58
import org .springframework .util .Assert ;
56
59
import org .springframework .util .CollectionUtils ;
57
60
import org .springframework .util .ObjectUtils ;
@@ -149,24 +152,8 @@ static <T extends Statement> T parseStatement(String sql, Class<T> classOfT) {
149
152
150
153
if (ParsedType .SELECT .equals (parsedType )) {
151
154
152
- Select selectStatement = (Select ) statement ;
153
-
154
- /*
155
- * For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide
156
- * alias since:
157
- * ValuesStatement has no alias
158
- * SetOperation can have multiple alias for each operation item
159
- */
160
- if (!(selectStatement instanceof PlainSelect selectBody )) {
161
- return null ;
162
- }
163
-
164
- if (selectBody .getFromItem () == null ) {
165
- return null ;
166
- }
167
-
168
- Alias alias = selectBody .getFromItem ().getAlias ();
169
- return alias == null ? null : alias .getName ();
155
+ return doWithPlainSelect (statement , it -> it .getFromItem () == null || it .getFromItem ().getAlias () == null ,
156
+ it -> it .getFromItem ().getAlias ().getName (), () -> null );
170
157
}
171
158
172
159
return null ;
@@ -179,20 +166,24 @@ static <T extends Statement> T parseStatement(String sql, Class<T> classOfT) {
179
166
*/
180
167
private static Set <String > getSelectionAliases (Statement statement ) {
181
168
182
- if (!( statement instanceof PlainSelect select ) || CollectionUtils . isEmpty ( select . getSelectItems ()) ) {
183
- return Collections . emptySet ( );
169
+ if (statement instanceof SetOperationList sel ) {
170
+ statement = sel . getSelect ( 0 );
184
171
}
185
172
186
- Set <String > set = new HashSet <>(select .getSelectItems ().size ());
173
+ return doWithPlainSelect (statement , it -> CollectionUtils .isEmpty (it .getSelectItems ()), it -> {
174
+
175
+ Set <String > set = new HashSet <>(it .getSelectItems ().size (), 1.0f );
187
176
188
- for (SelectItem <?> selectItem : select .getSelectItems ()) {
189
- Alias alias = selectItem .getAlias ();
190
- if (alias != null ) {
191
- set .add (alias .getName ());
177
+ for (SelectItem <?> selectItem : it .getSelectItems ()) {
178
+ Alias alias = selectItem .getAlias ();
179
+ if (alias != null ) {
180
+ set .add (alias .getName ());
181
+ }
192
182
}
193
- }
194
183
195
- return set ;
184
+ return set ;
185
+
186
+ }, Collections ::emptySet );
196
187
}
197
188
198
189
/**
@@ -202,21 +193,74 @@ private static Set<String> getSelectionAliases(Statement statement) {
202
193
*/
203
194
private static Set <String > getJoinAliases (Statement statement ) {
204
195
205
- if (!( statement instanceof PlainSelect selectBody ) || CollectionUtils . isEmpty ( selectBody . getJoins ()) ) {
206
- return Collections . emptySet ( );
196
+ if (statement instanceof SetOperationList sel ) {
197
+ statement = sel . getSelect ( 0 );
207
198
}
208
199
209
- Set < String > set = new HashSet <>( selectBody .getJoins (). size ());
200
+ return doWithPlainSelect ( statement , it -> CollectionUtils . isEmpty ( it .getJoins ()), it -> {
210
201
211
- for (Join join : selectBody .getJoins ()) {
212
- Alias alias = join .getRightItem ().getAlias ();
213
- if (alias != null ) {
214
- set .add (alias .getName ());
202
+ Set <String > set = new HashSet <>(it .getJoins ().size (), 1.0f );
203
+
204
+ for (Join join : it .getJoins ()) {
205
+ Alias alias = join .getRightItem ().getAlias ();
206
+ if (alias != null ) {
207
+ set .add (alias .getName ());
208
+ }
215
209
}
210
+ return set ;
211
+
212
+ }, Collections ::emptySet );
213
+ }
214
+
215
+ /**
216
+ * Apply a {@link java.util.function.Function mapping function} to the {@link PlainSelect} of the given
217
+ * {@link Statement} is or contains a {@link PlainSelect}.
218
+ *
219
+ * @param statement
220
+ * @param mapper
221
+ * @param fallback
222
+ * @return
223
+ * @param <T>
224
+ */
225
+ private static <T > T doWithPlainSelect (Statement statement , java .util .function .Function <PlainSelect , T > mapper ,
226
+ Supplier <T > fallback ) {
227
+
228
+ Predicate <PlainSelect > neverSkip = Predicates .isFalse ();
229
+ return doWithPlainSelect (statement , neverSkip , mapper , fallback );
230
+ }
231
+
232
+ /**
233
+ * Apply a {@link java.util.function.Function mapping function} to the {@link PlainSelect} of the given
234
+ * {@link Statement} is or contains a {@link PlainSelect}.
235
+ * <p>
236
+ * The operation is only applied if {@link Predicate skipIf} returns {@literal false} for the given statement
237
+ * returning the fallback value from {@code fallback}.
238
+ *
239
+ * @param statement
240
+ * @param skipIf
241
+ * @param mapper
242
+ * @param fallback
243
+ * @return
244
+ * @param <T>
245
+ */
246
+ private static <T > T doWithPlainSelect (Statement statement , Predicate <PlainSelect > skipIf ,
247
+ java .util .function .Function <PlainSelect , T > mapper , Supplier <T > fallback ) {
248
+
249
+ if (!(statement instanceof Select select )) {
250
+ return fallback .get ();
216
251
}
217
252
218
- return set ;
253
+ try {
254
+ if (skipIf .test (select .getPlainSelect ())) {
255
+ return fallback .get ();
256
+ }
257
+ }
258
+ // e.g. SetOperationList is a subclass of Select but it is not a PlainSelect
259
+ catch (ClassCastException e ) {
260
+ return fallback .get ();
261
+ }
219
262
263
+ return mapper .apply (select .getPlainSelect ());
220
264
}
221
265
222
266
private static String detectProjection (Statement statement ) {
@@ -235,18 +279,17 @@ private static String detectProjection(Statement statement) {
235
279
236
280
// using the first one since for setoperations the projection has to be the same
237
281
selectBody = setOperationList .getSelects ().get (0 );
238
-
239
- if (!(selectBody instanceof PlainSelect )) {
240
- return "" ;
241
- }
242
282
}
243
283
244
- StringJoiner joiner = new StringJoiner (", " );
245
- for (SelectItem <?> selectItem : selectBody .getPlainSelect ().getSelectItems ()) {
246
- joiner .add (selectItem .toString ());
247
- }
248
- return joiner .toString ().trim ();
284
+ return doWithPlainSelect (selectBody , it -> CollectionUtils .isEmpty (it .getSelectItems ()), it -> {
285
+
286
+ StringJoiner joiner = new StringJoiner (", " );
287
+ for (SelectItem <?> selectItem : it .getSelectItems ()) {
288
+ joiner .add (selectItem .toString ());
289
+ }
290
+ return joiner .toString ().trim ();
249
291
292
+ }, () -> "" );
250
293
}
251
294
252
295
/**
@@ -317,24 +360,22 @@ private String applySorting(@Nullable Select selectStatement, Sort sort, @Nullab
317
360
return applySortingToSetOperationList (setOperationList , sort );
318
361
}
319
362
320
- if (!(selectStatement instanceof PlainSelect selectBody )) {
321
- if (selectStatement != null ) {
322
- return selectStatement .toString ();
363
+ doWithPlainSelect (selectStatement , it -> {
364
+
365
+ List <OrderByElement > orderByElements = new ArrayList <>(16 );
366
+ for (Sort .Order order : sort ) {
367
+ orderByElements .add (getOrderClause (joinAliases , selectAliases , alias , order ));
368
+ }
369
+
370
+ if (CollectionUtils .isEmpty (it .getOrderByElements ())) {
371
+ it .setOrderByElements (orderByElements );
323
372
} else {
324
- throw new IllegalArgumentException ( "Select must not be null" );
373
+ it . getOrderByElements (). addAll ( orderByElements );
325
374
}
326
- }
327
375
328
- List <OrderByElement > orderByElements = new ArrayList <>(16 );
329
- for (Sort .Order order : sort ) {
330
- orderByElements .add (getOrderClause (joinAliases , selectAliases , alias , order ));
331
- }
376
+ return null ;
332
377
333
- if (CollectionUtils .isEmpty (selectBody .getOrderByElements ())) {
334
- selectBody .setOrderByElements (orderByElements );
335
- } else {
336
- selectBody .getOrderByElements ().addAll (orderByElements );
337
- }
378
+ }, () -> "" );
338
379
339
380
return selectStatement .toString ();
340
381
}
@@ -349,14 +390,9 @@ public String createCountQueryFor(@Nullable String countProjection) {
349
390
Assert .hasText (this .query .getQueryString (), "OriginalQuery must not be null or empty" );
350
391
351
392
Statement statement = (Statement ) deserialize (this .serialized );
352
- /*
353
- We only support count queries for {@link PlainSelect}.
354
- */
355
- if (!(statement instanceof PlainSelect selectBody )) {
356
- return this .query .getQueryString ();
357
- }
358
393
359
- return createCountQueryFor (selectBody , countProjection , primaryAlias );
394
+ return doWithPlainSelect (statement , it -> createCountQueryFor (it , countProjection , primaryAlias ),
395
+ this .query ::getQueryString );
360
396
}
361
397
362
398
private static String createCountQueryFor (PlainSelect selectBody , @ Nullable String countProjection ,
0 commit comments