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
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public interface DialectSpecificSettings {
*/
String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity";

/**
* Specifies whether the dialect should use the binary IEEE Oracle SQL types {@code binary_float}/{@code binary_double}
* over {@code float(p)}/{@code real}/{@code double precision} when generating DDL or SQL casts for float types.
*
* @settingDefault {@code true}
* @since 7.0
*/
String ORACLE_USE_BINARY_FLOATS = "hibernate.dialect.oracle.use_binary_floats";

/**
* Specifies whether the {@code ansinull} setting is enabled on Sybase.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,8 @@
*/
package org.hibernate.dialect;

import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType;
import org.hibernate.Length;
import org.hibernate.QueryTimeoutException;
import org.hibernate.boot.model.FunctionContributions;
Expand All @@ -34,6 +25,8 @@
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.dialect.unique.CreateTableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
Expand All @@ -47,20 +40,20 @@
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.CheckConstraint;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UserDefinedType;
import org.hibernate.mapping.CheckConstraint;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.procedure.internal.OracleCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.query.SemanticException;
import org.hibernate.query.common.FetchClauseType;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.CastType;
import org.hibernate.query.common.FetchClauseType;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
Expand Down Expand Up @@ -92,19 +85,29 @@
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.NamedNativeEnumDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeImpl;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;

import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType;
import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static org.hibernate.LockOptions.NO_WAIT;
import static org.hibernate.LockOptions.SKIP_LOCKED;
import static org.hibernate.LockOptions.WAIT_FOREVER;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
import static org.hibernate.dialect.OracleJdbcHelper.getArrayJdbcTypeConstructor;
import static org.hibernate.dialect.OracleJdbcHelper.getNestedTableJdbcTypeConstructor;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
Expand Down Expand Up @@ -200,11 +203,9 @@ protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggr

// Is the database accessed using a database service protected by Application Continuity.
protected final boolean applicationContinuity;

protected final int driverMajorVersion;

protected final int driverMinorVersion;

private boolean useBinaryFloat;

public OracleDialect() {
this( MINIMUM_VERSION );
Expand Down Expand Up @@ -770,11 +771,11 @@ protected String columnType(int sqlTypeCode) {
return "number(19,0)";
case REAL:
// Oracle's 'real' type is actually double precision
return "float(24)";
return useBinaryFloat ? "binary_float" : "float(24)";
case DOUBLE:
// Oracle's 'double precision' means float(126), and
// we never need 126 bits (38 decimal digits)
return "float(53)";
return useBinaryFloat ? "binary_double" : "float(53)";

case NUMERIC:
case DECIMAL:
Expand Down Expand Up @@ -959,6 +960,9 @@ public Exporter<UserDefinedType> getUserDefinedTypeExporter() {

@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
final ConfigurationService configurationService = serviceRegistry.requireService( ConfigurationService.class );
useBinaryFloat = configurationService.getSetting( ORACLE_USE_BINARY_FLOATS, StandardConverters.BOOLEAN, true );

super.contributeTypes( typeContributions, serviceRegistry );
if ( ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry, this ) == BIT ) {
typeContributions.contributeJdbcType( OracleBooleanJdbcType.INSTANCE );
Expand All @@ -972,6 +976,15 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );
}

if ( useBinaryFloat ) {
// Override the descriptor for float to produce binary_float or binary_double based on precision
typeContributions.getTypeConfiguration().getDdlTypeRegistry().addDescriptor(
CapacityDependentDdlType.builder( FLOAT, columnType( DOUBLE ), this )
.withTypeCapacity( getFloatPrecision(), columnType( REAL ) )
.build()
);
}

if ( getVersion().isSameOrAfter( 21 ) ) {
typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import org.hibernate.ScrollableResults;
import org.hibernate.TypeMismatchException;
import org.hibernate.cfg.Environment;
import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.community.dialect.DerbyDialect;
import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect;
Expand Down Expand Up @@ -63,6 +63,8 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
Expand All @@ -80,6 +82,7 @@
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hibernate.testing.orm.junit.ExtraAssertions.assertClassAssignability;
import static org.hibernate.testing.orm.junit.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;

/**
* Tests the integration of the new AST parser into the loading of query results using
Expand Down Expand Up @@ -2791,12 +2794,8 @@ public void testStr(SessionFactoryScope scope) {
String str = (String) session.createQuery(
"select str(an.bodyWeight) from Animal an where str(an.bodyWeight) like '%1%'" )
.uniqueResult();
if ( session.getDialect() instanceof DB2Dialect ) {
assertThat( str ).startsWith( "1.234" );
}
else {
assertThat( str ).startsWith( "123.4" );
}
BigDecimal value = new BigDecimal( str, new MathContext( 4, RoundingMode.DOWN ) );
assertEquals( new BigDecimal( "123.4" ), value );

String dateStr1 = (String) session.createQuery( "select str(current_date) from Animal" )
.uniqueResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
// Don't reorder columns in the types here to avoid the need to rewrite the test
@ServiceRegistry(settings = @Setting(name = AvailableSettings.COLUMN_ORDERING_STRATEGY, value = "legacy"))
@ServiceRegistry(settings = {
@Setting(name = AvailableSettings.COLUMN_ORDERING_STRATEGY, value = "legacy")
})
@DomainModel(annotatedClasses = JsonWithArrayEmbeddableTest.JsonHolder.class)
@SessionFactory
public class JsonWithArrayEmbeddableTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public static class Account {
@DialectOverride.Formula(dialect = DB2Dialect.class,
override = @Formula("varchar_format(rate * 100) || '%'"))
@DialectOverride.Formula(dialect = OracleDialect.class,
override = @Formula("to_char(rate * 100) || '%'"))
override = @Formula("to_char(cast(rate * 100 as number(10,2))) || '%'"))
@DialectOverride.Formula(dialect = SQLServerDialect.class,
override = @Formula("ltrim(str(rate * 100, 10, 2)) + '%'"))
@DialectOverride.Formula(dialect = SybaseDialect.class,
Expand Down
20 changes: 20 additions & 0 deletions migration-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,26 @@ The default precision for SQL Server timestamps was changed to 7, i.e. 100 nanos

Note that these changes only affect DDL generation.

[[float-mapping-changes-oracle]]
=== DDL type for Java `float` and `double` changed on Oracle

Previous version of Hibernate ORM mapped Java `float` and `double` to Oracle `float(p)`, `real` or `double precision`
types, which are all internally implemented as `number`. To avoid potential misbehavior compared to Java execution
and match the expectations of the IEEE floating point semantics as requested by using Java `float`/`double`,
the default DDL types were changed to Oracles IEEE floating point types `binary_float` and `binary_double` respectively.

Migration requires multiple steps because Oracle doesn't support online type changes:

```sql
alter table TBL add (NEW_COLUMN binary_float);
update TBL set NEW_COLUMN=OLD_COLUMN;
alter table TBL drop column OLD_COLUMN;
alter table TBL rename column NEW_COLUMN to OLD_COLUMN;
```

Note that changing the schema is not required for Hibernate ORM to work correctly.
The previous behavior may be recovered by setting `hibernate.dialect.oracle.use_binary_floats` to `false`.

[[array-mapping-changes-on-db2-sap-hana-sql-server-and-sybase-ase]]
=== Array Mapping Changes

Expand Down