Skip to content

Commit 9ae7d4b

Browse files
ShivamVerma380ybudweiserashwinrathod78
committed
HHH-19471 - Hashing a constraint name using SHA-256 if the MD5 algorithm is not available
Co-authored-by: Yitzchak Weiser <[email protected]> Co-authored-by: Ashwinkumar Rathod <[email protected]>
1 parent 6193c28 commit 9ae7d4b

File tree

5 files changed

+96
-25
lines changed

5 files changed

+96
-25
lines changed

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.List;
1313

1414
import org.hibernate.HibernateException;
15+
import org.jboss.logging.Logger;
1516

1617
import static java.util.Comparator.comparing;
1718

@@ -30,6 +31,8 @@ public static NamingHelper withCharset(String charset) {
3031

3132
private final String charset;
3233

34+
private static final Logger log = Logger.getLogger(NamingHelper.class);
35+
3336
public NamingHelper() {
3437
this(null);
3538
}
@@ -124,8 +127,8 @@ public String generateHashedConstraintName(
124127
}
125128

126129
/**
127-
* Hash a constraint name using SHA-256. Convert the SHA-256 digest to base 35
128-
* (full alphanumeric), guaranteeing
130+
* Hash a constraint name using MD5. If MD5 is not available, fall back to SHA-256.
131+
* Convert the digest to base 35 (full alphanumeric), guaranteeing
129132
* that the length of the name will always be smaller than the 30
130133
* character identifier restriction enforced by a few dialects.
131134
*
@@ -135,17 +138,36 @@ public String generateHashedConstraintName(
135138
*/
136139
public String hashedName(String name) {
137140
try {
138-
final MessageDigest md = MessageDigest.getInstance( "SHA-256" );
139-
md.reset();
140-
md.update( charset != null ? name.getBytes( charset ) : name.getBytes() );
141-
final BigInteger bigInt = new BigInteger( 1, md.digest() );
142-
// By converting to base 35 (full alphanumeric), we guarantee
143-
// that the length of the name will always be smaller than the 30
144-
// character identifier restriction enforced by a few dialects.
145-
return bigInt.toString( 35 );
141+
return hashWithAlgorithm(name, "MD5");
146142
}
147-
catch ( NoSuchAlgorithmException | UnsupportedEncodingException e ) {
148-
throw new HibernateException( "Unable to generate a hashed name", e );
143+
catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
144+
log.warnf("MD5 algorithm failed for hashedName, falling back to SHA-256: %s", e.getMessage());
145+
try {
146+
return hashWithAlgorithm(name, "SHA-256");
147+
}
148+
catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
149+
throw new HibernateException("Unable to generate a hashed name", ex);
150+
}
149151
}
150152
}
153+
154+
/**
155+
* Helper to hash a name with the given algorithm and convert to base 35.
156+
*
157+
* @param name The name to be hashed.
158+
* @param algorithm The hashing algorithm to use.
159+
*
160+
* @return String The hashed name.
161+
*/
162+
public String hashWithAlgorithm(String name, String algorithm)
163+
throws NoSuchAlgorithmException, UnsupportedEncodingException {
164+
final MessageDigest md = MessageDigest.getInstance(algorithm);
165+
md.reset();
166+
md.update( charset != null ? name.getBytes( charset ) : name.getBytes() );
167+
final BigInteger bigInt = new BigInteger( 1, md.digest() );
168+
// By converting to base 35 (full alphanumeric), we guarantee
169+
// that the length of the name will always be smaller than the 30
170+
// character identifier restriction enforced by a few dialects.
171+
return bigInt.toString( 35 );
172+
}
151173
}

hibernate-core/src/test/java/org/hibernate/orm/test/annotations/namingstrategy/charset/Iso88591CharsetNamingStrategyTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected String expectedUniqueKeyName() {
2323
return "UK38xspy14r49kkcmmyltias1j4"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
2424
}
2525
else {
26-
return "UK8k5luacfo75uusg9a3fb87wbeub3";
26+
return "UKq2jxex2hrvg4139p85npyj71g";
2727
}
2828
}
2929

@@ -33,7 +33,7 @@ protected String expectedForeignKeyName() {
3333
return "FKdvmx00nr88d03v6xhrjyujrq2"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
3434
}
3535
else {
36-
return "FKgkcc85inbuuppywsp0bxqfbqknno";
36+
return "FKdeqq4y6cesc2yfgi97u2hp61g";
3737
}
3838
}
3939

