Skip to content

Commit 970b268

Browse files
committed
HHH-18731 Add generate_series() set-returning function
1 parent 044d914 commit 970b268

File tree

70 files changed

+3971
-200
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3971
-200
lines changed

documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2958,7 +2958,7 @@ The following set-returning functions are available on many platforms:
29582958
| Function | purpose
29592959
29602960
| <<hql-array-unnest,`unnest()`>> | Turns an array into rows
2961-
//| `generate_series()` | Creates a series of values as rows
2961+
| <<hql-from-set-returning-functions-generate-series,`generate_series()`>> | Creates a series of values as rows
29622962
|===
29632963
29642964
To use set returning functions defined in the database, it is required to register them in a `FunctionContributor`:
@@ -2986,6 +2986,43 @@ which is not supported on some databases for user defined functions.
29862986
Hibernate ORM tries to emulate this feature by wrapping invocations as lateral subqueries and using `row_number()`,
29872987
which may lead to worse performance.
29882988
2989+
[[hql-from-set-returning-functions-generate-series]]
2990+
==== `generate_series` set-returning function
2991+
2992+
A <<hql-from-set-returning-functions,set-returning function>>, which generates rows from a given start value (inclusive)
2993+
up to a given stop value (inclusive). The function has 2 variants:
2994+
2995+
* `generate_series(numeric, numeric [,numeric])` - Arguments are `start`, `stop` and `step` with a default of `1` for the optional `step` argument
2996+
* `generate_series(temporal, temporal, duration)` - Like the numeric variant, but for temporal types and `step` is required
2997+
2998+
[[hql-generate-series-example]]
2999+
====
3000+
[source, java, indent=0]
3001+
----
3002+
include::{srf-example-dir-hql}/GenerateSeriesTest.java[tags=hql-set-returning-function-generate-series-example]
3003+
----
3004+
====
3005+
3006+
To obtain the "row number" of a generated value i.e. ordinality, it is possible to use the `index()` function.
3007+
3008+
[[hql-generate-series-ordinality-example]]
3009+
====
3010+
[source, java, indent=0]
3011+
----
3012+
include::{srf-example-dir-hql}/GenerateSeriesTest.java[tags=hql-set-returning-function-generate-series-ordinality-example]
3013+
----
3014+
====
3015+
3016+
The `step` argument can be a negative value and progress from a higher `start` value to a lower `stop` value.
3017+
3018+
[[hql-generate-series-temporal-example]]
3019+
====
3020+
[source, java, indent=0]
3021+
----
3022+
include::{srf-example-dir-hql}/GenerateSeriesTest.java[tags=hql-set-returning-function-generate-series-temporal-example]
3023+
----
3024+
====
3025+
29893026
[[hql-join]]
29903027
=== Declaring joined entities
29913028

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
518518
functionFactory.jsonArrayInsert_postgresql();
519519

520520
functionFactory.unnest_postgresql();
521+
functionFactory.generateSeries( null, "ordinality", true );
521522

522523
// Postgres uses # instead of ^ for XOR
523524
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,18 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
458458
functionFactory.xmlagg();
459459

460460
functionFactory.unnest_emulated();
461+
if ( supportsRecursiveCTE() ) {
462+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, true );
463+
}
464+
}
465+
466+
/**
467+
* DB2 doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
468+
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
469+
* of elements that the series can return.
470+
*/
471+
protected int getMaximumSeriesSize() {
472+
return 10000;
461473
}
462474

463475
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
429429
}
430430

431431
functionFactory.unnest_h2( getMaximumArraySize() );
432+
functionFactory.generateSeries_h2( getMaximumSeriesSize() );
432433
}
433434

