HHH-16131 - Added workaround and test for date calculcation errors on Oracle

Temporarily excluded TiDB from that test (until they fix https://github.com/pingcap/tidb/issues/41052)

Added tidb to the docker_db script

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2023-02-02 16:15:28 +01:00 committed by Christian Beikov
parent b918909359
commit 989a127b17
3 changed files with 83 additions and 51 deletions

View File

@ -769,6 +769,33 @@ EOF
} }
tidb() {
tidb_5_1
}
tidb_5_1() {
$CONTAINER_CLI rm -f tidb || true
$CONTAINER_CLI run --name tidb -p4000:4000 -d docker.io/pingcap/tidb:v5.1.4
# Give the container some time to start
OUTPUT=
n=0
until [ "$n" -ge 5 ]
do
OUTPUT=$($CONTAINER_CLI logs tidb 2>&1)
if [[ $OUTPUT == *"server is running"* ]]; then
break;
fi
n=$((n+1))
echo "Waiting for TiDB to start..."
sleep 3
done
if [ "$n" -ge 5 ]; then
echo "TiDB failed to start and configure after 15 seconds"
else
echo "TiDB successfully started"
fi
}
if [ -z ${1} ]; then if [ -z ${1} ]; then
echo "No db name provided" echo "No db name provided"
echo "Provide one of:" echo "Provide one of:"
@ -804,6 +831,8 @@ if [ -z ${1} ]; then
echo -e "\tpostgresql_10" echo -e "\tpostgresql_10"
echo -e "\tpostgresql_9_5" echo -e "\tpostgresql_9_5"
echo -e "\tsybase" echo -e "\tsybase"
echo -e "\ttidb"
echo -e "\ttidb_5_1"
else else
${1} ${1}
fi fi

View File

@ -150,6 +150,14 @@ public class OracleDialect extends Dialect {
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
private static final String yqmSelect =
"( SELECT b_.bd + ( LEAST( EXTRACT( DAY FROM b_.od ), EXTRACT( DAY FROM LAST_DAY( b_.bd ) ) ) - 1 )\n" +
"FROM (SELECT a_.od, TRUNC(a_.od, 'MONTH') + NUMTOYMINTERVAL(%1$s, 'MONTH') bd FROM ( SELECT %2$s od FROM dual ) a_) b_ ) ";
private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
private static final String ADD_MONTH_EXPRESSION = String.format( yqmSelect, "?2", "?3" );
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 11, 2 ); private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 11, 2 );
private final LimitHandler limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY ) private final LimitHandler limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY )
@ -480,63 +488,36 @@ public class OracleDialect extends Dialect {
@Override @Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
StringBuilder pattern = new StringBuilder(); StringBuilder pattern = new StringBuilder();
pattern.append("(?3+");
switch ( unit ) { switch ( unit ) {
case YEAR: case YEAR:
pattern.append( ADD_YEAR_EXPRESSION );
break;
case QUARTER: case QUARTER:
pattern.append( ADD_QUARTER_EXPRESSION );
break;
case MONTH: case MONTH:
pattern.append("numtoyminterval"); pattern.append( ADD_MONTH_EXPRESSION );
break; break;
case WEEK: case WEEK:
pattern.append("(?3+numtodsinterval((?2)*7,'day'))");
break;
case DAY: case DAY:
case HOUR: case HOUR:
case MINUTE: case MINUTE:
case SECOND: case SECOND:
pattern.append("(?3+numtodsinterval(?2,'?1'))");
break;
case NANOSECOND: case NANOSECOND:
pattern.append("(?3+numtodsinterval((?2)/1e9,'second'))");
break;
case NATIVE: case NATIVE:
pattern.append("numtodsinterval"); pattern.append("(?3+numtodsinterval(?2,'second'))");
break; break;
default: default:
throw new SemanticException(unit + " is not a legal field"); throw new SemanticException(unit + " is not a legal field");
} }
pattern.append("(");
switch ( unit ) {
case NANOSECOND:
case QUARTER:
case WEEK:
pattern.append("(");
break;
}
pattern.append("?2");
switch ( unit ) {
case QUARTER:
pattern.append(")*3");
break;
case WEEK:
pattern.append(")*7");
break;
case NANOSECOND:
pattern.append(")/1e9");
break;
}
pattern.append(",'");
switch ( unit ) {
case QUARTER:
pattern.append("month");
break;
case WEEK:
pattern.append("day");
break;
case NANOSECOND:
case NATIVE:
pattern.append("second");
break;
default:
pattern.append("?1");
}
pattern.append("')");
pattern.append(")");
return pattern.toString(); return pattern.toString();
} }