@@ -43,7 +43,7 @@ protected String expectedIndexName() {
4343
return "IDX38xspy14r49kkcmmyltias1j4"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
4444
}
4545
else {
46-
return "IDX8k5luacfo75uusg9a3fb87wbeub";
46+
return "IDXq2jxex2hrvg4139p85npyj71g";
4747
}
4848
}
4949
}

hibernate-core/src/test/java/org/hibernate/orm/test/annotations/namingstrategy/charset/Utf8CharsetNamingStrategyTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected String expectedUniqueKeyName() {
2323
return "UKinnacp0woeltj5l0k4vgabf8k"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
2424
}
2525
else {
26-
return "UK3punuckhqfc03ddypdrpeahs9cty";
26+
return "UKpm66tdjkgtsca5x2uwux487t5";
2727
}
2828
}
2929

@@ -33,7 +33,7 @@ protected String expectedForeignKeyName() {
3333
return "FKe1lr9dd16cmmon53r7m736yev"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
3434
}
3535
else {
36-
return "FK6av084md8iluhsdmw8hqv4ep8g98";
36+
return "FKgvrnki5fwp3qo0hfp1bu1jj0q";
3737
}
3838
}
3939

@@ -43,7 +43,7 @@ protected String expectedIndexName() {
4343
return "IDXinnacp0woeltj5l0k4vgabf8k"; // Non-ASCII, non-alphanumeric identifiers are quoted on HANA
4444
}
4545
else {
46-
return "IDX3punuckhqfc03ddypdrpeahs9ct";
46+
return "IDXpm66tdjkgtsca5x2uwux487t5";
4747
}
4848
}
4949
}

hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/naming/NamingHelperTest.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@
99
import org.junit.jupiter.params.ParameterizedTest;
1010
import org.junit.jupiter.params.provider.Arguments;
1111
import org.junit.jupiter.params.provider.MethodSource;
12+
import org.junit.jupiter.api.Test;
1213

1314
import java.nio.charset.StandardCharsets;
15+
import java.io.UnsupportedEncodingException;
16+
import java.security.NoSuchAlgorithmException;
1417
import java.util.List;
1518
import java.util.stream.Collectors;
1619
import java.util.stream.Stream;
1720

1821
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.junit.jupiter.api.Assertions.*;
1923

