HHH-15665 - Fix and added test for issue

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2023-02-02 16:06:36 +01:00 committed by Christian Beikov
parent 1078caa19f
commit 38ec412e61
3 changed files with 173 additions and 2 deletions

View File

@ -75,6 +75,64 @@ public class Identifier implements Comparable<Identifier> {
}
}
/**
* Means to generate an {@link Identifier} instance from its simple text form.
* <p>
* If passed text is {@code null}, {@code null} is returned.
* <p>
* If passed text is surrounded in quote markers, the generated Identifier
* is considered quoted. Quote markers include back-ticks (`),
* double-quotes (") and brackets ([ and ]).
*
* @param text The text form
* @param quote Whether to quote unquoted text forms
* @param quoteOnNonIdentifierChar Controls whether to treat the result as quoted if text contains characters that are invalid for identifiers
*
* @return The identifier form, or {@code null} if text was {@code null}
*/
public static Identifier toIdentifier(String text, boolean quote, boolean quoteOnNonIdentifierChar) {
if ( StringHelper.isEmpty( text ) ) {
return null;
}
int start = 0;
int end = text.length();
while ( start < end ) {
if ( !Character.isWhitespace( text.charAt( start ) ) ) {
break;
}
start++;
}
while ( start < end ) {
if ( !Character.isWhitespace( text.charAt( end - 1 ) ) ) {
break;
}
end--;
}
if ( isQuoted( text, start, end ) ) {
start++;
end--;
quote = true;
}
else if ( quoteOnNonIdentifierChar && !quote ) {
// Check the letters to determine if we must quote the text
char c = text.charAt( start );
if ( !Character.isLetter( c ) && c != '_' ) {
// SQL identifiers must begin with a letter or underscore
quote = true;
}
else {
for ( int i = start + 1; i < end; i++ ) {
c = text.charAt( i );
if ( !Character.isLetterOrDigit( c ) && c != '_' ) {
quote = true;
break;
}
}
}
}
return new Identifier( text.substring( start, end ), quote );
}
/**
* Is the given identifier text considered quoted. The following patterns are
* recognized as quoted:<ul>
@ -96,6 +154,20 @@ public class Identifier implements Comparable<Identifier> {
|| ( name.startsWith( "\"" ) && name.endsWith( "\"" ) );
}
public static boolean isQuoted(String name, int start, int end) {
if ( start + 2 < end ) {
switch ( name.charAt( start ) ) {
case '`':
return name.charAt( end - 1 ) == '`';
case '[':
return name.charAt( end - 1 ) == ']';
case '"':
return name.charAt( end - 1 ) == '"';
}
}
return false;
}
/**
* Constructs an identifier instance.
*

View File

@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
@ -28,7 +29,7 @@ public class SequenceInformationExtractorMariaDBDatabaseImpl extends SequenceInf
// SQL to get metadata from individual sequence
private static final String SQL_SEQUENCE_QUERY =
"SELECT '%1$s' as sequence_name, minimum_value, maximum_value, start_value, increment, cache_size FROM %1$s ";
"SELECT '%1$s' as sequence_name, minimum_value, maximum_value, start_value, increment, cache_size FROM %2$s ";
private static final String UNION_ALL =
"UNION ALL ";
@ -56,7 +57,7 @@ public class SequenceInformationExtractorMariaDBDatabaseImpl extends SequenceInf
if ( sequenceInfoQueryBuilder.length() > 0 ) {
sequenceInfoQueryBuilder.append( UNION_ALL );
}
sequenceInfoQueryBuilder.append( String.format( SQL_SEQUENCE_QUERY, sequenceName ) );
sequenceInfoQueryBuilder.append( String.format( SQL_SEQUENCE_QUERY, sequenceName, Identifier.toIdentifier( sequenceName, false, true ) ) );
}
return extractionContext.getQueryResults(
sequenceInfoQueryBuilder.toString(),

View File

@ -0,0 +1,98 @@
package org.hibernate.test.dialect.functional;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.StreamSupport;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorMariaDBDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInAutoCommit;
/**
* @author Jan Schatteman
*/
@RequiresDialect(value = MariaDBDialect.class)
public class MariaDBExtractSequenceInformationTest extends BaseCoreFunctionalTestCase {
private final static String hhh15665SeqName = "HHH-15665-seq";
private final static Map<String, Object> settings = new HashMap<>(3);
static {
settings.put( AvailableSettings.URL, Environment.getProperties().getProperty( AvailableSettings.URL ) );
settings.put( AvailableSettings.USER, Environment.getProperties().getProperty( AvailableSettings.USER ) );
settings.put( AvailableSettings.PASS, Environment.getProperties().getProperty( AvailableSettings.PASS ) );
}
@BeforeClass
public static void setUp() throws Exception {
doInAutoCommit( settings, "CREATE SEQUENCE IF NOT EXISTS `" + hhh15665SeqName + "`" );
}
@AfterClass
public static void tearDown() throws SQLException {
doInAutoCommit( settings, "DROP SEQUENCE IF EXISTS `" + hhh15665SeqName + "`" );
}
@Test
@TestForIssue( jiraKey = "HHH-15665" )
public void testExtractSequenceInformationForSqlServerWithCaseSensitiveCollation() {
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().applySettings( settings ).build();
JdbcEnvironment jdbcEnvironment = ssr.getService( JdbcEnvironment.class );
JdbcConnectionAccess bootstrapJdbcConnectionAccess = ssr.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess();
try ( Connection connection = bootstrapJdbcConnectionAccess.obtainConnection() ) {
Iterable<SequenceInformation> sequenceInformations = SequenceInformationExtractorMariaDBDatabaseImpl.INSTANCE.extractMetadata(
new ExtractionContext.EmptyExtractionContext() {
@Override
public Connection getJdbcConnection() {
return connection;
}
@Override
public JdbcEnvironment getJdbcEnvironment() {
return jdbcEnvironment;
}
} );
Assert.assertNotNull( sequenceInformations );
Optional<SequenceInformation> seq = StreamSupport.stream( sequenceInformations.spliterator(), false )
.filter(
sequence -> hhh15665SeqName.equals( sequence.getSequenceName()
.getSequenceName()
.getText() )
)
.findFirst();
Assert.assertTrue( hhh15665SeqName + " not found", seq.isPresent() );
}
catch ( SQLException e ) {
Assert.fail( "Sequence information for " + hhh15665SeqName + " was not retrieved: " + e.getMessage() );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
}