Skip to content

Commit

Permalink
HHH-17693 Fix typecheck assertions for converted properties
Browse files Browse the repository at this point in the history
Also introduce a custom `DurationJdbcType`, mainly for validation purposes.
  • Loading branch information
mbladel committed Feb 8, 2024
1 parent 658e9bc commit 6138c76
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ public void contributeType(CompositeUserType<?> type) {
);
}
else {
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.NUMERIC );
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION );
}

addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ private void serializeBasicTo(
case SqlTypes.DOUBLE:
case SqlTypes.DECIMAL:
case SqlTypes.NUMERIC:
case SqlTypes.DURATION:
jdbcJavaType.appendEncodedString(
appender,
jdbcJavaType.unwrap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ else if ( mappedType instanceof BasicType<?> ) {
break;
case SqlTypes.DECIMAL:
case SqlTypes.NUMERIC:
case SqlTypes.DURATION:
case SqlTypes.UUID:
// These types need to be serialized as JSON string, but don't have a need for escaping
appender.append( '"' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ private static void serializeValueTo(XMLAppender appender, SelectableMapping sel
case SqlTypes.DOUBLE:
case SqlTypes.DECIMAL:
case SqlTypes.NUMERIC:
case SqlTypes.DURATION:
jdbcJavaType.appendEncodedString(
appender,
jdbcJavaType.unwrap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ public static synchronized int getPreferredSqlTypeCodeForDuration(StandardServic
return explicitSetting;
}

return SqlTypes.NUMERIC;
return SqlTypes.DURATION;
}

@Incubating
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,39 +507,24 @@ private static boolean isNumberArray(SqmExpressible<?> expressible) {
public static void assertString(SqmExpression<?> expression) {
final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) {
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
if ( javaType != String.class && javaType != char[].class ) {
final DomainType<?> domainType = nodeType.getSqmType();
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isStringLike() ) {
throw new SemanticException(
"Operand of 'like' is of type '" + nodeType.getTypeName() +
"' which is not a string (it is not an instance of 'java.lang.String' or 'char[]')"
"' which is not a string (its JDBC type code is not string-like)"
);
}
}
}

// public static void assertNumeric(SqmExpression<?> expression, BinaryArithmeticOperator op) {
// final SqmExpressible<?> nodeType = expression.getNodeType();
// if ( nodeType != null ) {
// final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
// if ( !Number.class.isAssignableFrom( javaType )
// && !Temporal.class.isAssignableFrom( javaType )
// && !TemporalAmount.class.isAssignableFrom( javaType )
// && !java.util.Date.class.isAssignableFrom( javaType ) ) {
// throw new SemanticException( "Operand of " + op.getOperatorSqlText()
// + " is of type '" + nodeType.getTypeName() + "' which is not a numeric type"
// + " (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')" );
// }
// }
// }

public static void assertDuration(SqmExpression<?> expression) {
final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) {
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
if ( !TemporalAmount.class.isAssignableFrom( javaType ) ) {
final DomainType<?> domainType = nodeType.getSqmType();
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isDuration() ) {
throw new SemanticException(
"Operand of 'by' is of type '" + nodeType.getTypeName() +
"' which is not a duration (it is not an instance of 'java.time.TemporalAmount')"
"' which is not a duration (its JDBC type code is not duration-like)"
);
}
}
Expand All @@ -548,11 +533,11 @@ public static void assertDuration(SqmExpression<?> expression) {
public static void assertNumeric(SqmExpression<?> expression, UnaryArithmeticOperator op) {
final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) {
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
if ( !Number.class.isAssignableFrom( javaType ) ) {
final DomainType<?> domainType = nodeType.getSqmType();
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isNumber() ) {
throw new SemanticException(
"Operand of " + op.getOperatorChar() + " is of type '" + nodeType.getTypeName() +
"' which is not a numeric type (it is not an instance of 'java.lang.Number')"
"' which is not a numeric type (its JDBC type code is not numeric)"
);
}
}
Expand Down
15 changes: 15 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,14 @@ public class SqlTypes {
*/
public static final int TIME_UTC = 3007;

/**
* A type code representing a "virtual mapping" of {@linkplain java.time.Duration}.
*
* @see Types#NUMERIC
* @see org.hibernate.type.descriptor.jdbc.DurationJdbcType
*/
public static final int DURATION = 3015;

// Interval types

/**
Expand Down Expand Up @@ -779,6 +787,13 @@ public static boolean isIntervalType(int typeCode) {
return typeCode == INTERVAL_SECOND;
}

/**
* Does the given typecode represent a {@code duration} type?
*/
public static boolean isDurationType(int typeCode) {
return typeCode == DURATION;
}

/**
* Does the given typecode represent a SQL date or timestamp type?
* @param typeCode a JDBC type code from {@link Types}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.type.descriptor.jdbc;

import java.sql.Types;
import java.time.Duration;

import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions;

/**
* Descriptor for {@link java.time.Duration}.
*
* @author Marco Belladelli
*/
public class DurationJdbcType extends NumericJdbcType {
public static final DurationJdbcType INSTANCE = new DurationJdbcType();

@Override
public int getDdlTypeCode() {
return Types.NUMERIC;
}

@Override
public int getDefaultSqlTypeCode() {
return SqlTypes.DURATION;
}

@Override
public String getFriendlyName() {
return "DURATION";
}

@Override
public String toString() {
return "DurationJdbcType";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ default boolean isInterval() {
return isIntervalType( getDdlTypeCode() );
}

default boolean isDuration() {
final int ddlTypeCode = getDefaultSqlTypeCode();
return isDurationType( ddlTypeCode )
|| isIntervalType( ddlTypeCode );
}

default CastType getCastType() {
return getCastType( getDdlTypeCode() );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hibernate.type.descriptor.jdbc.DateJdbcType;
import org.hibernate.type.descriptor.jdbc.DecimalJdbcType;
import org.hibernate.type.descriptor.jdbc.DoubleJdbcType;
import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
import org.hibernate.type.descriptor.jdbc.FloatJdbcType;
import org.hibernate.type.descriptor.jdbc.IntegerJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
Expand Down Expand Up @@ -66,6 +67,7 @@ public static void prime(BaselineTarget target) {
target.addDescriptor( TimestampWithTimeZoneJdbcType.INSTANCE );
target.addDescriptor( TimeJdbcType.INSTANCE );
target.addDescriptor( TimeWithTimeZoneJdbcType.INSTANCE );
target.addDescriptor( DurationJdbcType.INSTANCE );

target.addDescriptor( BinaryJdbcType.INSTANCE );
target.addDescriptor( VarbinaryJdbcType.INSTANCE );
Expand Down

0 comments on commit 6138c76

Please sign in to comment.