diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index eb634495d1..25874915e4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -473,8 +473,16 @@ public class OracleLegacyDialect extends Dialect { @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { + return timestampaddPattern( unit, temporalType, intervalType, false ); + } - StringBuilder pattern = new StringBuilder(); + @Override + public String timestampaddPattern( + TemporalUnit unit, + TemporalType temporalType, + IntervalType intervalType, + boolean hasTimeZone) { + final StringBuilder pattern = new StringBuilder(); switch ( unit ) { case YEAR: pattern.append( ADD_YEAR_EXPRESSION ); @@ -486,22 +494,32 @@ public class OracleLegacyDialect extends Dialect { pattern.append( ADD_MONTH_EXPRESSION ); break; case WEEK: - pattern.append("(?3+numtodsinterval((?2)*7,'day'))"); + if ( hasTimeZone ) { + pattern.append( "(?3+numtodsinterval(?2*7,'day'))" ); + } + else { + pattern.append( "(?3+?2" ).append( unit.conversionFactor( DAY, this ) ).append( ")" ); + } break; case DAY: case HOUR: case MINUTE: case SECOND: - pattern.append("(?3+numtodsinterval(?2,'?1'))"); + if ( hasTimeZone ) { + pattern.append( "(?3+numtodsinterval(?2,'?1'))" ); + } + else { + pattern.append( "(?3+?2" ).append( unit.conversionFactor( DAY, this ) ).append( ")" ); + } break; case NANOSECOND: - pattern.append("(?3+numtodsinterval((?2)/1e9,'second'))"); + pattern.append( "(?3+numtodsinterval((?2)/1e9,'second'))" ); break; case NATIVE: - pattern.append("(?3+numtodsinterval(?2,'second'))"); + pattern.append( "(?3+numtodsinterval(?2,'second'))" ); break; default: - throw new SemanticException(unit + " is not a legal field"); + throw new SemanticException( unit + " is not a legal field" ); } return pattern.toString(); } @@ -522,42 +540,51 @@ public class OracleLegacyDialect extends Dialect { extractField( pattern, MONTH, unit ); pattern.append( ")" ); break; - case WEEK: case DAY: - extractField( pattern, DAY, unit ); - break; - case HOUR: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); + pattern.append( "(cast(?3 as date)-cast(?2 as date))" ); + } + else { + pattern.append( "(?3-?2)" ); } - pattern.append( ")" ); break; + case WEEK: case MINUTE: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); + case SECOND: + case HOUR: if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); - pattern.append( "+" ); - extractField( pattern, MINUTE, unit ); + pattern.append( "((cast(?3 as date)-cast(?2 as date))" ); } + else { + pattern.append( "((?3-?2)" ); + } + pattern.append( TemporalUnit.DAY.conversionFactor(unit ,this ) ); pattern.append( ")" ); break; case NATIVE: case NANOSECOND: - case SECOND: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); - pattern.append( "+" ); - extractField( pattern, MINUTE, unit ); - pattern.append( "+" ); - extractField( pattern, SECOND, unit ); + if ( supportsLateral() ) { + pattern.append( "(select extract(day from t.i)" ).append( TemporalUnit.DAY.conversionFactor( unit, this ) ) + .append( "+extract(hour from t.i)" ).append( TemporalUnit.HOUR.conversionFactor( unit, this ) ) + .append( "+extract(minute from t.i)" ).append( MINUTE.conversionFactor( unit, this ) ) + .append( "+extract(second from t.i)" ).append( SECOND.conversionFactor( unit, this ) ) + .append( " from(select ?3-?2 i from dual)t" ); + } + else { + pattern.append( "(" ); + extractField( pattern, DAY, unit ); + pattern.append( "+" ); + extractField( pattern, HOUR, unit ); + pattern.append( "+" ); + extractField( pattern, MINUTE, unit ); + pattern.append( "+" ); + extractField( pattern, SECOND, unit ); + } + } + else { + pattern.append( "((?3-?2)" ); + pattern.append( TemporalUnit.DAY.conversionFactor( unit, this ) ); } pattern.append( ")" ); break; @@ -570,17 +597,16 @@ public class OracleLegacyDialect extends Dialect { private void extractField(StringBuilder pattern, TemporalUnit unit, TemporalUnit toUnit) { pattern.append( "extract(" ); pattern.append( translateExtractField( unit ) ); - pattern.append( " from (?3-?2) " ); + pattern.append( " from (?3-?2)" ); switch ( unit ) { case YEAR: case MONTH: - pattern.append( "year to month" ); + pattern.append( " year(9) to month" ); break; case DAY: case HOUR: case MINUTE: case SECOND: - pattern.append( "day to second" ); break; default: throw new SemanticException( unit + " is not a legal field" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 2ff97b5781..e55f534848 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1441,6 +1441,22 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun * @param temporalType The type of the temporal * @param intervalType The type of interval to add or null if it's not a native interval */ + public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType, boolean withTimeZone) { + return timestampaddPattern( unit, temporalType, intervalType ); + } + + /** + * Obtain a pattern for the SQL equivalent to a + * {@code timestampadd()} function call. The resulting + * pattern must contain ?1, ?2, and ?3 placeholders + * for the arguments. + * + * @param unit The unit to add to the temporal + * @param temporalType The type of the temporal + * @param intervalType The type of interval to add or null if it's not a native interval + * @deprecated use {@link #timestampaddPattern(TemporalUnit, TemporalType, IntervalType, boolean)} instead + */ + @Deprecated public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { throw new UnsupportedOperationException( "`" + getClass().getName() + "` does not yet support #timestampaddPattern" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java index eebbd43586..686ac7c892 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java @@ -302,6 +302,15 @@ public class DialectDelegateWrapper extends Dialect { return wrapped.timestampdiffPattern( unit, fromTemporalType, toTemporalType ); } + @Override + public String timestampaddPattern( + TemporalUnit unit, + TemporalType temporalType, + IntervalType intervalType, + boolean withTimeZone) { + return wrapped.timestampaddPattern( unit, temporalType, intervalType, withTimeZone ); + } + @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { return wrapped.timestampaddPattern( unit, temporalType, intervalType ); @@ -1640,4 +1649,14 @@ public class DialectDelegateWrapper extends Dialect { public String getRowIdColumnString(String rowId) { return wrapped.getRowIdColumnString( rowId ); } + + @Override + public boolean useArrayForMultiValuedParameters() { + return wrapped.useArrayForMultiValuedParameters(); + } + + @Override + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return wrapped.getDmlTargetColumnQualifierSupport(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 107da4fc3b..0b3c4f0a26 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -494,8 +494,16 @@ public class OracleDialect extends Dialect { @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { + return timestampaddPattern( unit, temporalType, intervalType, false ); + } - StringBuilder pattern = new StringBuilder(); + @Override + public String timestampaddPattern( + TemporalUnit unit, + TemporalType temporalType, + IntervalType intervalType, + boolean hasTimeZone) { + final StringBuilder pattern = new StringBuilder(); switch ( unit ) { case YEAR: pattern.append( ADD_YEAR_EXPRESSION ); @@ -507,22 +515,32 @@ public class OracleDialect extends Dialect { pattern.append( ADD_MONTH_EXPRESSION ); break; case WEEK: - pattern.append("(?3+numtodsinterval((?2)*7,'day'))"); + if ( hasTimeZone ) { + pattern.append( "(?3+numtodsinterval(?2*7,'day'))" ); + } + else { + pattern.append( "(?3+?2" ).append( unit.conversionFactor( DAY, this ) ).append( ")" ); + } break; case DAY: case HOUR: case MINUTE: case SECOND: - pattern.append("(?3+numtodsinterval(?2,'?1'))"); + if ( hasTimeZone ) { + pattern.append( "(?3+numtodsinterval(?2,'?1'))" ); + } + else { + pattern.append( "(?3+?2" ).append( unit.conversionFactor( DAY, this ) ).append( ")" ); + } break; case NANOSECOND: - pattern.append("(?3+numtodsinterval((?2)/1e9,'second'))"); + pattern.append( "(?3+numtodsinterval((?2)/1e9,'second'))" ); break; case NATIVE: - pattern.append("(?3+numtodsinterval(?2,'second'))"); + pattern.append( "(?3+numtodsinterval(?2,'second'))" ); break; default: - throw new SemanticException(unit + " is not a legal field"); + throw new SemanticException( unit + " is not a legal field" ); } return pattern.toString(); } @@ -543,42 +561,51 @@ public class OracleDialect extends Dialect { extractField( pattern, MONTH, unit ); pattern.append( ")" ); break; - case WEEK: case DAY: - extractField( pattern, DAY, unit ); - break; - case HOUR: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); + pattern.append( "(cast(?3 as date)-cast(?2 as date))" ); + } + else { + pattern.append( "(?3-?2)" ); } - pattern.append( ")" ); break; + case WEEK: case MINUTE: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); + case SECOND: + case HOUR: if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); - pattern.append( "+" ); - extractField( pattern, MINUTE, unit ); + pattern.append( "((cast(?3 as date)-cast(?2 as date))" ); } + else { + pattern.append( "((?3-?2)" ); + } + pattern.append( TemporalUnit.DAY.conversionFactor(unit ,this ) ); pattern.append( ")" ); break; case NATIVE: case NANOSECOND: - case SECOND: - pattern.append( "(" ); - extractField( pattern, DAY, unit ); if ( hasTimePart ) { - pattern.append( "+" ); - extractField( pattern, HOUR, unit ); - pattern.append( "+" ); - extractField( pattern, MINUTE, unit ); - pattern.append( "+" ); - extractField( pattern, SECOND, unit ); + if ( supportsLateral() ) { + pattern.append( "(select extract(day from t.i)" ).append( TemporalUnit.DAY.conversionFactor( unit, this ) ) + .append( "+extract(hour from t.i)" ).append( TemporalUnit.HOUR.conversionFactor( unit, this ) ) + .append( "+extract(minute from t.i)" ).append( MINUTE.conversionFactor( unit, this ) ) + .append( "+extract(second from t.i)" ).append( SECOND.conversionFactor( unit, this ) ) + .append( " from(select ?3-?2 i from dual)t" ); + } + else { + pattern.append( "(" ); + extractField( pattern, DAY, unit ); + pattern.append( "+" ); + extractField( pattern, HOUR, unit ); + pattern.append( "+" ); + extractField( pattern, MINUTE, unit ); + pattern.append( "+" ); + extractField( pattern, SECOND, unit ); + } + } + else { + pattern.append( "((?3-?2)" ); + pattern.append( TemporalUnit.DAY.conversionFactor( unit, this ) ); } pattern.append( ")" ); break; @@ -591,17 +618,16 @@ public class OracleDialect extends Dialect { private void extractField(StringBuilder pattern, TemporalUnit unit, TemporalUnit toUnit) { pattern.append( "extract(" ); pattern.append( translateExtractField( unit ) ); - pattern.append( " from (?3-?2) " ); + pattern.append( " from (?3-?2)" ); switch ( unit ) { case YEAR: case MONTH: - pattern.append( "year to month" ); + pattern.append( " year(9) to month" ); break; case DAY: case HOUR: case MINUTE: case SECOND: - pattern.append( "day to second" ); break; default: throw new SemanticException( unit + " is not a legal field" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampaddFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampaddFunction.java index 05c159a53e..b3c3229ebe 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampaddFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampaddFunction.java @@ -23,6 +23,7 @@ import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.expression.DurationUnit; import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.SqlTypes; import org.hibernate.type.spi.TypeConfiguration; import java.util.List; @@ -38,7 +39,7 @@ import static org.hibernate.type.spi.TypeConfiguration.getSqlTemporalType; * The {@code timestampadd()} or {@code dateadd()} function has a funny * syntax which accepts a {@link TemporalUnit} as the first argument, * and the actual set of accepted units varies widely. This class uses - * {@link Dialect#timestampaddPattern(TemporalUnit, TemporalType, IntervalType)} + * {@link Dialect#timestampaddPattern(TemporalUnit, TemporalType, IntervalType, boolean)} * to abstract these differences. * * @author Gavin King @@ -77,7 +78,19 @@ public class TimestampaddFunction PatternRenderer patternRenderer(TemporalUnit unit, Expression interval, Expression to) { TemporalType temporalType = getSqlTemporalType( to.getExpressionType() ); IntervalType intervalType = getSqlIntervalType( interval.getExpressionType().getSingleJdbcMapping() ); - return new PatternRenderer( dialect.timestampaddPattern( unit, temporalType, intervalType ) ); + boolean withTimeZone = hasTimeZone( to.getExpressionType().getSingleJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ); + return new PatternRenderer( dialect.timestampaddPattern( unit, temporalType, intervalType, withTimeZone ) ); + } + + private boolean hasTimeZone(int sqlTypeCode) { + switch ( sqlTypeCode ) { + case SqlTypes.TIME_UTC: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIMESTAMP_UTC: + case SqlTypes.TIMESTAMP_WITH_TIMEZONE: + return true; + } + return false; } // @Override