Skip to content

Commit 38ec412

Browse files
jrenaatbeikov
authored andcommitted
HHH-15665 - Fix and added test for issue
Signed-off-by: Jan Schatteman <[email protected]>
1 parent 1078caa commit 38ec412

File tree

3 files changed

+173
-2
lines changed

3 files changed

+173
-2
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java

+72
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,64 @@ public static Identifier toIdentifier(String text, boolean quote) {
7575
}
7676
}
7777

78+
/**
79+
* Means to generate an {@link Identifier} instance from its simple text form.
80+
* <p>
81+
* If passed text is {@code null}, {@code null} is returned.
82+
* <p>
83+
* If passed text is surrounded in quote markers, the generated Identifier
84+
* is considered quoted. Quote markers include back-ticks (`),
85+
* double-quotes (") and brackets ([ and ]).
86+
*
87+
* @param text The text form
88+
* @param quote Whether to quote unquoted text forms
89+
* @param quoteOnNonIdentifierChar Controls whether to treat the result as quoted if text contains characters that are invalid for identifiers
90+
*
91+
* @return The identifier form, or {@code null} if text was {@code null}
92+
*/
93+
public static Identifier toIdentifier(String text, boolean quote, boolean quoteOnNonIdentifierChar) {
94+
if ( StringHelper.isEmpty( text ) ) {
95+
return null;
96+
}
97+
int start = 0;
98+
int end = text.length();
99+
while ( start < end ) {
100+
if ( !Character.isWhitespace( text.charAt( start ) ) ) {
101+
break;
102+
}
103+
start++;
104+
}
105+
while ( start < end ) {
106+
if ( !Character.isWhitespace( text.charAt( end - 1 ) ) ) {
107+
break;
108+
}
109+
end--;
110+
}
111+
if ( isQuoted( text, start, end ) ) {
112+
start++;
113+
end--;
114+
quote = true;
115+
}
116+
else if ( quoteOnNonIdentifierChar && !quote ) {
117+
// Check the letters to determine if we must quote the text
118+
char c = text.charAt( start );
119+
if ( !Character.isLetter( c ) && c != '_' ) {
120+
// SQL identifiers must begin with a letter or underscore
121+
quote = true;
122+
}
123+
else {
124+
for ( int i = start + 1; i < end; i++ ) {
125+
c = text.charAt( i );
126+
if ( !Character.isLetterOrDigit( c ) && c != '_' ) {
127+
quote = true;
128+
break;
129+
}
130+
}
131+
}
132+
}
133+
return new Identifier( text.substring( start, end ), quote );
134+
}
135+
78136
/**
79137
* Is the given identifier text considered quoted. The following patterns are
80138
* recognized as quoted:<ul>
@@ -96,6 +154,20 @@ public static boolean isQuoted(String name) {
96154
|| ( name.startsWith( "\"" ) && name.endsWith( "\"" ) );
97155
}
98156

157+
public static boolean isQuoted(String name, int start, int end) {
158+
if ( start + 2 < end ) {
159+
switch ( name.charAt( start ) ) {
160+
case '`':
161+
return name.charAt( end - 1 ) == '`';
162+
case '[':
163+
return name.charAt( end - 1 ) == ']';
164+
case '"':
165+
return name.charAt( end - 1 ) == '"';
166+
}
167+
}
168+
return false;
169+
}
170+
99171
/**
100172
* Constructs an identifier instance.
101173
*

hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/SequenceInformationExtractorMariaDBDatabaseImpl.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Collections;
1313
import java.util.List;
1414

15+
import org.hibernate.boot.model.naming.Identifier;
1516
import org.hibernate.boot.model.relational.QualifiedSequenceName;
1617
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
1718
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
@@ -28,7 +29,7 @@ public class SequenceInformationExtractorMariaDBDatabaseImpl extends SequenceInf
2829

2930
// SQL to get metadata from individual sequence
3031
private static final String SQL_SEQUENCE_QUERY =
31-
"SELECT '%1$s' as sequence_name, minimum_value, maximum_value, start_value, increment, cache_size FROM %1$s ";
32+
"SELECT '%1$s' as sequence_name, minimum_value, maximum_value, start_value, increment, cache_size FROM %2$s ";
3233

3334
private static final String UNION_ALL =
3435
"UNION ALL ";
@@ -56,7 +57,7 @@ public Iterable<SequenceInformation> extractMetadata(ExtractionContext extractio
5657
if ( sequenceInfoQueryBuilder.length() > 0 ) {
5758
sequenceInfoQueryBuilder.append( UNION_ALL );
5859
}
59-
sequenceInfoQueryBuilder.append( String.format( SQL_SEQUENCE_QUERY, sequenceName ) );
60+
sequenceInfoQueryBuilder.append( String.format( SQL_SEQUENCE_QUERY, sequenceName, Identifier.toIdentifier( sequenceName, false, true ) ) );
6061
}
6162
return extractionContext.getQueryResults(
6263
sequenceInfoQueryBuilder.toString(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.hibernate.test.dialect.functional;
2+
3+
import java.sql.Connection;
4+
import java.sql.SQLException;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Optional;
8+
import java.util.stream.StreamSupport;
9+
10+
import org.hibernate.boot.registry.StandardServiceRegistry;
11+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
12+
import org.hibernate.cfg.AvailableSettings;
13+
import org.hibernate.cfg.Environment;
14+
import org.hibernate.dialect.MariaDBDialect;
15+
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
16+
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
17+
import org.hibernate.engine.jdbc.spi.JdbcServices;
18+
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorMariaDBDatabaseImpl;
19+
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
20+
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
21+
import org.hibernate.testing.RequiresDialect;
22+
import org.hibernate.testing.TestForIssue;
23+
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
24+
25+
import org.junit.AfterClass;
26+
import org.junit.Assert;
27+
import org.junit.BeforeClass;
28+
import org.junit.Test;
29+
30+
import static org.hibernate.testing.transaction.TransactionUtil.doInAutoCommit;
31+
32+
/**
33+
* @author Jan Schatteman
34+
*/
35+
@RequiresDialect(value = MariaDBDialect.class)
36+
public class MariaDBExtractSequenceInformationTest extends BaseCoreFunctionalTestCase {
37+
38+
private final static String hhh15665SeqName = "HHH-15665-seq";
39+
40+
private final static Map<String, Object> settings = new HashMap<>(3);
41+
42+
static {
43+
settings.put( AvailableSettings.URL, Environment.getProperties().getProperty( AvailableSettings.URL ) );
44+
settings.put( AvailableSettings.USER, Environment.getProperties().getProperty( AvailableSettings.USER ) );
45+
settings.put( AvailableSettings.PASS, Environment.getProperties().getProperty( AvailableSettings.PASS ) );
46+
}
47+
48+
@BeforeClass
49+
public static void setUp() throws Exception {
50+
doInAutoCommit( settings, "CREATE SEQUENCE IF NOT EXISTS `" + hhh15665SeqName + "`" );
51+
}
52+
53+
@AfterClass
54+
public static void tearDown() throws SQLException {
55+
doInAutoCommit( settings, "DROP SEQUENCE IF EXISTS `" + hhh15665SeqName + "`" );
56+
}
57+
58+
@Test
59+
@TestForIssue( jiraKey = "HHH-15665" )
60+
public void testExtractSequenceInformationForSqlServerWithCaseSensitiveCollation() {
61+
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().applySettings( settings ).build();
62+
JdbcEnvironment jdbcEnvironment = ssr.getService( JdbcEnvironment.class );
63+
JdbcConnectionAccess bootstrapJdbcConnectionAccess = ssr.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess();
64+
65+
try ( Connection connection = bootstrapJdbcConnectionAccess.obtainConnection() ) {
66+
Iterable<SequenceInformation> sequenceInformations = SequenceInformationExtractorMariaDBDatabaseImpl.INSTANCE.extractMetadata(
67+
new ExtractionContext.EmptyExtractionContext() {
68+
@Override
69+
public Connection getJdbcConnection() {
70+
return connection;
71+
}
72+
73+
@Override
74+
public JdbcEnvironment getJdbcEnvironment() {
75+
return jdbcEnvironment;
76+
}
77+
} );
78+
79+
Assert.assertNotNull( sequenceInformations );
80+
81+
Optional<SequenceInformation> seq = StreamSupport.stream( sequenceInformations.spliterator(), false )
82+
.filter(
83+
sequence -> hhh15665SeqName.equals( sequence.getSequenceName()
84+
.getSequenceName()
85+
.getText() )
86+
)
87+
.findFirst();
88+
89+
Assert.assertTrue( hhh15665SeqName + " not found", seq.isPresent() );
90+
}
91+
catch ( SQLException e ) {
92+
Assert.fail( "Sequence information for " + hhh15665SeqName + " was not retrieved: " + e.getMessage() );
93+
}
94+
finally {
95+
StandardServiceRegistryBuilder.destroy( ssr );
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)