HHH-17242 Improve temporal arithmetic SQL rendering
This commit is contained in:
parent
4faa30f172
commit
5b97f49bc8
|
@ -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" );
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue