Skip to content

Commit 452f616

Browse files
committed
[HHH-18900][JBEAP-30676] Hibernate tests fail with MariaDB 11.6+
1 parent a41ac68 commit 452f616

File tree

9 files changed

+86
-14
lines changed

9 files changed

+86
-14
lines changed

databases/mariadb/matrix.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
55
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
66
*/
7-
jdbcDependency 'org.mariadb.jdbc:mariadb-java-client:3.4.0'
7+
jdbcDependency 'org.mariadb.jdbc:mariadb-java-client:3.5.1'

docker_db.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ mysql_8_2() {
9292
}
9393

9494
mariadb() {
95-
mariadb_11_4
95+
mariadb_11_7
9696
}
9797

9898
mariadb_wait_until_start()
@@ -138,6 +138,12 @@ mariadb_11_4() {
138138
mariadb_wait_until_start
139139
}
140140

141+
mariadb_11_7() {
142+
$CONTAINER_CLI rm -f mariadb || true
143+
$CONTAINER_CLI run --name mariadb -e MARIADB_USER=hibernate_orm_test -e MARIADB_PASSWORD=hibernate_orm_test -e MARIADB_DATABASE=hibernate_orm_test -e MARIADB_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d ${DB_IMAGE_MARIADB_11_7:-docker.io/mariadb:11.7-rc} --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --lower_case_table_names=2
144+
mariadb_wait_until_start
145+
}
146+
141147
mariadb_verylatest() {
142148
$CONTAINER_CLI rm -f mariadb || true
143149
$CONTAINER_CLI run --name mariadb -e MARIADB_USER=hibernate_orm_test -e MARIADB_PASSWORD=hibernate_orm_test -e MARIADB_DATABASE=hibernate_orm_test -e MARIADB_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d ${DB_IMAGE_MARIADB_VERYLATEST:-quay.io/mariadb-foundation/mariadb-devel:verylatest} --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --lower_case_table_names=2
@@ -992,6 +998,7 @@ if [ -z ${1} ]; then
992998
echo -e "\thana"
993999
echo -e "\tmariadb"
9941000
echo -e "\tmariadb_verylatest"
1001+
echo -e "\tmariadb_11_7"
9951002
echo -e "\tmariadb_11_4"
9961003
echo -e "\tmariadb_11_1"
9971004
echo -e "\tmariadb_10_11"

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.sql.DatabaseMetaData;
1010
import java.sql.SQLException;
1111

12+
import org.hibernate.PessimisticLockException;
1213
import org.hibernate.boot.model.FunctionContributions;
1314
import org.hibernate.boot.model.TypeContributions;
1415
import org.hibernate.dialect.function.CommonFunctionFactory;
@@ -21,6 +22,10 @@
2122
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
2223
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
2324
import org.hibernate.engine.spi.SessionFactoryImplementor;
25+
import org.hibernate.exception.ConstraintViolationException;
26+
import org.hibernate.exception.LockAcquisitionException;
27+
import org.hibernate.exception.LockTimeoutException;
28+
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
2429
import org.hibernate.service.ServiceRegistry;
2530
import org.hibernate.sql.ast.SqlAstTranslator;
2631
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
@@ -37,6 +42,7 @@
3742
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
3843
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
3944

45+
import static org.hibernate.internal.util.JdbcExceptionHelper.extractSqlState;
4046
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC;
4147
import static org.hibernate.type.SqlTypes.GEOMETRY;
4248
import static org.hibernate.type.SqlTypes.OTHER;
@@ -281,4 +287,44 @@ public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, D
281287
public String getDual() {
282288
return "dual";
283289
}
290+
291+
@Override
292+
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
293+
return (sqlException, message, sql) -> {
294+
switch ( sqlException.getErrorCode() ) {
295+
// If @@innodb_snapshot_isolation is set (default since 11.6.2),
296+
// if an attempt to acquire a lock on a record that does not exist in the current read view is made,
297+
// an error DB_RECORD_CHANGED will be raised.
298+
case 1020:
299+
return new LockAcquisitionException( message, sqlException, sql );
300+
case 1205:
301+
case 3572:
302+
return new PessimisticLockException( message, sqlException, sql );
303+
case 1207:
304+
case 1206:
305+
return new LockAcquisitionException( message, sqlException, sql );
306+
case 1062:
307+
// Unique constraint violation
308+
return new ConstraintViolationException(
309+
message,
310+
sqlException,
311+
sql,
312+
ConstraintViolationException.ConstraintKind.UNIQUE,
313+
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
314+
);
315+
}
316+
317+
final String sqlState = extractSqlState( sqlException );
318+
if ( sqlState != null ) {
319+
switch ( sqlState ) {
320+
case "41000":
321+
return new LockTimeoutException( message, sqlException, sql );
322+
case "40001":
323+
return new LockAcquisitionException( message, sqlException, sql );
324+
}
325+
}
326+
327+
return null;
328+
};
329+
}
284330
}

hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,9 @@ public class SqlTypes {
678678

679679

680680
/**
681-
* A type code representing an {@code embedding vector} type for databases like
682-
* {@link org.hibernate.dialect.PostgreSQLDialect PostgreSQL} and {@link org.hibernate.dialect.OracleDialect Oracle 23ai}.
681+
* A type code representing an {@code embedding vector} type for databases
682+
* like {@link org.hibernate.dialect.PostgreSQLDialect PostgreSQL},
683+
* {@link org.hibernate.dialect.OracleDialect Oracle 23ai} and {@link org.hibernate.dialect.MariaDBDialect MariaDB}.
683684
* An embedding vector essentially is a {@code float[]} with a fixed size.
684685
*
685686
* @since 6.4

hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchOptimisticLockingTest.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.hibernate.dialect.CockroachDialect;
1818
import org.hibernate.dialect.OracleDialect;
1919

20+
import org.hibernate.dialect.MariaDBDialect;
2021
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
2122
import org.junit.Test;
2223

@@ -72,7 +73,7 @@ public void testBatchAndOptimisticLocking() {
7273
} );
7374

7475
try {
75-
inTransaction( (session) -> {
76+
inTransaction( session -> {
7677
List<Person> persons = session
7778
.createSelectionQuery( "select p from Person p", Person.class )
7879
.getResultList();
@@ -109,10 +110,18 @@ public void testBatchAndOptimisticLocking() {
109110
);
110111
}
111112
else {
112-
assertEquals(
113-
"Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1; statement executed: update Person set name=?,version=? where id=? and version=?",
114-
expected.getMessage()
115-
);
113+
if ( getDialect() instanceof MariaDBDialect && getDialect().getVersion().isAfter( 11, 6, 2 )) {
114+
assertTrue(
115+
expected.getMessage()
116+
.contains( "Record has changed since last read in table 'Person'" )
117+
);
118+
} else {
119+
assertTrue(
120+
expected.getMessage()
121+
.startsWith(
122+
"Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1; statement executed: update Person set name=?,version=? where id=? and version=?" )
123+
);
124+
}
116125
}
117126
}
118127
}

hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/RepeatableReadTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
1414
import org.hibernate.cfg.AvailableSettings;
1515
import org.hibernate.dialect.CockroachDialect;
16+
import org.hibernate.dialect.MariaDBDialect;
1617
import org.hibernate.dialect.SQLServerDialect;
1718
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
1819
import org.hibernate.exception.SQLGrammarException;
@@ -106,6 +107,7 @@ public void testStaleVersionedInstanceFoundInQueryResult() {
106107

107108
@Test
108109
@SkipForDialect(dialectClass = CockroachDialect.class, reason = "Cockroach uses SERIALIZABLE by default and fails to acquire a write lock after a TX in between committed changes to a row")
110+
@SkipForDialect(dialectClass = MariaDBDialect.class, majorVersion = 11, minorVersion = 6, microVersion = 2, reason = "MariaDB will throw an error DB_RECORD_CHANGED when acquiring a lock on a record that have changed")
109111
public void testStaleVersionedInstanceFoundOnLock() {
110112
if ( !readCommittedIsolationMaintained( "repeatable read tests" ) ) {
111113
return;
@@ -228,6 +230,7 @@ public void testStaleNonVersionedInstanceFoundInQueryResult() {
228230

229231
@Test
230232
@SkipForDialect(dialectClass = CockroachDialect.class, reason = "Cockroach uses SERIALIZABLE by default and fails to acquire a write lock after a TX in between committed changes to a row")
233+
@SkipForDialect(dialectClass = MariaDBDialect.class, majorVersion = 11, minorVersion = 6, microVersion = 2, reason = "MariaDB will throw an error DB_RECORD_CHANGED when acquiring a lock on a record that have changed")
231234
public void testStaleNonVersionedInstanceFoundOnLock() {
232235
if ( !readCommittedIsolationMaintained( "repeatable read tests" ) ) {
233236
return;

hibernate-core/src/test/java/org/hibernate/orm/test/locking/OptimisticAndPessimisticLockTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import org.hibernate.LockMode;
88
import org.hibernate.dialect.CockroachDialect;
9-
9+
import org.hibernate.dialect.MariaDBDialect;
1010
import org.hibernate.testing.orm.junit.DomainModel;
1111
import org.hibernate.testing.orm.junit.JiraKey;
1212
import org.hibernate.testing.orm.junit.SessionFactory;
@@ -26,6 +26,7 @@
2626
@SessionFactory
2727
@JiraKey("HHH-16461")
2828
@SkipForDialect(dialectClass = CockroachDialect.class, reason = "CockroachDB uses SERIALIZABLE isolation, and does not support this")
29+
@SkipForDialect(dialectClass = MariaDBDialect.class, majorVersion = 11, minorVersion = 6, microVersion = 2, reason = "MariaDB will throw an error DB_RECORD_CHANGED when acquiring a lock on a record that have changed")
2930
public class OptimisticAndPessimisticLockTest {
3031

3132
public Stream<LockMode> pessimisticLockModes() {

hibernate-core/src/test/java/org/hibernate/orm/test/locking/OptimisticLockTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
import jakarta.persistence.Version;
1313
import org.hibernate.annotations.OptimisticLock;
1414
import org.hibernate.dialect.CockroachDialect;
15+
import org.hibernate.dialect.MariaDBDialect;
1516
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
16-
import org.hibernate.testing.SkipForDialect;
17+
import org.hibernate.testing.orm.junit.SkipForDialect;
1718
import org.junit.Test;
1819

1920
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -31,7 +32,8 @@ protected Class<?>[] getAnnotatedClasses() {
3132
}
3233

3334
@Test
34-
@SkipForDialect(value = CockroachDialect.class, comment = "Fails at SERIALIZABLE isolation")
35+
@SkipForDialect(dialectClass = CockroachDialect.class, reason = "Fails at SERIALIZABLE isolation")
36+
@SkipForDialect(dialectClass = MariaDBDialect.class, majorVersion = 11, minorVersion = 6, microVersion = 2, reason = "MariaDB will throw an error DB_RECORD_CHANGED when acquiring a lock on a record that have changed")
3537
public void test() {
3638
doInJPA(this::entityManagerFactory, entityManager -> {
3739
Phone phone = new Phone();

hibernate-core/src/test/java/org/hibernate/orm/test/optlock/OptimisticLockTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.hibernate.StaleStateException;
1616
import org.hibernate.dialect.CockroachDialect;
1717
import org.hibernate.dialect.Dialect;
18+
import org.hibernate.dialect.MariaDBDialect;
1819
import org.hibernate.dialect.SQLServerDialect;
1920

2021
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
@@ -25,6 +26,7 @@
2526
import org.junit.jupiter.api.AfterEach;
2627
import org.junit.jupiter.api.Test;
2728

29+
import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
2830
import static org.junit.jupiter.api.Assertions.fail;
2931

3032
/**
@@ -191,8 +193,9 @@ else if ( dialect instanceof CockroachDialect && ( (JDBCException) cause ).getSQ
191193
"40001" ) ) {
192194
// CockroachDB always runs in SERIALIZABLE isolation, and uses SQL state 40001 to indicate
193195
// serialization failure.
194-
}
195-
else {
196+
} else if (dialect instanceof MariaDBDialect && getDialect().getVersion().isAfter( 11, 6, 2 )) {
197+
// Mariadb snapshot_isolation throws error
198+
} else {
196199
throw e;
197200
}
198201
}

0 commit comments

Comments
 (0)