2024
class NamingHelperTest {
2125

@@ -57,34 +61,79 @@ void smoke(String charset, String prefix, String tableName, String referencedTab
5761
.isEqualTo( expectedConstraintName );
5862
}
5963

64+
@Test
65+
void testHashWithAlgorithm_md5_utf8() throws Exception {
66+
NamingHelper helper = NamingHelper.withCharset(StandardCharsets.UTF_8.name());
67+
String hash = helper.hashWithAlgorithm("table_name", "MD5");
68+
assertNotNull(hash);
69+
assertFalse(hash.isEmpty());
70+
// MD5 hash of "table_name" in base 35 should be deterministic
71+
assertEquals("8q6ok4ne4ufel54crtitkq7ir", hash);
72+
}
73+
74+
@Test
75+
void testHashWithAlgorithm_sha256_utf8() throws Exception {
76+
NamingHelper helper = NamingHelper.withCharset(StandardCharsets.UTF_8.name());
77+
String hash = helper.hashWithAlgorithm("table_name", "SHA-256");
78+
assertNotNull(hash);
79+
assertFalse(hash.isEmpty());
80+
// SHA-256 hash of "table_name" in base 35 should be deterministic
81+
assertEquals("nie2bx5e7mderevrnl4gkuhtmy45nwfvst7dv6cx3pb3yy9ul1", hash);
82+
}
83+
84+
@Test
85+
void testHashWithAlgorithm_md5_iso88591() throws Exception {
86+
NamingHelper helper = NamingHelper.withCharset(StandardCharsets.ISO_8859_1.name());
87+
String hash = helper.hashWithAlgorithm("café", "MD5");
88+
assertNotNull(hash);
89+
assertFalse(hash.isEmpty());
90+
assertEquals("hgll69c0qdhsjikniholqfcj4", hash);
91+
}
92+
93+
@Test
94+
void testHashWithAlgorithm_invalidAlgorithm() {
95+
NamingHelper helper = NamingHelper.withCharset(StandardCharsets.UTF_8.name());
96+
assertThrows(NoSuchAlgorithmException.class, () -> {
97+
helper.hashWithAlgorithm("table_name", "NOPE");
98+
});
99+
}
100+
101+
@Test
102+
void testHashWithAlgorithm_invalidCharset() {
103+
NamingHelper helper = NamingHelper.withCharset("NOPE-CHARSET");
104+
assertThrows(UnsupportedEncodingException.class, () -> {
105+
helper.hashWithAlgorithm("table_name", "MD5");
106+
});
107+
}
108+
60109
private static Stream<Arguments> args() {
61110
// String charset, String prefix, String tableName, String referencedTableName,
62111
// List<String> columnNames, String expectedFkName, String expectedConstraintName
63112
return Stream.of(
64113
Arguments.of(
65114
StandardCharsets.UTF_8.name(),
66115
"fk_", "table_name", "other_table_name", List.of( "col1", "col2", "col3" ),
67-
"fk_ka01ji8vk10osbgp6ve604cm1kfsso7byjr6s294jaukhv3ajq", "fk_kv2dq7fp00eyv1vq5yc29rvc3yftq2fmyg9iacv99wrn3nn0l6" ),
116+
"fk_f4u43ook9b825fxbm3exb18q6", "fk_1o8k3sa4q2a2wb596v4htt8qf" ),
68117
Arguments.of(
69118
StandardCharsets.ISO_8859_1.name(),
70119
"fk_", "table_name", "other_table_name", List.of( "col1", "col2", "col3" ),
71-
"fk_ka01ji8vk10osbgp6ve604cm1kfsso7byjr6s294jaukhv3ajq", "fk_kv2dq7fp00eyv1vq5yc29rvc3yftq2fmyg9iacv99wrn3nn0l6" ),
120+
"fk_f4u43ook9b825fxbm3exb18q6", "fk_1o8k3sa4q2a2wb596v4htt8qf" ),
72121
Arguments.of(
73122
StandardCharsets.UTF_8.name(),
74123
"fk_", "café", "le_déjeuner", List.of( "col1", "col2", "col3" ),
75-
"fk_ih8sokb1hh74aiucascp5pv0dlecescli8httwu7ca8ggbvxx4", "fk_1mlbg6hqesxj797eo16lo82hd491j5ag67833h3i1k7q99wo9b" ),
124+
"fk_jdvsrk14lxab6a829ok160vyj", "fk_h34kugb2bguwmcn1g5h1q3snf" ),
76125
Arguments.of(
77126
StandardCharsets.ISO_8859_1.name(),
78127
"fk_", "café", "le_déjeuner", List.of( "col1", "col2", "col3" ),
79-
"fk_i2tnixfnx9ylanksjrn8u41wvg5cdgl8wr7264olc17srxpa95", "fk_h8iedvm0im7uuapsek0b5wsc5goahu7wvjgtc3a5snqi79outg" ),
128+
"fk_g1py0mkjd1tu46tr8c2e1vm2l", "fk_1pitt5gtytwpy6ea02o7l5men" ),
80129
Arguments.of(
81130
StandardCharsets.UTF_8.name(),
82131
"fk_", "abcdefghijklmnopqrstuvwxyzäöüß", "stuvwxyzäöüß", List.of( "col1" ),
83-
"fk_eh9134y0qw0bck215ws5kixdw8v41w26nuq76p5p6vdheyvbpk", "fk_megnb1o6em9hrlel3dvyomlmo41my964kfvdudonbumofve1jx" ),
132+
"fk_q11mlivmrc3sdfnncd2hwkpqp", "fk_gm8xsqu7ayucv5w5w2gj2dfly" ),
84133
Arguments.of(
85134
StandardCharsets.ISO_8859_1.name(),
86135
"fk_", "abcdefghijklmnopqrstuvwxyzäöüß", "stuvwxyzäöüß", List.of( "col1" )
87-
, "fk_t8yjwdnsr4el6guwpgnxtlsvcgodr9rtaod8uor849w36552h", "fk_x5u4f3i64gnbca1jxu03q2968mn4b66bb6lbqbtf5apo6ux13" )
136+
, "fk_fua9hgc6dn6eno8hlqt58j72o", "fk_3iig3yrgsf5bjlbdo05d7mp2" )
88137
);
89138
}
90139

hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
public class ConstraintTest extends BaseNonConfigCoreFunctionalTestCase {
3333

34-
private static final int MAX_NAME_LENGTH = 170;
34+
private static final int MAX_NAME_LENGTH = 30;
3535

3636
private static final String EXPLICIT_FK_NAME_NATIVE = "fk_explicit_native";
3737

0 commit comments

Comments
 (0)