Skip to content

Commit 0cbcc76

Browse files
committed
squash! HHH-16516 test for automatic quoting
1 parent 26fc684 commit 0cbcc76

File tree

6 files changed

+76
-47
lines changed

6 files changed

+76
-47
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,7 @@ public String translateExtractField(TemporalUnit unit) {
927927
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
928928
throws SQLException {
929929
builder.setAutoQuoteInitialUnderscore(true);
930+
builder.setAutoQuoteDollar(true);
930931
return super.buildIdentifierHelper(builder, dbMetaData);
931932
}
932933

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

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ public static Identifier toIdentifier(String text, boolean quote) {
6868
/**
6969
* Means to generate an {@link Identifier} instance from its simple text form.
7070
* <p>
71-
* If passed text is {@code null}, {@code null} is returned.
71+
* If passed {@code text} is {@code null}, {@code null} is returned.
7272
* <p>
73-
* If passed text is surrounded in quote markers, the generated Identifier
74-
* is considered quoted. Quote markers include back-ticks (`),
75-
* double-quotes (") and brackets ([ and ]).
73+
* If passed {@code text} is surrounded in quote markers, the returned Identifier
74+
* is considered quoted. Quote markers include back-ticks (`), double-quotes ("),
75+
* and brackets ([ and ]).
7676
*
7777
* @param text The text form
7878
* @param quote Whether to quote unquoted text forms
79-
* @param quoteOnNonIdentifierChar Controls whether to treat the result as quoted if text contains characters that are invalid for identifiers
79+
* @param autoquote Whether to quote the result if it contains special characters
8080
*
8181
* @return The identifier form, or {@code null} if text was {@code null}
8282
*/
83-
public static Identifier toIdentifier(String text, boolean quote, boolean quoteOnNonIdentifierChar) {
83+
public static Identifier toIdentifier(String text, boolean quote, boolean autoquote) {
8484
if ( isBlank( text ) ) {
8585
return null;
8686
}
@@ -103,25 +103,40 @@ public static Identifier toIdentifier(String text, boolean quote, boolean quoteO
103103
end--;
104104
quote = true;
105105
}
106-
else if ( quoteOnNonIdentifierChar && !quote ) {
107-
// Check the letters to determine if we must quote the text
108-
char c = text.charAt( start );
109-
if ( !isLetter( c ) && c != '_' ) {
110-
// SQL identifiers must begin with a letter or underscore
111-
quote = true;
112-
}
113-
else {
114-
for ( int i = start + 1; i < end; i++ ) {
115-
c = text.charAt( i );
116-
// every database except HSQLDB also allows a $ here
117-
if ( !isLetterOrDigit( c ) && c != '_' ) {
118-
quote = true;
119-
break;
120-
}
106+
else if ( autoquote && !quote ) {
107+
quote = autoquote( text, start, end );
108+
}
109+
return new Identifier( text.substring( start, end ), quote );
110+
}
111+
112+
private static boolean autoquote(String text, int start, int end) {
113+
// Check the letters to determine if we must quote the text
114+
if ( !isLegalFirstChar( text.charAt( start ) ) ) {
115+
// SQL identifiers must begin with a letter or underscore
116+
return true;
117+
}
118+
else {
119+
for ( int i = start + 1; i < end; i++ ) {
120+
if ( !isLegalChar( text.charAt( i ) ) ) {
121+
return true;
121122
}
122123
}
123124
}
124-
return new Identifier( text.substring( start, end ), quote );
125+
return false;
126+
}
127+
128+
private static boolean isLegalChar(char current) {
129+
return isLetterOrDigit( current )
130+
// every database also allows _ here
131+
|| current == '_'
132+
// every database except HSQLDB also allows $ here
133+
|| current == '$';
134+
}
135+
136+
private static boolean isLegalFirstChar(char first) {
137+
return isLetter( first )
138+
// many databases also allow _ here
139+
|| first == '_';
125140
}
126141

127142
/**
@@ -143,21 +158,22 @@ public static boolean isQuoted(String name) {
143158

144159
public static boolean isQuoted(String name, int start, int end) {
145160
if ( start + 2 < end ) {
146-
switch ( name.charAt( start ) ) {
147-
case '`':
148-
return name.charAt( end - 1 ) == '`';
149-
case '[':
150-
return name.charAt( end - 1 ) == ']';
151-
case '"':
152-
return name.charAt( end - 1 ) == '"';
153-
}
161+
final char first = name.charAt( start );
162+
final char last = name.charAt( end - 1 );
163+
return switch ( first ) {
164+
case '`' -> last == '`';
165+
case '[' -> last == ']';
166+
case '"' -> last == '"';
167+
default -> false;
168+
};
169+
}
170+
else {
171+
return false;
154172
}
155-
return false;
156173
}
157174

158175
public static String unQuote(String name) {
159176
assert isQuoted( name );
160-
161177
return name.substring( 1, name.length() - 1 );
162178
}
163179

@@ -240,7 +256,7 @@ public String toString() {
240256
@Override
241257
public boolean equals(Object object) {
242258
return object instanceof Identifier that
243-
&& getCanonicalName().equals( that.getCanonicalName() );
259+
&& getCanonicalName().equals( that.getCanonicalName() );
244260
}
245261

246262
public boolean matches(String name) {
@@ -267,7 +283,7 @@ public static Identifier quote(Identifier identifier) {
267283
}
268284

269285
@Override
270-
public int compareTo(Identifier o) {
271-
return getCanonicalName().compareTo( o.getCanonicalName() );
286+
public int compareTo(Identifier identifier) {
287+
return getCanonicalName().compareTo( identifier.getCanonicalName() );
272288
}
273289
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ public String translateExtractField(TemporalUnit unit) {
712712
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
713713
throws SQLException {
714714
builder.setAutoQuoteInitialUnderscore( true );
715+
builder.setAutoQuoteDollar( true );
715716
return super.buildIdentifierHelper( builder, dbMetaData );
716717
}
717718

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/NormalizingIdentifierHelperImpl.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
2727
private final boolean globallyQuoteIdentifiersSkipColumnDefinitions;
2828
private final boolean autoQuoteKeywords;
2929
private final boolean autoQuoteInitialUnderscore;
30+
private final boolean autoQuoteDollar;
3031
private final TreeSet<String> reservedWords;
3132
private final IdentifierCaseStrategy unquotedCaseStrategy;
3233
private final IdentifierCaseStrategy quotedCaseStrategy;
@@ -38,6 +39,7 @@ public NormalizingIdentifierHelperImpl(
3839
boolean globallyQuoteIdentifiersSkipColumnDefinitions,
3940
boolean autoQuoteKeywords,
4041
boolean autoQuoteInitialUnderscore,
42+
boolean autoQuoteDollar,
4143
TreeSet<String> reservedWords, //careful, we intentionally omit making a defensive copy to not waste memory
4244
IdentifierCaseStrategy unquotedCaseStrategy,
4345
IdentifierCaseStrategy quotedCaseStrategy) {
@@ -47,6 +49,7 @@ public NormalizingIdentifierHelperImpl(
4749
this.globallyQuoteIdentifiersSkipColumnDefinitions = globallyQuoteIdentifiersSkipColumnDefinitions;
4850
this.autoQuoteKeywords = autoQuoteKeywords;
4951
this.autoQuoteInitialUnderscore = autoQuoteInitialUnderscore;
52+
this.autoQuoteDollar = autoQuoteDollar;
5053
this.reservedWords = reservedWords;
5154
this.unquotedCaseStrategy = unquotedCaseStrategy == null ? IdentifierCaseStrategy.UPPER : unquotedCaseStrategy;
5255
this.quotedCaseStrategy = quotedCaseStrategy == null ? IdentifierCaseStrategy.MIXED : quotedCaseStrategy;
@@ -57,24 +60,22 @@ public Identifier normalizeQuoting(Identifier identifier) {
5760
if ( identifier == null ) {
5861
return null;
5962
}
60-
61-
if ( identifier.isQuoted() ) {
63+
else if ( identifier.isQuoted() ) {
6264
return identifier;
6365
}
64-
65-
if ( globallyQuoteIdentifiers ) {
66-
return Identifier.toIdentifier( identifier.getText(), true );
67-
}
68-
69-
if ( autoQuoteKeywords && isReservedWord( identifier.getText() ) ) {
66+
else if ( mustQuote( identifier ) ) {
7067
return Identifier.toIdentifier( identifier.getText(), true );
7168
}
72-
73-
if ( autoQuoteInitialUnderscore && identifier.getText().startsWith("_") ) {
74-
return Identifier.toIdentifier( identifier.getText(), true );
69+
else {
70+
return identifier;
7571
}
72+
}
7673

77-
return identifier;
74+
private boolean mustQuote(Identifier identifier) {
75+
return globallyQuoteIdentifiers
76+
|| autoQuoteKeywords && isReservedWord( identifier.getText() )
77+
|| autoQuoteInitialUnderscore && identifier.getText().startsWith( "_" )
78+
|| autoQuoteDollar && identifier.getText().contains( "$" );
7879
}
7980

8081
@Override

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/IdentifierHelperBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class IdentifierHelperBuilder {
4040
private boolean skipGlobalQuotingForColumnDefinitions = false;
4141
private boolean autoQuoteKeywords = true;
4242
private boolean autoQuoteInitialUnderscore = false;
43+
private boolean autoQuoteDollar = false;
4344
private IdentifierCaseStrategy unquotedCaseStrategy = IdentifierCaseStrategy.UPPER;
4445
private IdentifierCaseStrategy quotedCaseStrategy = IdentifierCaseStrategy.MIXED;
4546

@@ -150,6 +151,10 @@ public void setAutoQuoteInitialUnderscore(boolean autoQuoteInitialUnderscore) {
150151
this.autoQuoteInitialUnderscore = autoQuoteInitialUnderscore;
151152
}
152153

154+
public void setAutoQuoteDollar(boolean autoQuoteDollar) {
155+
this.autoQuoteDollar = autoQuoteDollar;
156+
}
157+
153158
public NameQualifierSupport getNameQualifierSupport() {
154159
return nameQualifierSupport;
155160
}
@@ -215,6 +220,7 @@ public IdentifierHelper build() {
215220
skipGlobalQuotingForColumnDefinitions,
216221
autoQuoteKeywords,
217222
autoQuoteInitialUnderscore,
223+
autoQuoteDollar,
218224
reservedWords,
219225
unquotedCaseStrategy,
220226
quotedCaseStrategy

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/autoquote/SpecialCharactersTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,9 @@ public class SpecialCharactersTest {
2727
String nameWithHash;
2828
@Column(name="NAME NAME")
2929
String nameWithSpace;
30+
@Column(name="NAME_NAME")
31+
String nameWithUnderscore;
32+
@Column(name="_NAME")
33+
String nameWithInitialUnderscore;
3034
}
3135
}

0 commit comments

Comments
 (0)