434435
/**
@@ -441,6 +442,16 @@ protected int getMaximumArraySize() {
441442
return 1000;
442443
}
443444

445+
/**
446+
* Since H2 doesn't support ordinality for the {@code system_range} function or {@code lateral},
447+
* it's impossible to use {@code system_range} for non-constant cases.
448+
* Luckily, correlation can be emulated, but requires that there is an upper bound on the amount
449+
* of elements that the series can return.
450+
*/
451+
protected int getMaximumSeriesSize() {
452+
return 10000;
453+
}
454+
444455
@Override
445456
public @Nullable String getDefaultOrdinalityColumnName() {
446457
return "nord";

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
497497
functionFactory.unnest_hana();
498498
// functionFactory.json_table();
499499

500+
functionFactory.generateSeries_hana( getMaximumSeriesSize() );
501+
500502
if ( getVersion().isSameOrAfter(2, 0, 20 ) ) {
501503
if ( getVersion().isSameOrAfter( 2, 0, 40 ) ) {
502504
// Introduced in 2.0 SPS 04
@@ -513,6 +515,14 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
513515
}
514516
}
515517

518+
/**
519+
* HANA doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
520+
* so it has to be emulated with the {@code xmltable} and {@code lpad} functions.
521+
*/
522+
protected int getMaximumSeriesSize() {
523+
return 10000;
524+
}
525+
516526
@Override
517527
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
518528
return new StandardSqlAstTranslatorFactory() {

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
279279
}
280280

281281
functionFactory.unnest( "c1", "c2" );
282+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), true, false );
282283

283284
//trim() requires parameters to be cast when used as trim character
284285
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
@@ -288,6 +289,16 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
288289
) );
289290
}
290291

292+
/**
293+
* HSQLDB doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
294+
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
295+
* of elements that the series can return.
296+
*/
297+
protected int getMaximumSeriesSize() {
298+
// The maximum recursion depth of HSQLDB
299+
return 258;
300+
}
301+
291302
@Override
292303
public @Nullable String getDefaultOrdinalityColumnName() {
293304
return "c2";

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,9 +676,22 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
676676
if ( getMySQLVersion().isSameOrAfter( 8 ) ) {
677677
functionFactory.unnest_emulated();
678678
}
679+
if ( supportsRecursiveCTE() ) {
680+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false );
681+
}
679682
}
680683
}
681684

685+
/**
686+
* MySQL doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
687+
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
688+
* of elements that the series can return.
689+
*/
690+
protected int getMaximumSeriesSize() {
691+
// The maximum recursion depth of MySQL
692+
return 1000;
693+
}
694+
682695
@Override
683696
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
684697
super.contributeTypes( typeContributions, serviceRegistry );

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,16 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
335335
functionFactory.xmlagg();
336336

337337
functionFactory.unnest_oracle();
338+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), true, false );
339+
}
340+
341+
/**
342+
* Oracle doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
343+
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
344+
* of elements that the series can return.
345+
*/
346+
protected int getMaximumSeriesSize() {
347+
return 10000;
338348
}
339349

340350
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
711711
else {
712712
functionFactory.unnest_postgresql();
713713
}
714+
functionFactory.generateSeries( null, "ordinality", false );
714715
}
715716

716717
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,13 +440,32 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
440440
functionFactory.leastGreatest();
441441
functionFactory.dateTrunc_datetrunc();
442442
functionFactory.trunc_round_datetrunc();
443+
functionFactory.generateSeries_sqlserver( getMaximumSeriesSize() );
443444
}
444445
else {
445446
functionContributions.getFunctionRegistry().register(
446447
"trunc",
447448
new SqlServerConvertTruncFunction( functionContributions.getTypeConfiguration() )
448449
);
449450
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
451+
if ( supportsRecursiveCTE() ) {
452+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false );
453+
}
454+
}
455+
}
456+
457+
/**
458+
* SQL Server doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
459+
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
460+
* of elements that the series can return.
461+
*/
462+
protected int getMaximumSeriesSize() {
463+
if ( getVersion().isSameOrAfter( 16 ) ) {
464+
return 10000;
465+
}
466+
else {
467+
// The maximum recursion depth of SQL Server
468+
return 100;
450469
}
451470
}
452471

0 commit comments

Comments
 (0)