Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions docker_db.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ mariadb_verylatest() {
}

postgresql() {
postgresql_16
postgresql_17
}

postgresql_12() {
Expand Down Expand Up @@ -186,8 +186,15 @@ postgresql_16() {
$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;"'
}

postgresql_17() {
$CONTAINER_CLI rm -f postgres || true
$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} \
-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
$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;"'
}

edb() {
edb_16
edb_17
}

edb_12() {
Expand Down Expand Up @@ -218,6 +225,13 @@ edb_16() {
$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
}

edb_17() {
$CONTAINER_CLI rm -f edb || true
# We need to build a derived image because the existing image is mainly made for use by a kubernetes operator
(cd edb; $CONTAINER_CLI build -t edb-test:17 -f edb17.Dockerfile .)
$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
}

db2() {
db2_11_5
}
Expand Down
48 changes: 48 additions & 0 deletions edb/edb17.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM quay.io/enterprisedb/edb-postgres-advanced:17.4-3.5-postgis
USER root
# this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)
RUN chown -R postgres:postgres /var/lib/edb && chmod 777 /var/lib/edb && rm /docker-entrypoint-initdb.d/10_postgis.sh

USER postgres
ENV LANG en_US.utf8
ENV PG_MAJOR 17
ENV PG_VERSION 17
ENV PGPORT 5444
ENV PGDATA /var/lib/edb/as$PG_MAJOR/data/
VOLUME /var/lib/edb/as$PG_MAJOR/data/

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

# We set the default STOPSIGNAL to SIGINT, which corresponds to what PostgreSQL
# calls "Fast Shutdown mode" wherein new connections are disallowed and any
# in-progress transactions are aborted, allowing PostgreSQL to stop cleanly and
# flush tables to disk, which is the best compromise available to avoid data
# corruption.
#
# Users who know their applications do not keep open long-lived idle connections
# may way to use a value of SIGTERM instead, which corresponds to "Smart
# Shutdown mode" in which any existing sessions are allowed to finish and the
# server stops when all sessions are terminated.
#
# See https://www.postgresql.org/docs/12/server-shutdown.html for more details
# about available PostgreSQL server shutdown signals.
#
# See also https://www.postgresql.org/docs/12/server-start.html for further
# justification of this as the default value, namely that the example (and
# shipped) systemd service files use the "Fast Shutdown mode" for service
# termination.
#
STOPSIGNAL SIGINT
#
# An additional setting that is recommended for all users regardless of this
# value is the runtime "--stop-timeout" (or your orchestrator/runtime's
# equivalent) for controlling how long to wait between sending the defined
# STOPSIGNAL and sending SIGKILL (which is likely to cause data corruption).
#
# The default in most runtimes (such as Docker) is 10 seconds, and the
# documentation at https://www.postgresql.org/docs/12/server-start.html notes
# that even 90 seconds may not be long enough in many instances.

EXPOSE 5444
CMD ["postgres"]
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.jsonArrayAppend_postgresql( false );
functionFactory.jsonArrayInsert_postgresql();

functionFactory.unnest_postgresql();
functionFactory.unnest_postgresql( false );
functionFactory.generateSeries( null, "ordinality", true );
functionFactory.jsonTable_cockroachdb();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.arrayToString_postgresql();

if ( getVersion().isSameOrAfter( 17 ) ) {
functionFactory.jsonValue();
functionFactory.jsonValue_postgresql( true );
functionFactory.jsonQuery();
functionFactory.jsonExists();
functionFactory.jsonObject();
Expand All @@ -660,7 +660,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.jsonTable();
}
else {
functionFactory.jsonValue_postgresql();
functionFactory.jsonValue_postgresql( false );
functionFactory.jsonQuery_postgresql();
functionFactory.jsonExists_postgresql();
if ( getVersion().isSameOrAfter( 16 ) ) {
Expand Down Expand Up @@ -726,12 +726,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
functionFactory.dateTrunc();

if ( getVersion().isSameOrAfter( 17 ) ) {
functionFactory.unnest( null, "ordinality" );
}
else {
functionFactory.unnest_postgresql();
}
functionFactory.unnest_postgresql( getVersion().isSameOrAfter( 17 ) );
functionFactory.generateSeries( null, "ordinality", false );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.jsonArrayAppend_postgresql( false );
functionFactory.jsonArrayInsert_postgresql();

functionFactory.unnest_postgresql();
functionFactory.unnest_postgresql( false );
functionFactory.generateSeries( null, "ordinality", true );
functionFactory.jsonTable_cockroachdb();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.arrayToString_postgresql();

if ( getVersion().isSameOrAfter( 17 ) ) {
functionFactory.jsonValue();
functionFactory.jsonValue_postgresql( true );
functionFactory.jsonQuery();
functionFactory.jsonExists();
functionFactory.jsonObject();
Expand All @@ -624,7 +624,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.jsonTable();
}
else {
functionFactory.jsonValue_postgresql();
functionFactory.jsonValue_postgresql( false );
functionFactory.jsonQuery_postgresql();
functionFactory.jsonExists_postgresql();
if ( getVersion().isSameOrAfter( 16 ) ) {
Expand Down Expand Up @@ -688,12 +688,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
functionFactory.dateTrunc();

if ( getVersion().isSameOrAfter( 17 ) ) {
functionFactory.unnest( null, "ordinality" );
}
else {
functionFactory.unnest_postgresql();
}
functionFactory.unnest_postgresql( getVersion().isSameOrAfter( 17 ) );
functionFactory.generateSeries( null, "ordinality", false );

functionFactory.hex( "encode(?1, 'hex')" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3319,13 +3319,6 @@ public void arrayToString_oracle() {
functionRegistry.register( "array_to_string", new OracleArrayToStringFunction( typeConfiguration ) );
}

/**
* json_value() function
*/
public void jsonValue() {
functionRegistry.register( "json_value", new JsonValueFunction( typeConfiguration, true, true ) );
}

/**
* HANA json_value() function
*/
Expand All @@ -3350,8 +3343,8 @@ public void jsonValue_db2() {
/**
* PostgreSQL json_value() function
*/
public void jsonValue_postgresql() {
functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( typeConfiguration ) );
public void jsonValue_postgresql(boolean supportsStandard) {
functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( supportsStandard, typeConfiguration ) );
}

/**
Expand Down Expand Up @@ -4232,8 +4225,8 @@ public void unnest_oracle() {
/**
* PostgreSQL unnest() function
*/
public void unnest_postgresql() {
functionRegistry.register( "unnest", new PostgreSQLUnnestFunction() );
public void unnest_postgresql(boolean supportsJsonTable) {
functionRegistry.register( "unnest", new PostgreSQLUnnestFunction( supportsJsonTable ) );
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
*/
public class PostgreSQLUnnestFunction extends UnnestFunction {

public PostgreSQLUnnestFunction() {
private final boolean supportsJsonTable;

public PostgreSQLUnnestFunction(boolean supportsJsonTable) {
super( null, "ordinality" );
this.supportsJsonTable = supportsJsonTable;
}

@Override
Expand All @@ -36,41 +39,54 @@ protected void renderJsonTable(
AnonymousTupleTableGroupProducer tupleType,
String tableIdentifierVariable,
SqlAstTranslator<?> walker) {
final AggregateSupport aggregateSupport = walker.getSessionFactory().getJdbcServices().getDialect()
.getAggregateSupport();
sqlAppender.appendSql( "(select" );
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
if ( selectionIndex == 0 ) {
sqlAppender.append( ' ' );
}
else {
sqlAppender.append( ',' );
}
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
sqlAppender.appendSql( "t.i" );
if ( supportsJsonTable ) {
super.renderJsonTable(
sqlAppender,
array,
pluralType,
sqlTypedMapping,
tupleType,
tableIdentifierVariable,
walker
);
}
else {
final AggregateSupport aggregateSupport = walker.getSessionFactory().getJdbcServices().getDialect()
.getAggregateSupport();
sqlAppender.appendSql( "(select" );
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
if ( selectionIndex == 0 ) {
sqlAppender.append( ' ' );
}
else {
sqlAppender.append( ',' );
}
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
sqlAppender.appendSql( "t.i" );
}
else {
sqlAppender.append( aggregateSupport.aggregateComponentCustomReadExpression(
"",
"",
"t.v",
selectableMapping.getSelectableName(),
SqlTypes.JSON,
selectableMapping,
walker.getSessionFactory().getTypeConfiguration()
) );
}
sqlAppender.append( " as " );
sqlAppender.append( selectableMapping.getSelectionExpression() );
} );
sqlAppender.appendSql( " from jsonb_array_elements(" );
array.accept( walker );
sqlAppender.appendSql( ')' );
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
sqlAppender.appendSql( " with ordinality t(v,i))" );
}
else {
sqlAppender.append( aggregateSupport.aggregateComponentCustomReadExpression(
"",
"",
"t.v",
selectableMapping.getSelectableName(),
SqlTypes.JSON,
selectableMapping,
walker.getSessionFactory().getTypeConfiguration()
) );
sqlAppender.appendSql( " t(v))" );
}
sqlAppender.append( " as " );
sqlAppender.append( selectableMapping.getSelectionExpression() );
} );
sqlAppender.appendSql( " from jsonb_array_elements(" );
array.accept( walker );
sqlAppender.appendSql( ')' );
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
sqlAppender.appendSql( " with ordinality t(v,i))" );
}
else {
sqlAppender.appendSql( " t(v))" );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ protected void renderColumnPath(String name, @Nullable String jsonPath, SqlAppen
sqlAppender.appendSql( " path " );
sqlAppender.appendSingleQuoteEscapedString( jsonPath );
}
else {
// Always append implicit path to avoid identifier case sensitivity issues
sqlAppender.appendSql( " path '$." );
sqlAppender.appendSql( name );
sqlAppender.appendSql( '\'' );
}
}

protected void renderJsonQueryColumnDefinition(SqlAppender sqlAppender, JsonTableQueryColumnDefinition definition, int clauseLevel, SqlAstTranslator<?> walker) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
*/
public class PostgreSQLJsonValueFunction extends JsonValueFunction {

public PostgreSQLJsonValueFunction(TypeConfiguration typeConfiguration) {
private final boolean supportsStandard;

public PostgreSQLJsonValueFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) {
super( typeConfiguration, true, true );
this.supportsStandard = supportsStandard;
}

@Override
Expand All @@ -36,23 +39,50 @@
JsonValueArguments arguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
// jsonb_path_query_first errors by default
if ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonValueErrorBehavior.ERROR ) {
throw new QueryException( "Can't emulate on error clause on PostgreSQL" );
if ( supportsStandard ) {
super.render( sqlAppender, arguments, returnType, walker );
// PostgreSQL unfortunately renders `t`/`f` for JSON booleans instead of `true`/`false` like every other DB.
// To work around this, extract the jsonb node directly and then use the `#>>` operator to unquote values
// Also see https://stackoverflow.com/questions/79483975/postgresql-json-value-boolean-behavior
if ( isString( arguments.returningType() ) ) {
// Unquote the value
sqlAppender.appendSql( "#>>'{}'" );
}
}
if ( arguments.emptyBehavior() != null && arguments.emptyBehavior() != JsonValueEmptyBehavior.NULL ) {
throw new QueryException( "Can't emulate on empty clause on PostgreSQL" );
else {
// jsonb_path_query_first errors by default
if ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonValueErrorBehavior.ERROR ) {
throw new QueryException( "Can't emulate on error clause on PostgreSQL" );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
QueryException.QueryException
should be avoided because it has been deprecated.
}
if ( arguments.emptyBehavior() != null && arguments.emptyBehavior() != JsonValueEmptyBehavior.NULL ) {
throw new QueryException( "Can't emulate on empty clause on PostgreSQL" );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
QueryException.QueryException
should be avoided because it has been deprecated.
}

appendJsonValue(
sqlAppender,
arguments.jsonDocument(),
arguments.jsonPath(),
arguments.isJsonType(),
arguments.returningType(),
arguments.passingClause(),
walker
);
}
}

@Override
protected void renderReturningClause(SqlAppender sqlAppender, JsonValueArguments arguments, SqlAstTranslator<?> walker) {
// See #render for an explanation of this behavior
if ( supportsStandard && isString( arguments.returningType() ) ) {
sqlAppender.appendSql( " returning jsonb" );
}
else {
super.renderReturningClause( sqlAppender, arguments, walker );
}
}

appendJsonValue(
sqlAppender,
arguments.jsonDocument(),
arguments.jsonPath(),
arguments.isJsonType(),
arguments.returningType(),
arguments.passingClause(),
walker
);
private boolean isString(@Nullable CastTarget castTarget) {
return castTarget == null || castTarget.getJdbcMapping().getJdbcType().isString();
}

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