Skip to content

Commit 53c7ef4

Browse files
committed
HHH-16533 Fix issues with jConnect driver related to temporal literals. Also improve truncation and casting SQL
1 parent 473984f commit 53c7ef4

File tree

5 files changed

+201
-28
lines changed

5 files changed

+201
-28
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java

+91-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import java.sql.DatabaseMetaData;
1010
import java.sql.SQLException;
1111
import java.sql.Types;
12+
import java.time.temporal.TemporalAccessor;
13+
import java.util.Calendar;
14+
import java.util.Date;
15+
import java.util.TimeZone;
1216

1317
import org.hibernate.boot.model.FunctionContributions;
1418
import org.hibernate.boot.model.TypeContributions;
@@ -64,6 +68,11 @@
6468

6569
import jakarta.persistence.TemporalType;
6670

71+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
72+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
73+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
74+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
75+
6776

6877
/**
6978
* Superclass for all Sybase dialects.
@@ -295,16 +304,95 @@ public String castPattern(CastType from, CastType to) {
295304
if ( to == CastType.STRING ) {
296305
switch ( from ) {
297306
case DATE:
298-
return "str_replace(convert(varchar,?1,102),'.','-')";
307+
return "substring(convert(varchar,?1,23),1,10)";
299308
case TIME:
300-
return "convert(varchar,?1,108)";
309+
return "convert(varchar,?1,8)";
301310
case TIMESTAMP:
302-
return "str_replace(convert(varchar,?1,23),'T',' ')";
311+
return "convert(varchar,?1,140)";
303312
}
304313
}
305314
return super.castPattern( from, to );
306315
}
307316

317+
/* Something odd is going on with the jConnect driver when using JDBC escape syntax, so let's use native functions */
318+
319+
@Override
320+
public void appendDateTimeLiteral(
321+
SqlAppender appender,
322+
TemporalAccessor temporalAccessor,
323+
TemporalType precision,
324+
TimeZone jdbcTimeZone) {
325+
switch ( precision ) {
326+
case DATE:
327+
appender.appendSql( "convert(date,'" );
328+
appendAsDate( appender, temporalAccessor );
329+
appender.appendSql( "',140)" );
330+
break;
331+
case TIME:
332+
appender.appendSql( "convert(time,'" );
333+
appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
334+
appender.appendSql( "',8)" );
335+
break;
336+
case TIMESTAMP:
337+
appender.appendSql( "convert(datetime,'" );
338+
appendAsTimestampWithMillis( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
339+
appender.appendSql( "',140)" );
340+
break;
341+
default:
342+
throw new IllegalArgumentException();
343+
}
344+
}
345+
346+
@Override
347+
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
348+
switch ( precision ) {
349+
case DATE:
350+
appender.appendSql( "convert(date,'" );
351+
appendAsDate( appender, date );
352+
appender.appendSql( "',140)" );
353+
break;
354+
case TIME:
355+
appender.appendSql( "convert(time,'" );
356+
appendAsLocalTime( appender, date );
357+
appender.appendSql( "',8)" );
358+
break;
359+
case TIMESTAMP:
360+
appender.appendSql( "convert(datetime,'" );
361+
appendAsTimestampWithMillis( appender, date, jdbcTimeZone );
362+
appender.appendSql( "',140)" );
363+
break;
364+
default:
365+
throw new IllegalArgumentException();
366+
}
367+
}
368+
369+
@Override
370+
public void appendDateTimeLiteral(
371+
SqlAppender appender,
372+
Calendar calendar,
373+
TemporalType precision,
374+
TimeZone jdbcTimeZone) {
375+
switch ( precision ) {
376+
case DATE:
377+
appender.appendSql( "convert(date,'" );
378+
appendAsDate( appender, calendar );
379+
appender.appendSql( "',140)" );
380+
break;
381+
case TIME:
382+
appender.appendSql( "convert(time,'" );
383+
appendAsLocalTime( appender, calendar );
384+
appender.appendSql( "',8)" );
385+
break;
386+
case TIMESTAMP:
387+
appender.appendSql( "convert(datetime,'" );
388+
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
389+
appender.appendSql( "',140)" );
390+
break;
391+
default:
392+
throw new IllegalArgumentException();
393+
}
394+
}
395+
308396
@Override
309397
public String translateExtractField(TemporalUnit unit) {
310398
switch ( unit ) {

hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java

+91-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import java.sql.DatabaseMetaData;
1010
import java.sql.SQLException;
1111
import java.sql.Types;
12+
import java.time.temporal.TemporalAccessor;
13+
import java.util.Calendar;
14+
import java.util.Date;
15+
import java.util.TimeZone;
1216

1317
import org.hibernate.boot.model.FunctionContributions;
1418
import org.hibernate.boot.model.TypeContributions;
@@ -63,6 +67,11 @@
6367

6468
import jakarta.persistence.TemporalType;
6569

70+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
71+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
72+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
73+
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
74+
6675

6776
/**
6877
* Superclass for all Sybase dialects.
@@ -313,16 +322,95 @@ public String castPattern(CastType from, CastType to) {
313322
if ( to == CastType.STRING ) {
314323
switch ( from ) {
315324
case DATE:
316-
return "str_replace(convert(varchar,?1,102),'.','-')";
325+
return "substring(convert(varchar,?1,23),1,10)";
317326
case TIME:
318-
return "convert(varchar,?1,108)";
327+
return "convert(varchar,?1,8)";
319328
case TIMESTAMP:
320-
return "str_replace(convert(varchar,?1,23),'T',' ')";
329+
return "convert(varchar,?1,140)";
321330
}
322331
}
323332
return super.castPattern( from, to );
324333
}
325334

335+
/* Something odd is going on with the jConnect driver when using JDBC escape syntax, so let's use native functions */
336+
337+
@Override
338+
public void appendDateTimeLiteral(
339+
SqlAppender appender,
340+
TemporalAccessor temporalAccessor,
341+
TemporalType precision,
342+
TimeZone jdbcTimeZone) {
343+
switch ( precision ) {
344+
case DATE:
345+
appender.appendSql( "convert(date,'" );
346+
appendAsDate( appender, temporalAccessor );
347+
appender.appendSql( "',140)" );
348+
break;
349+
case TIME:
350+
appender.appendSql( "convert(time,'" );
351+
appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
352+
appender.appendSql( "',8)" );
353+
break;
354+
case TIMESTAMP:
355+
appender.appendSql( "convert(datetime,'" );
356+
appendAsTimestampWithMillis( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
357+
appender.appendSql( "',140)" );
358+
break;
359+
default:
360+
throw new IllegalArgumentException();
361+
}
362+
}
363+
364+
@Override
365+
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
366+
switch ( precision ) {
367+
case DATE:
368+
appender.appendSql( "convert(date,'" );
369+
appendAsDate( appender, date );
370+
appender.appendSql( "',140)" );
371+
break;
372+
case TIME:
373+
appender.appendSql( "convert(time,'" );
374+
appendAsLocalTime( appender, date );
375+
appender.appendSql( "',8)" );
376+
break;
377+
case TIMESTAMP:
378+
appender.appendSql( "convert(datetime,'" );
379+
appendAsTimestampWithMillis( appender, date, jdbcTimeZone );
380+
appender.appendSql( "',140)" );
381+
break;
382+
default:
383+
throw new IllegalArgumentException();
384+
}
385+
}
386+
387+
@Override
388+
public void appendDateTimeLiteral(
389+
SqlAppender appender,
390+
Calendar calendar,
391+
TemporalType precision,
392+
TimeZone jdbcTimeZone) {
393+
switch ( precision ) {
394+
case DATE:
395+
appender.appendSql( "convert(date,'" );
396+
appendAsDate( appender, calendar );
397+
appender.appendSql( "',140)" );
398+
break;
399+
case TIME:
400+
appender.appendSql( "convert(time,'" );
401+
appendAsLocalTime( appender, calendar );
402+
appender.appendSql( "',8)" );
403+
break;
404+
case TIMESTAMP:
405+
appender.appendSql( "convert(datetime,'" );
406+
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
407+
appender.appendSql( "',140)" );
408+
break;
409+
default:
410+
throw new IllegalArgumentException();
411+
}
412+
}
413+
326414
@Override
327415
public String translateExtractField(TemporalUnit unit) {
328416
switch ( unit ) {

hibernate-core/src/main/java/org/hibernate/dialect/function/SybaseTruncFunction.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public void render(
9595
sqlAppender.append( '(' );
9696
sqlAppender.append( "datetime,substring(convert(varchar," );
9797
sqlAstArguments.get( 0 ).accept( walker );
98-
sqlAppender.append( ",21),1,17" );
98+
sqlAppender.append( ",140),1,26" );
9999
if ( sqlAstArguments.size() > 1 ) {
100100
sqlAppender.append( "-len(" );
101101
sqlAstArguments.get( 1 ).accept( walker );
@@ -105,7 +105,7 @@ public void render(
105105
else {
106106
sqlAppender.append( ')' );
107107
}
108-
sqlAppender.append( ",21)" );
108+
sqlAppender.append( ",140)" );
109109
}
110110

111111
@Override
@@ -119,22 +119,22 @@ protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
119119
final String literal;
120120
switch ( temporalUnit ) {
121121
case YEAR:
122-
literal = "/01/01 00:00:00";
122+
literal = "-01-01T00:00:00.000000";
123123
break;
124124
case MONTH:
125-
literal = "/01 00:00:00";
125+
literal = "-01T00:00:00.000000";
126126
break;
127127
case DAY:
128-
literal = " 00:00:00";
128+
literal = "T00:00:00.000000";
129129
break;
130130
case HOUR:
131-
literal = ":00:00";
131+
literal = ":00:00.000000";
132132
break;
133133
case MINUTE:
134-
literal = ":00";
134+
literal = ":00.000000";
135135
break;
136136
case SECOND:
137-
literal = null;
137+
literal = ".000000";
138138
break;
139139
default:
140140
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );

hibernate-core/src/test/java/org/hibernate/orm/test/annotations/dataTypes/BasicOperationsTest.java

+6-14
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.dialect.Dialect;
1919
import org.hibernate.dialect.OracleDialect;
2020
import org.hibernate.dialect.PostgresPlusDialect;
21+
import org.hibernate.dialect.SybaseASEDialect;
2122
import org.hibernate.engine.spi.SessionImplementor;
2223
import org.hibernate.jdbc.Work;
2324
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
@@ -31,6 +32,7 @@
3132
import org.hibernate.testing.orm.junit.SessionFactory;
3233
import org.hibernate.testing.orm.junit.SessionFactoryScope;
3334
import org.hibernate.testing.orm.junit.Setting;
35+
import org.hibernate.testing.orm.junit.SkipForDialect;
3436
import org.junit.jupiter.api.Test;
3537

3638
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -40,13 +42,10 @@
4042
/**
4143
* @author Steve Ebersole
4244
*/
43-
@RequiresDialectFeatureGroup(
44-
value = {
45-
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsExpectedLobUsagePattern.class),
46-
@RequiresDialectFeature(feature = BasicOperationsTest.OracleDialectChecker.class)
47-
},
48-
jiraKey = "HHH-6834"
49-
)
45+
@SkipForDialect(dialectClass = OracleDialect.class, reason = "HHH-6834")
46+
@SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "HHH-6834")
47+
@SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "jConnect reports the type code 11 for bigdatetime columns, which is an unknown type code..")
48+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsExpectedLobUsagePattern.class, jiraKey = "HHH-6834")
5049
@DomainModel(
5150
annotatedClasses = { SomeEntity.class, SomeOtherEntity.class }
5251
)
@@ -60,13 +59,6 @@ public class BasicOperationsTest {
6059
private static final String SOME_OTHER_ENTITY_TABLE_NAME = "SOMEOTHERENTITY";
6160

6261

63-
public static class OracleDialectChecker implements DialectFeatureCheck {
64-
@Override
65-
public boolean apply(Dialect dialect) {
66-
return !( dialect instanceof OracleDialect ) && !( dialect instanceof PostgresPlusDialect );
67-
}
68-
}
69-
7062
@Test
7163
public void testCreateAndDelete(SessionFactoryScope scope) {
7264
Date now = new Date();

hibernate-core/src/test/java/org/hibernate/orm/test/version/db/DbVersionTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.hibernate.Transaction;
1515
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
1616

17+
import org.hibernate.dialect.SybaseDialect;
1718
import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
1819

1920
import static org.junit.Assert.assertFalse;
@@ -93,6 +94,10 @@ public void testCollectionNoVersion() {
9394
s.close();
9495

9596
Timestamp steveTimestamp = steve.getTimestamp();
97+
if ( getDialect() instanceof SybaseDialect ) {
98+
// Sybase has 1/300th sec precision, but not for the `getdate()` function which we use for DB generation
99+
steveTimestamp = new Timestamp( steveTimestamp.getTime() );
100+
}
96101

97102
s = openSession();
98103
t = s.beginTransaction();

0 commit comments

Comments
 (0)