HHH-15511 - fix version determination for CockroachDB

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2022-09-15 00:27:10 +02:00 committed by Christian Beikov
parent e62a376262
commit 1d76f970e8
3 changed files with 181 additions and 2 deletions

View File

@ -7,6 +7,7 @@
package org.hibernate.dialect; package org.hibernate.dialect;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
@ -14,6 +15,8 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
@ -34,6 +37,7 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
@ -63,6 +67,8 @@ import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType; import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.jboss.logging.Logger;
import static org.hibernate.query.sqm.TemporalUnit.DAY; import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.NATIVE; import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BINARY;
@ -97,10 +103,14 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM
*/ */
public class CockroachDialect extends Dialect { public class CockroachDialect extends Dialect {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, CockroachDialect.class.getName() );
private static final CockroachDBIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new CockroachDBIdentityColumnSupport(); private static final CockroachDBIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new CockroachDBIdentityColumnSupport();
// KNOWN LIMITATIONS: // KNOWN LIMITATIONS:
// * no support for java.sql.Clob // * no support for java.sql.Clob
// Pre-compile and reuse pattern
private static final Pattern CRDB_VERSION_PATTERN = Pattern.compile( "v[\\d]+(\\.[\\d]+)?(\\.[\\d]+)?" );
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 21, 1 ); private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 21, 1 );
private final PostgreSQLDriverKind driverKind; private final PostgreSQLDriverKind driverKind;
@ -110,8 +120,8 @@ public class CockroachDialect extends Dialect {
} }
public CockroachDialect(DialectResolutionInfo info) { public CockroachDialect(DialectResolutionInfo info) {
super(info); this( fetchDataBaseVersion( info ), PostgreSQLDriverKind.determineKind( info ) );
driverKind = PostgreSQLDriverKind.determineKind( info ); registerKeywords( info );
} }
public CockroachDialect(DatabaseVersion version) { public CockroachDialect(DatabaseVersion version) {
@ -124,6 +134,46 @@ public class CockroachDialect extends Dialect {
this.driverKind = driverKind; this.driverKind = driverKind;
} }
protected static DatabaseVersion fetchDataBaseVersion( DialectResolutionInfo info ) {
String versionString = null;
if ( info.getDatabaseMetadata() != null ) {
try (java.sql.Statement s = info.getDatabaseMetadata().getConnection().createStatement() ) {
final ResultSet rs = s.executeQuery( "SELECT version()" );
if ( rs.next() ) {
versionString = rs.getString( 1 );
}
}
catch (SQLException ex) {
// Ignore
}
}
return parseVersion( versionString );
}
protected static DatabaseVersion parseVersion(String versionString ) {
DatabaseVersion databaseVersion = null;
// What the DB select returns is similar to "CockroachDB CCL v21.2.10 (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)"
Matcher m = CRDB_VERSION_PATTERN.matcher( versionString == null ? "" : versionString );
if ( m.find() ) {
String[] versionParts = m.group().substring( 1 ).split( "\\." );
// if we got to this point, there is at least a major version, so no need to check [].length > 0
int majorVersion = Integer.parseInt( versionParts[0] );
int minorVersion = versionParts.length > 1 ? Integer.parseInt( versionParts[1] ) : 0;
int microVersion = versionParts.length > 2 ? Integer.parseInt( versionParts[2] ) : 0;
databaseVersion= new SimpleDatabaseVersion( majorVersion, minorVersion, microVersion);
}
if ( databaseVersion == null ) {
LOG.unableToDetermineCockroachDatabaseVersion(
MINIMUM_VERSION.getDatabaseMajorVersion() + "." +
MINIMUM_VERSION.getDatabaseMinorVersion() + "." +
MINIMUM_VERSION.getDatabaseMicroVersion()
);
databaseVersion = MINIMUM_VERSION;
}
return databaseVersion;
}
@Override @Override
protected DatabaseVersion getMinimumSupportedVersion() { protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION; return MINIMUM_VERSION;

View File

@ -1794,4 +1794,8 @@ public interface CoreMessageLogger extends BasicLogger {
@Message(value = "The %2$s version for [%s] is no longer supported, hence certain features may not work properly. The minimum supported version is %3$s. Check the community dialects project for available legacy versions.", id = 511) @Message(value = "The %2$s version for [%s] is no longer supported, hence certain features may not work properly. The minimum supported version is %3$s. Check the community dialects project for available legacy versions.", id = 511)
void unsupportedDatabaseVersion(String databaseName, String actualVersion, String minimumVersion); void unsupportedDatabaseVersion(String databaseName, String actualVersion, String minimumVersion);
@LogMessage(level = WARN)
@Message(value = "The database version version for the Cockroach Dialect could not be determined. The minimum supported version (%s) has been set instead.", id = 512)
void unableToDetermineCockroachDatabaseVersion(String minimumVersion);
} }

View File

@ -0,0 +1,125 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.dialect.unit;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.Triggerable;
import org.hibernate.testing.orm.logger.LoggerInspectionExtension;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.jboss.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* @author Jan Schatteman
*/
public class CockroachDialectVersionTest {
private Triggerable triggerable;
@RegisterExtension
public LoggerInspectionExtension logger = LoggerInspectionExtension
.builder().setLogger(
Logger.getMessageLogger( CoreMessageLogger.class, CockroachDialect.class.getName() )
).build();
@BeforeEach
public void setUp() {
triggerable = logger.watchForLogMessages("HHH000512" );
triggerable.reset();
}
@Test
@TestForIssue(jiraKey = "HHH-15511")
public void testCockroachDialectVersionParsing() {
String failMsg = "HHH000511: The database version version for the Cockroach Dialect could not be determined ... should have been logged";
CockroachDBTestDialect testDialect = new CockroachDBTestDialect( null );
Assertions.assertTrue( triggerable.wasTriggered(), failMsg);
DatabaseVersion dv = testDialect.getVersion();
assertNotNull( dv );
assertEquals( testDialect.getMinimumVersion().getDatabaseMajorVersion(), dv.getMajor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMinorVersion(), dv.getMinor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMicroVersion(), dv.getMicro() );
triggerable.reset();
testDialect = new CockroachDBTestDialect( "" );
Assertions.assertTrue( triggerable.wasTriggered(), failMsg);
dv = testDialect.getVersion();
assertNotNull( dv );
assertEquals( testDialect.getMinimumVersion().getDatabaseMajorVersion(), dv.getMajor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMinorVersion(), dv.getMinor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMicroVersion(), dv.getMicro() );
triggerable.reset();
testDialect = new CockroachDBTestDialect( "Some version lacking string" );
Assertions.assertTrue( triggerable.wasTriggered(), failMsg);
dv = testDialect.getVersion();
assertNotNull( dv );
assertEquals( testDialect.getMinimumVersion().getDatabaseMajorVersion(), dv.getMajor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMinorVersion(), dv.getMinor() );
assertEquals( testDialect.getMinimumVersion().getDatabaseMicroVersion(), dv.getMicro() );
triggerable.reset();
// using a fictitious major version, to avoid minimum version warnings
Dialect dialect = new CockroachDBTestDialect( "CockroachDB CCL v99.2.10 (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)" );
dv = dialect.getVersion();
assertNotNull( dv );
assertEquals( 99, dv.getMajor() );
assertEquals( 2, dv.getMinor() );
assertEquals( 10, dv.getMicro() );
dialect = new CockroachDBTestDialect("CockroachDB CCL v99.2. (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)");
dv = dialect.getVersion();
assertNotNull( dv );
assertEquals( 99, dv.getMajor() );
assertEquals( 2, dv.getMinor() );
assertEquals( 0, dv.getMicro() );
dialect = new CockroachDBTestDialect("CockroachDB CCL v99.2 (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)");
dv = dialect.getVersion();
assertNotNull( dv );
assertEquals( 99, dv.getMajor() );
assertEquals( 2, dv.getMinor() );
assertEquals( 0, dv.getMicro() );
dialect = new CockroachDBTestDialect("CockroachDB CCL v99. (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)");
dv = dialect.getVersion();
assertNotNull( dv );
assertEquals( 99, dv.getMajor() );
assertEquals( 0, dv.getMinor() );
assertEquals( 0, dv.getMicro() );
dialect = new CockroachDBTestDialect("CockroachDB CCL v99 (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6)");
dv = dialect.getVersion();
assertNotNull( dv );
assertEquals( 99, dv.getMajor() );
assertEquals( 0, dv.getMinor() );
assertEquals( 0, dv.getMicro() );
}
private static final class CockroachDBTestDialect extends CockroachDialect {
private CockroachDBTestDialect(String versionString) {
super (parseVersion( versionString ));
}
private DatabaseVersion getMinimumVersion() {
return getMinimumSupportedVersion();
}
}
}