Skip to content

Commit e4e3573

Browse files
committed
HHH-13694 fix numeric overflow exception for large sequence min values
1 parent 6d21f7e commit e4e3573

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.tool.schema.extract.internal;
88

9+
import java.math.BigDecimal;
910
import java.sql.ResultSet;
1011
import java.sql.SQLException;
1112

@@ -18,6 +19,9 @@ public class SequenceInformationExtractorOracleDatabaseImpl extends SequenceInfo
1819
*/
1920
public static final SequenceInformationExtractorOracleDatabaseImpl INSTANCE = new SequenceInformationExtractorOracleDatabaseImpl();
2021

22+
private static final BigDecimal MIN_VALUE = BigDecimal.valueOf( Long.MIN_VALUE );
23+
private static final BigDecimal MAX_VALUE = BigDecimal.valueOf( Long.MAX_VALUE );
24+
2125
@Override
2226
protected String sequenceCatalogColumn() {
2327
return null;
@@ -38,9 +42,33 @@ protected String sequenceMinValueColumn() {
3842
return "min_value";
3943
}
4044

45+
@Override
46+
protected String sequenceMaxValueColumn() {
47+
return "max_value";
48+
}
49+
50+
@Override
51+
protected Long resultSetMinValue(ResultSet resultSet) throws SQLException {
52+
final BigDecimal asDecimal = resultSet.getBigDecimal( sequenceMinValueColumn() );
53+
54+
// BigDecimal.longValue() may return a result with the opposite sign
55+
if ( asDecimal.compareTo( MIN_VALUE ) == -1 ) {
56+
return Long.MIN_VALUE;
57+
}
58+
59+
return asDecimal.longValue();
60+
}
61+
4162
@Override
4263
protected Long resultSetMaxValue(ResultSet resultSet) throws SQLException {
43-
return resultSet.getBigDecimal( "max_value" ).longValue();
64+
final BigDecimal asDecimal = resultSet.getBigDecimal( sequenceMaxValueColumn() );
65+
66+
// BigDecimal.longValue() may return a result with the opposite sign
67+
if ( asDecimal.compareTo( MAX_VALUE ) == 1 ) {
68+
return Long.MAX_VALUE;
69+
}
70+
71+
return asDecimal.longValue();
4472
}
4573

4674
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.dialect.functional;
8+
9+
import java.math.BigDecimal;
10+
import java.sql.Connection;
11+
import java.sql.SQLException;
12+
import java.util.Optional;
13+
import java.util.stream.StreamSupport;
14+
15+
import org.hibernate.dialect.Oracle8iDialect;
16+
import org.hibernate.dialect.OracleDialect;
17+
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
18+
import org.hibernate.testing.RequiresDialect;
19+
import org.hibernate.testing.TestForIssue;
20+
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
21+
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl;
22+
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
23+
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
24+
import org.junit.After;
25+
import org.junit.Before;
26+
import org.junit.Test;
27+
28+
import static org.hibernate.testing.transaction.TransactionUtil.doInAutoCommit;
29+
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertTrue;
31+
32+
@RequiresDialect(value = { Oracle8iDialect.class })
33+
@TestForIssue(jiraKey = "HHH-13694")
34+
public class OracleDialectSequenceInformationTest extends BaseNonConfigCoreFunctionalTestCase {
35+
36+
private static final String MIN_SEQUENCE_NAME = "SEQ_MIN_TEST";
37+
private static final String MAX_SEQUENCE_NAME = "SEQ_MAX_TEST";
38+
private static final String MIN_VALUE = "-99999999999999999999999999";
39+
private static final String MAX_VALUE = "99999999999999999999999999";
40+
41+
@Before
42+
public void prepareTest() throws Exception {
43+
doInAutoCommit(
44+
"DROP SEQUENCE " + MIN_SEQUENCE_NAME,
45+
"CREATE SEQUENCE " + MIN_SEQUENCE_NAME + " MINVALUE " + MIN_VALUE + " MAXVALUE -1 INCREMENT BY -1",
46+
"DROP SEQUENCE " + MAX_SEQUENCE_NAME,
47+
"CREATE SEQUENCE " + MAX_SEQUENCE_NAME + " MINVALUE 0 MAXVALUE " + MAX_VALUE + " INCREMENT BY 1" );
48+
}
49+
50+
@After
51+
public void cleanupTest() throws Exception {
52+
doInAutoCommit(
53+
"DROP SEQUENCE " + MIN_SEQUENCE_NAME,
54+
"DROP SEQUENCE " + MAX_SEQUENCE_NAME );
55+
}
56+
57+
@Test
58+
public void testExtractSequenceWithMinValueLowerThanLongMinValue() throws SQLException {
59+
SequenceInformation sequence = fetchSequenceInformation( MIN_SEQUENCE_NAME );
60+
61+
assertEquals( -1L, sequence.getIncrementValue().longValue() );
62+
assertEquals( Long.MIN_VALUE, sequence.getMinValue().longValue() );
63+
}
64+
65+
@Test
66+
public void testExtractSequenceWithMaxValueGreaterThanLongMaxValue() throws SQLException {
67+
SequenceInformation sequence = fetchSequenceInformation( MAX_SEQUENCE_NAME );
68+
69+
assertEquals( 1L, sequence.getIncrementValue().longValue() );
70+
assertEquals( Long.MAX_VALUE, sequence.getMaxValue().longValue() );
71+
}
72+
73+
private SequenceInformation fetchSequenceInformation(String sequenceName) throws SQLException {
74+
try ( Connection connection = sessionFactory().getJdbcServices()
75+
.getBootstrapJdbcConnectionAccess()
76+
.obtainConnection() ) {
77+
JdbcEnvironment jdbcEnvironment = sessionFactory().getJdbcServices().getJdbcEnvironment();
78+
SequenceInformationExtractorOracleDatabaseImpl sequenceExtractor = SequenceInformationExtractorOracleDatabaseImpl.INSTANCE;
79+
Iterable<SequenceInformation> sequenceInformations = sequenceExtractor.extractMetadata(
80+
new ExtractionContext.EmptyExtractionContext() {
81+
82+
@Override
83+
public Connection getJdbcConnection() {
84+
return connection;
85+
}
86+
87+
@Override
88+
public JdbcEnvironment getJdbcEnvironment() {
89+
return jdbcEnvironment;
90+
}
91+
} );
92+
93+
// lets skip system sequences
94+
Optional<SequenceInformation> foundSequence = StreamSupport.stream( sequenceInformations.spliterator(), false )
95+
.filter( sequence -> sequenceName.equals( sequence.getSequenceName().getSequenceName().getText().toUpperCase() ) )
96+
.findFirst();
97+
98+
assertTrue( sequenceName + " not found", foundSequence.isPresent() );
99+
100+
return foundSequence.get();
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)