View File

@ -10,14 +10,12 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.TiDBDialect; import org.hibernate.dialect.TiDBDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
@ -28,10 +26,10 @@ import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -51,6 +49,7 @@ import org.hamcrest.Matchers;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.isOneOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@ -58,7 +57,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@ServiceRegistry
@DomainModel( standardModels = StandardDomainModel.GAMBIT ) @DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory @SessionFactory
public class FunctionTests { public class FunctionTests {
@ -1314,20 +1312,44 @@ public class FunctionTests {
} }
@Test @Test
@SkipForDialect( dialectClass = TiDBDialect.class, reason = "Bug in the TiDB timestampadd function (https://github.com/pingcap/tidb/issues/41052)")
public void testDurationArithmetic(SessionFactoryScope scope) { public void testDurationArithmetic(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
assertEquals( LocalDate.now().minus(2, ChronoUnit.DAYS), assertEquals( LocalDate.now().minus(2, ChronoUnit.DAYS),
session.createQuery("select local date - 2 day") session.createQuery("select local date - 2 day", LocalDate.class)
.getSingleResult() ); .getSingleResult() );
assertEquals( LocalDate.now().plus(1, ChronoUnit.MONTHS), assertEquals( LocalDate.now().plus(1, ChronoUnit.MONTHS),
session.createQuery("select local date + 1 month") session.createQuery("select local date + 1 month", LocalDate.class)
.getSingleResult() ); .getSingleResult() );
session.createQuery("select e.theTimestamp - 21 second from EntityOfBasics e") assertEquals( LocalDate.now().plus(1, ChronoUnit.YEARS),
session.createQuery("select local date + 1 year", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.now().plus(3, ChronoUnit.MONTHS),
session.createQuery("select local date + 1 quarter", LocalDate.class)
.getSingleResult() );
// Some explicit 'special' cases:
assertEquals( LocalDate.of(2024, 02, 29),
session.createQuery("select {2024-01-31} + 1 month", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.of(2025, 02, 28),
session.createQuery("select {2024-02-29} + 1 year", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.of(2028, 02, 29),
session.createQuery("select {2024-02-29} + 4 year", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.of(2025, 03, 29),
session.createQuery("select {2024-02-29} + 13 month", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.of(2024, 02, 29),
session.createQuery("select {2023-11-30} + 1 quarter", LocalDate.class)
.getSingleResult() );
session.createQuery("select e.theTimestamp - 21 second from EntityOfBasics e", java.util.Date.class)
.getSingleResult(); .getSingleResult();
session.createQuery("select e.theTimestamp + 2 day from EntityOfBasics e") session.createQuery("select e.theTimestamp + 2 day from EntityOfBasics e", java.util.Date.class)
.getSingleResult(); .getSingleResult();
session.createQuery("select e.theTimestamp - 21 second + 2 day from EntityOfBasics e") session.createQuery("select e.theTimestamp - 21 second + 2 day from EntityOfBasics e", java.util.Date.class)
.getSingleResult(); .getSingleResult();
//TODO: FIX!! //TODO: FIX!!
// session.createQuery("select e.theTimestamp + 2 * e.theDuration from EntityOfBasics e") // session.createQuery("select e.theTimestamp + 2 * e.theDuration from EntityOfBasics e")