Skip to content

Commit 82b20a0

Browse files
committed
HHH-18731 Add generate_series() set-returning function
1 parent 5bd244d commit 82b20a0

File tree

75 files changed

+3974
-208
lines changed

Some content is hidden

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

75 files changed

+3974
-208
lines changed

ci/build.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ elif [ "$RDBMS" == "db2_10_5" ]; then
6060
goal="-Pdb=db2"
6161
elif [ "$RDBMS" == "mssql" ] || [ "$RDBMS" == "mssql_2017" ]; then
6262
goal="-Pdb=mssql_ci"
63+
# Exclude some Sybase tests on CI because they use `xmltable` function which has a memory leak on the DB version in CI
6364
elif [ "$RDBMS" == "sybase" ]; then
64-
goal="-Pdb=sybase_ci"
65+
goal="-Pdb=sybase_ci -PexcludeTests=**.GenerateSeriesTest*"
6566
elif [ "$RDBMS" == "sybase_jconn" ]; then
66-
goal="-Pdb=sybase_jconn_ci"
67+
goal="-Pdb=sybase_jconn_ci -PexcludeTests=**.GenerateSeriesTest*"
6768
elif [ "$RDBMS" == "tidb" ]; then
6869
goal="-Pdb=tidb"
6970
elif [ "$RDBMS" == "hana_cloud" ]; then

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
@@ -428,6 +428,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
428428
}
429429

430430
functionFactory.unnest_h2( getMaximumArraySize() );
431+
functionFactory.generateSeries_h2( getMaximumSeriesSize() );
431432
}
432433

433434
/**
@@ -440,6 +441,16 @@ protected int getMaximumArraySize() {
440441
return 1000;
441442
}
442443

444+
/**
445+
* Since H2 doesn't support ordinality for the {@code system_range} function or {@code lateral},
446+
* it's impossible to use {@code system_range} for non-constant cases.
447+
* Luckily, correlation can be emulated, but requires that there is an upper bound on the amount
448+
* of elements that the series can return.
449+
*/
450+
protected int getMaximumSeriesSize() {
451+
return 10000;
452+
}
453+
443454
@Override
444455
public @Nullable String getDefaultOrdinalityColumnName() {
445456
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
@@ -675,9 +675,22 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
675675
if ( getMySQLVersion().isSameOrAfter( 8 ) ) {
676676
functionFactory.unnest_emulated();
677677
}
678+
if ( supportsRecursiveCTE() ) {
679+
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false );
680+
}
678681
}
679682
}
680683

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

0 commit comments

Comments
 (0)