Skip to content

Commit 0e18a54

Browse files
committed
HHH-18886 Upgrade PostgreSQL and EDB testing to version 17
1 parent 6141168 commit 0e18a54

File tree

10 files changed

+176
-79
lines changed

10 files changed

+176
-79
lines changed

docker_db.sh

+16-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ mariadb_verylatest() {
151151
}
152152

153153
postgresql() {
154-
postgresql_16
154+
postgresql_17
155155
}
156156

157157
postgresql_12() {
@@ -186,8 +186,15 @@ postgresql_16() {
186186
$CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-16-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"'
187187
}
188188

189+
postgresql_17() {
190+
$CONTAINER_CLI rm -f postgres || true
191+
$CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 --tmpfs /pgtmpfs:size=131072k -d ${DB_IMAGE_POSTGRESQL_17:-docker.io/postgis/postgis:17-3.5} \
192+
-c fsync=off -c synchronous_commit=off -c full_page_writes=off -c shared_buffers=256MB -c maintenance_work_mem=256MB -c max_wal_size=1GB -c checkpoint_timeout=1d
193+
$CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-17-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"'
194+
}
195+
189196
edb() {
190-
edb_16
197+
edb_17
191198
}
192199

193200
edb_12() {
@@ -218,6 +225,13 @@ edb_16() {
218225
$CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:16
219226
}
220227

228+
edb_17() {
229+
$CONTAINER_CLI rm -f edb || true
230+
# We need to build a derived image because the existing image is mainly made for use by a kubernetes operator
231+
(cd edb; $CONTAINER_CLI build -t edb-test:17 -f edb17.Dockerfile .)
232+
$CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:17
233+
}
234+
221235
db2() {
222236
db2_11_5
223237
}

edb/edb17.Dockerfile

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM quay.io/enterprisedb/edb-postgres-advanced:17.4-3.5-postgis
2+
USER root
3+
# this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)
4+
RUN chown -R postgres:postgres /var/lib/edb && chmod 777 /var/lib/edb && rm /docker-entrypoint-initdb.d/10_postgis.sh
5+
6+
USER postgres
7+
ENV LANG en_US.utf8
8+
ENV PG_MAJOR 17
9+
ENV PG_VERSION 17
10+
ENV PGPORT 5444
11+
ENV PGDATA /var/lib/edb/as$PG_MAJOR/data/
12+
VOLUME /var/lib/edb/as$PG_MAJOR/data/
13+
14+
COPY docker-entrypoint.sh /usr/local/bin/
15+
ENTRYPOINT ["docker-entrypoint.sh"]
16+
17+
# We set the default STOPSIGNAL to SIGINT, which corresponds to what PostgreSQL
18+
# calls "Fast Shutdown mode" wherein new connections are disallowed and any
19+
# in-progress transactions are aborted, allowing PostgreSQL to stop cleanly and
20+
# flush tables to disk, which is the best compromise available to avoid data
21+
# corruption.
22+
#
23+
# Users who know their applications do not keep open long-lived idle connections
24+
# may way to use a value of SIGTERM instead, which corresponds to "Smart
25+
# Shutdown mode" in which any existing sessions are allowed to finish and the
26+
# server stops when all sessions are terminated.
27+
#
28+
# See https://www.postgresql.org/docs/12/server-shutdown.html for more details
29+
# about available PostgreSQL server shutdown signals.
30+
#
31+
# See also https://www.postgresql.org/docs/12/server-start.html for further
32+
# justification of this as the default value, namely that the example (and
33+
# shipped) systemd service files use the "Fast Shutdown mode" for service
34+
# termination.
35+
#
36+
STOPSIGNAL SIGINT
37+
#
38+
# An additional setting that is recommended for all users regardless of this
39+
# value is the runtime "--stop-timeout" (or your orchestrator/runtime's
40+
# equivalent) for controlling how long to wait between sending the defined
41+
# STOPSIGNAL and sending SIGKILL (which is likely to cause data corruption).
42+
#
43+
# The default in most runtimes (such as Docker) is 10 seconds, and the
44+
# documentation at https://www.postgresql.org/docs/12/server-start.html notes
45+
# that even 90 seconds may not be long enough in many instances.
46+
47+
EXPOSE 5444
48+
CMD ["postgres"]

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
519519
functionFactory.jsonArrayAppend_postgresql( false );
520520
functionFactory.jsonArrayInsert_postgresql();
521521

522-
functionFactory.unnest_postgresql();
522+
functionFactory.unnest_postgresql( false );
523523
functionFactory.generateSeries( null, "ordinality", true );
524524
functionFactory.jsonTable_cockroachdb();
525525

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

+3-8
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
650650
functionFactory.arrayToString_postgresql();
651651

652652
if ( getVersion().isSameOrAfter( 17 ) ) {
653-
functionFactory.jsonValue();
653+
functionFactory.jsonValue_postgresql( true );
654654
functionFactory.jsonQuery();
655655
functionFactory.jsonExists();
656656
functionFactory.jsonObject();
@@ -660,7 +660,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
660660
functionFactory.jsonTable();
661661
}
662662
else {
663-
functionFactory.jsonValue_postgresql();
663+
functionFactory.jsonValue_postgresql( false );
664664
functionFactory.jsonQuery_postgresql();
665665
functionFactory.jsonExists_postgresql();
666666
if ( getVersion().isSameOrAfter( 16 ) ) {
@@ -726,12 +726,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
726726
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
727727
functionFactory.dateTrunc();
728728

729-
if ( getVersion().isSameOrAfter( 17 ) ) {
730-
functionFactory.unnest( null, "ordinality" );
731-
}
732-
else {
733-
functionFactory.unnest_postgresql();
734-
}
729+
functionFactory.unnest_postgresql( getVersion().isSameOrAfter( 17 ) );
735730
functionFactory.generateSeries( null, "ordinality", false );
736731
}
737732

hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
487487
functionFactory.jsonArrayAppend_postgresql( false );
488488
functionFactory.jsonArrayInsert_postgresql();
489489

490-
functionFactory.unnest_postgresql();
490+
functionFactory.unnest_postgresql( false );
491491
functionFactory.generateSeries( null, "ordinality", true );
492492
functionFactory.jsonTable_cockroachdb();
493493

hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java

+3-8
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
614614
functionFactory.arrayToString_postgresql();
615615

616616
if ( getVersion().isSameOrAfter( 17 ) ) {
617-
functionFactory.jsonValue();
617+
functionFactory.jsonValue_postgresql( true );
618618
functionFactory.jsonQuery();
619619
functionFactory.jsonExists();
620620
functionFactory.jsonObject();
@@ -624,7 +624,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
624624
functionFactory.jsonTable();
625625
}
626626
else {
627-
functionFactory.jsonValue_postgresql();
627+
functionFactory.jsonValue_postgresql( false );
628628
functionFactory.jsonQuery_postgresql();
629629
functionFactory.jsonExists_postgresql();
630630
if ( getVersion().isSameOrAfter( 16 ) ) {
@@ -688,12 +688,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
688688
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
689689
functionFactory.dateTrunc();
690690

691-
if ( getVersion().isSameOrAfter( 17 ) ) {
692-
functionFactory.unnest( null, "ordinality" );
693-
}
694-
else {
695-
functionFactory.unnest_postgresql();
696-
}
691+
functionFactory.unnest_postgresql( getVersion().isSameOrAfter( 17 ) );
697692
functionFactory.generateSeries( null, "ordinality", false );
698693

699694
functionFactory.hex( "encode(?1, 'hex')" );

hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java

+4-11
Original file line numberDiff line numberDiff line change
@@ -3319,13 +3319,6 @@ public void arrayToString_oracle() {
33193319
functionRegistry.register( "array_to_string", new OracleArrayToStringFunction( typeConfiguration ) );
33203320
}
33213321

3322-
/**
3323-
* json_value() function
3324-
*/
3325-
public void jsonValue() {
3326-
functionRegistry.register( "json_value", new JsonValueFunction( typeConfiguration, true, true ) );
3327-
}
3328-
33293322
/**
33303323
* HANA json_value() function
33313324
*/
@@ -3350,8 +3343,8 @@ public void jsonValue_db2() {
33503343
/**
33513344
* PostgreSQL json_value() function
33523345
*/
3353-
public void jsonValue_postgresql() {
3354-
functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( typeConfiguration ) );
3346+
public void jsonValue_postgresql(boolean supportsStandard) {
3347+
functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( supportsStandard, typeConfiguration ) );
33553348
}
33563349

33573350
/**
@@ -4232,8 +4225,8 @@ public void unnest_oracle() {
42324225
/**
42334226
* PostgreSQL unnest() function
42344227
*/
4235-
public void unnest_postgresql() {
4236-
functionRegistry.register( "unnest", new PostgreSQLUnnestFunction() );
4228+
public void unnest_postgresql(boolean supportsJsonTable) {
4229+
functionRegistry.register( "unnest", new PostgreSQLUnnestFunction( supportsJsonTable ) );
42374230
}
42384231

42394232
/**

hibernate-core/src/main/java/org/hibernate/dialect/function/array/PostgreSQLUnnestFunction.java

+49-33
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
*/
2424
public class PostgreSQLUnnestFunction extends UnnestFunction {
2525

26-
public PostgreSQLUnnestFunction() {
26+
private final boolean supportsJsonTable;
27+
28+
public PostgreSQLUnnestFunction(boolean supportsJsonTable) {
2729
super( null, "ordinality" );
30+
this.supportsJsonTable = supportsJsonTable;
2831
}
2932

3033
@Override
@@ -36,41 +39,54 @@ protected void renderJsonTable(
3639
AnonymousTupleTableGroupProducer tupleType,
3740
String tableIdentifierVariable,
3841
SqlAstTranslator<?> walker) {
39-
final AggregateSupport aggregateSupport = walker.getSessionFactory().getJdbcServices().getDialect()
40-
.getAggregateSupport();
41-
sqlAppender.appendSql( "(select" );
42-
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
43-
if ( selectionIndex == 0 ) {
44-
sqlAppender.append( ' ' );
45-
}
46-
else {
47-
sqlAppender.append( ',' );
48-
}
49-
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
50-
sqlAppender.appendSql( "t.i" );
42+
if ( supportsJsonTable ) {
43+
super.renderJsonTable(
44+
sqlAppender,
45+
array,
46+
pluralType,
47+
sqlTypedMapping,
48+
tupleType,
49+
tableIdentifierVariable,
50+
walker
51+
);
52+
}
53+
else {
54+
final AggregateSupport aggregateSupport = walker.getSessionFactory().getJdbcServices().getDialect()
55+
.getAggregateSupport();
56+
sqlAppender.appendSql( "(select" );
57+
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
58+
if ( selectionIndex == 0 ) {
59+
sqlAppender.append( ' ' );
60+
}
61+
else {
62+
sqlAppender.append( ',' );
63+
}
64+
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
65+
sqlAppender.appendSql( "t.i" );
66+
}
67+
else {
68+
sqlAppender.append( aggregateSupport.aggregateComponentCustomReadExpression(
69+
"",
70+
"",
71+
"t.v",
72+
selectableMapping.getSelectableName(),
73+
SqlTypes.JSON,
74+
selectableMapping,
75+
walker.getSessionFactory().getTypeConfiguration()
76+
) );
77+
}
78+
sqlAppender.append( " as " );
79+
sqlAppender.append( selectableMapping.getSelectionExpression() );
80+
} );
81+
sqlAppender.appendSql( " from jsonb_array_elements(" );
82+
array.accept( walker );
83+
sqlAppender.appendSql( ')' );
84+
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
85+
sqlAppender.appendSql( " with ordinality t(v,i))" );
5186
}
5287
else {
53-
sqlAppender.append( aggregateSupport.aggregateComponentCustomReadExpression(
54-
"",
55-
"",
56-
"t.v",
57-
selectableMapping.getSelectableName(),
58-
SqlTypes.JSON,
59-
selectableMapping,
60-
walker.getSessionFactory().getTypeConfiguration()
61-
) );
88+
sqlAppender.appendSql( " t(v))" );
6289
}
63-
sqlAppender.append( " as " );
64-
sqlAppender.append( selectableMapping.getSelectionExpression() );
65-
} );
66-
sqlAppender.appendSql( " from jsonb_array_elements(" );
67-
array.accept( walker );
68-
sqlAppender.appendSql( ')' );
69-
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
70-
sqlAppender.appendSql( " with ordinality t(v,i))" );
71-
}
72-
else {
73-
sqlAppender.appendSql( " t(v))" );
7490
}
7591
}
7692

hibernate-core/src/main/java/org/hibernate/dialect/function/json/JsonTableFunction.java

+6
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ protected void renderColumnPath(String name, @Nullable String jsonPath, SqlAppen
248248
sqlAppender.appendSql( " path " );
249249
sqlAppender.appendSingleQuoteEscapedString( jsonPath );
250250
}
251+
else {
252+
// Always append implicit path to avoid identifier case sensitivity issues
253+
sqlAppender.appendSql( " path '$." );
254+
sqlAppender.appendSql( name );
255+
sqlAppender.appendSql( '\'' );
256+
}
251257
}
252258

253259
protected void renderJsonQueryColumnDefinition(SqlAppender sqlAppender, JsonTableQueryColumnDefinition definition, int clauseLevel, SqlAstTranslator<?> walker) {

hibernate-core/src/main/java/org/hibernate/dialect/function/json/PostgreSQLJsonValueFunction.java

+45-15
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
*/
2727
public class PostgreSQLJsonValueFunction extends JsonValueFunction {
2828

29-
public PostgreSQLJsonValueFunction(TypeConfiguration typeConfiguration) {
29+
private final boolean supportsStandard;
30+
31+
public PostgreSQLJsonValueFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) {
3032
super( typeConfiguration, true, true );
33+
this.supportsStandard = supportsStandard;
3134
}
3235

3336
@Override
@@ -36,23 +39,50 @@ protected void render(
3639
JsonValueArguments arguments,
3740
ReturnableType<?> returnType,
3841
SqlAstTranslator<?> walker) {
39-
// jsonb_path_query_first errors by default
40-
if ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonValueErrorBehavior.ERROR ) {
41-
throw new QueryException( "Can't emulate on error clause on PostgreSQL" );
42+
if ( supportsStandard ) {
43+
super.render( sqlAppender, arguments, returnType, walker );
44+
// PostgreSQL unfortunately renders `t`/`f` for JSON booleans instead of `true`/`false` like every other DB.
45+
// To work around this, extract the jsonb node directly and then use the `#>>` operator to unquote values
46+
// Also see https://stackoverflow.com/questions/79483975/postgresql-json-value-boolean-behavior
47+
if ( isString( arguments.returningType() ) ) {
48+
// Unquote the value
49+
sqlAppender.appendSql( "#>>'{}'" );
50+
}
4251
}
43-
if ( arguments.emptyBehavior() != null && arguments.emptyBehavior() != JsonValueEmptyBehavior.NULL ) {
44-
throw new QueryException( "Can't emulate on empty clause on PostgreSQL" );
52+
else {
53+
// jsonb_path_query_first errors by default
54+
if ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonValueErrorBehavior.ERROR ) {
55+
throw new QueryException( "Can't emulate on error clause on PostgreSQL" );
56+
}
57+
if ( arguments.emptyBehavior() != null && arguments.emptyBehavior() != JsonValueEmptyBehavior.NULL ) {
58+
throw new QueryException( "Can't emulate on empty clause on PostgreSQL" );
59+
}
60+
61+
appendJsonValue(
62+
sqlAppender,
63+
arguments.jsonDocument(),
64+
arguments.jsonPath(),
65+
arguments.isJsonType(),
66+
arguments.returningType(),
67+
arguments.passingClause(),
68+
walker
69+
);
4570
}
71+
}
72+
73+
@Override
74+
protected void renderReturningClause(SqlAppender sqlAppender, JsonValueArguments arguments, SqlAstTranslator<?> walker) {
75+
// See #render for an explanation of this behavior
76+
if ( supportsStandard && isString( arguments.returningType() ) ) {
77+
sqlAppender.appendSql( " returning jsonb" );
78+
}
79+
else {
80+
super.renderReturningClause( sqlAppender, arguments, walker );
81+
}
82+
}
4683

47-
appendJsonValue(
48-
sqlAppender,
49-
arguments.jsonDocument(),
50-
arguments.jsonPath(),
51-
arguments.isJsonType(),
52-
arguments.returningType(),
53-
arguments.passingClause(),
54-
walker
55-
);
84+
private boolean isString(@Nullable CastTarget castTarget) {
85+
return castTarget == null || castTarget.getJdbcMapping().getJdbcType().isString();
5686
}
5787

5888
static void appendJsonValue(SqlAppender sqlAppender, Expression jsonDocument, SqlAstNode jsonPath, boolean isJsonType, @Nullable CastTarget castTarget, @Nullable JsonPathPassingClause passingClause, SqlAstTranslator<?> walker) {

0 commit comments

Comments
 (0)