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 0b4b52b08a
commit 35682e50da
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
echo "No db name provided"
echo "Provide one of:"
@ -804,6 +831,8 @@ if [ -z ${1} ]; then
echo -e "\tpostgresql_10"
echo -e "\tpostgresql_9_5"
echo -e "\tsybase"
echo -e "\ttidb"
echo -e "\ttidb_5_1"
else
${1}
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";
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 final LimitHandler limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY )
@ -480,63 +488,36 @@ public class OracleDialect extends Dialect {
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
StringBuilder pattern = new StringBuilder();
pattern.append("(?3+");
switch ( unit ) {
case YEAR:
pattern.append( ADD_YEAR_EXPRESSION );
break;
case QUARTER:
pattern.append( ADD_QUARTER_EXPRESSION );
break;
case MONTH:
pattern.append("numtoyminterval");
pattern.append( ADD_MONTH_EXPRESSION );
break;
case WEEK:
pattern.append("(?3+numtodsinterval((?2)*7,'day'))");
break;
case DAY:
case HOUR:
case MINUTE:
case SECOND:
pattern.append("(?3+numtodsinterval(?2,'?1'))");
break;
case NANOSECOND:
pattern.append("(?3+numtodsinterval((?2)/1e9,'second'))");
break;
case NATIVE:
pattern.append("numtodsinterval");
pattern.append("(?3+numtodsinterval(?2,'second'))");
break;
default:
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();
}

View File

@ -10,14 +10,12 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.TiDBDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.domain.StandardDomainModel;
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.RequiresDialect;
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.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -51,6 +49,7 @@ import org.hamcrest.Matchers;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isOneOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ -58,7 +57,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author Gavin King
*/
@ServiceRegistry
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory
public class FunctionTests {
@ -1314,20 +1312,44 @@ public class FunctionTests {
}
@Test
@SkipForDialect( dialectClass = TiDBDialect.class, reason = "Bug in the TiDB timestampadd function (https://github.com/pingcap/tidb/issues/41052)")
public void testDurationArithmetic(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertEquals( LocalDate.now().minus(2, ChronoUnit.DAYS),
session.createQuery("select local date - 2 day")
session.createQuery("select local date - 2 day", LocalDate.class)
.getSingleResult() );
assertEquals( LocalDate.now().plus(1, ChronoUnit.MONTHS),
session.createQuery("select local date + 1 month")
.getSingleResult() );
session.createQuery("select e.theTimestamp - 21 second from EntityOfBasics e")
session.createQuery("select local date + 1 month", LocalDate.class)
.getSingleResult() );
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();
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();
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();
//TODO: FIX!!
// session.createQuery("select e.theTimestamp + 2 * e.theDuration from EntityOfBasics e")