Skip to content

Commit 36f8fb5

Browse files
committed
HHH-19710 Add vector support for SAP HANA Cloud
1 parent af843e0 commit 36f8fb5

File tree

12 files changed

+601
-19
lines changed

12 files changed

+601
-19
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ private LockingSupport buildLockingSupport() {
216216

217217
@Override
218218
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
219-
return HANALegacyServerConfiguration.staticDetermineDatabaseVersion( info );
219+
return HANALegacyServerConfiguration.determineDatabaseVersion( info );
220220
}
221221

222222
// Use column or row tables by default

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

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
import java.sql.ResultSet;
99
import java.sql.SQLException;
1010
import java.sql.Statement;
11+
import java.time.LocalDate;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
1114

1215
import org.hibernate.dialect.DatabaseVersion;
16+
import org.hibernate.dialect.SimpleDatabaseVersion;
1317
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
1418
import org.hibernate.internal.CoreLogging;
1519
import org.hibernate.internal.CoreMessageLogger;
@@ -24,6 +28,7 @@
2428
public class HANALegacyServerConfiguration {
2529

2630
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HANALegacyServerConfiguration.class );
31+
private static final Pattern CLOUD_VERSION_PATTERN = Pattern.compile( "\\(fa/CE(\\d+)\\.(\\d+)\\)" );
2732
public static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
2833

2934
private final DatabaseVersion fullVersion;
@@ -49,22 +54,39 @@ public int getMaxLobPrefetchSize() {
4954
public static HANALegacyServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
5055
Integer maxLobPrefetchSize = null;
5156
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
57+
DatabaseVersion databaseVersion = null;
5258
if ( databaseMetaData != null ) {
53-
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
54-
try ( ResultSet rs = statement.executeQuery(
55-
"SELECT TOP 1 VALUE,MAP(LAYER_NAME,'DEFAULT',1,'SYSTEM',2,'DATABASE',3,4) AS LAYER FROM SYS.M_INIFILE_CONTENTS WHERE FILE_NAME='indexserver.ini' AND SECTION='session' AND KEY='max_lob_prefetch_size' ORDER BY LAYER DESC" ) ) {
56-
// This only works if the current user has the privilege INIFILE ADMIN
57-
if ( rs.next() ) {
58-
maxLobPrefetchSize = rs.getInt( 1 );
59-
}
60-
}
59+
int databaseMajorVersion = -1;
60+
try {
61+
databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion();
6162
}
6263
catch (SQLException e) {
6364
// Ignore
6465
LOG.debug(
65-
"An error occurred while trying to determine the value of the HANA parameter indexserver.ini / session / max_lob_prefetch_size.",
66+
"An error occurred while trying to determine the database version.",
6667
e );
6768
}
69+
70+
if (databaseMajorVersion > 0 && databaseMajorVersion < 4) {
71+
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
72+
try ( ResultSet rs = statement.executeQuery(
73+
"SELECT TOP 1 VALUE,MAP(LAYER_NAME,'DEFAULT',1,'SYSTEM',2,'DATABASE',3,4) AS LAYER FROM SYS.M_CONFIGURATION_PARAMETER_VALUES WHERE FILE_NAME='indexserver.ini' AND SECTION='session' AND KEY='max_lob_prefetch_size' ORDER BY LAYER DESC" ) ) {
74+
// This only works if the current user has the privilege INIFILE ADMIN
75+
if ( rs.next() ) {
76+
maxLobPrefetchSize = rs.getInt( 1 );
77+
}
78+
}
79+
}
80+
catch (SQLException e) {
81+
// Ignore
82+
LOG.debug(
83+
"An error occurred while trying to determine the value of the HANA parameter indexserver.ini / session / max_lob_prefetch_size.",
84+
e );
85+
}
86+
}
87+
else {
88+
databaseVersion = determineDatabaseVersion( info );
89+
}
6890
}
6991
// default to the dialect-specific configuration settings
7092
if ( maxLobPrefetchSize == null ) {
@@ -74,12 +96,78 @@ public static HANALegacyServerConfiguration fromDialectResolutionInfo(DialectRes
7496
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
7597
);
7698
}
77-
return new HANALegacyServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
99+
if ( databaseVersion == null ) {
100+
databaseVersion = staticDetermineDatabaseVersion( info );
101+
}
102+
return new HANALegacyServerConfiguration( databaseVersion, maxLobPrefetchSize );
103+
}
104+
105+
public static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
106+
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
107+
String databaseVersion = null;
108+
if ( databaseMetaData != null ) {
109+
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
110+
try (ResultSet rs = statement.executeQuery(
111+
"SELECT VALUE FROM M_SYSTEM_OVERVIEW WHERE NAME='Version'" )) {
112+
// This only works if the current user has the privilege INIFILE ADMIN
113+
if ( rs.next() ) {
114+
databaseVersion = rs.getString( 1 );
115+
}
116+
}
117+
}
118+
catch (SQLException e) {
119+
// Ignore
120+
LOG.debug( "An error occurred while trying to determine the HANA Cloud version.", e );
121+
}
122+
}
123+
return databaseVersion == null
124+
? staticDetermineDatabaseVersion( info )
125+
: determineDatabaseVersion( databaseVersion );
126+
}
127+
128+
public static DatabaseVersion determineDatabaseVersion(String versionString) {
129+
if ( versionString == null ) {
130+
return HANALegacyDialect.DEFAULT_VERSION;
131+
}
132+
final String[] components = StringHelper.split( " ", versionString );
133+
final DatabaseVersion databaseVersion = staticDetermineDatabaseVersion( components[0] );
134+
if ( components.length == 1 || databaseVersion.isBefore( 4 ) ) {
135+
return databaseVersion;
136+
}
137+
else {
138+
// Parse the HANA Cloud version
139+
final Matcher matcher = CLOUD_VERSION_PATTERN.matcher( components[1] );
140+
if ( matcher.matches() ) {
141+
final int year = Integer.parseInt( matcher.group( 1 ) );
142+
final int week = Integer.parseInt( matcher.group( 2 ) );
143+
return new SimpleDatabaseVersion(
144+
databaseVersion.getDatabaseMajorVersion(),
145+
getHanaCloudVersion( LocalDate.of( year, 1, 1 ).plusWeeks( week ) ),
146+
databaseVersion.getDatabaseMicroVersion()
147+
);
148+
}
149+
else {
150+
return databaseVersion;
151+
}
152+
}
153+
}
154+
155+
private static int getHanaCloudVersion(LocalDate date) {
156+
final int quarter = switch (date.getMonth()) {
157+
case JANUARY, FEBRUARY, MARCH -> 1;
158+
case APRIL, MAY, JUNE -> 2;
159+
case JULY, AUGUST, SEPTEMBER -> 3;
160+
case OCTOBER, NOVEMBER, DECEMBER -> 4;
161+
};
162+
return date.getYear() * 10 + quarter;
163+
}
164+
165+
public static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
166+
return staticDetermineDatabaseVersion( info.getDatabaseVersion() );
78167
}
79168

80-
static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
169+
public static DatabaseVersion staticDetermineDatabaseVersion(String versionString) {
81170
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
82-
final String versionString = info.getDatabaseVersion();
83171
int majorVersion = 1;
84172
int minorVersion = 0;
85173
int patchLevel = 0;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ protected DatabaseVersion getMinimumSupportedVersion() {
217217

218218
@Override
219219
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
220-
return HANAServerConfiguration.staticDetermineDatabaseVersion( info );
220+
return HANAServerConfiguration.determineDatabaseVersion( info );
221221
}
222222

223223
// Use column or row tables by default

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

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import java.sql.ResultSet;
99
import java.sql.SQLException;
1010
import java.sql.Statement;
11+
import java.time.LocalDate;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
1114

1215
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
1316
import org.hibernate.internal.CoreLogging;
@@ -23,6 +26,7 @@
2326
public class HANAServerConfiguration {
2427

2528
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HANAServerConfiguration.class );
29+
private static final Pattern CLOUD_VERSION_PATTERN = Pattern.compile( "\\(fa/CE(\\d+)\\.(\\d+)\\)" );
2630
public static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
2731

2832
private final DatabaseVersion fullVersion;
@@ -48,6 +52,7 @@ public int getMaxLobPrefetchSize() {
4852
public static HANAServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
4953
Integer maxLobPrefetchSize = null;
5054
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
55+
DatabaseVersion databaseVersion = null;
5156
if ( databaseMetaData != null ) {
5257
int databaseMajorVersion = -1;
5358
try {
@@ -77,6 +82,9 @@ public static HANAServerConfiguration fromDialectResolutionInfo(DialectResolutio
7782
e );
7883
}
7984
}
85+
else {
86+
databaseVersion = determineDatabaseVersion( info );
87+
}
8088
}
8189
// default to the dialect-specific configuration settings
8290
if ( maxLobPrefetchSize == null ) {
@@ -86,12 +94,78 @@ public static HANAServerConfiguration fromDialectResolutionInfo(DialectResolutio
8694
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
8795
);
8896
}
89-
return new HANAServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
97+
if ( databaseVersion == null ) {
98+
databaseVersion = staticDetermineDatabaseVersion( info );
99+
}
100+
return new HANAServerConfiguration( databaseVersion, maxLobPrefetchSize );
101+
}
102+
103+
public static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
104+
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
105+
String databaseVersion = null;
106+
if ( databaseMetaData != null ) {
107+
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
108+
try (ResultSet rs = statement.executeQuery(
109+
"SELECT VALUE FROM M_SYSTEM_OVERVIEW WHERE NAME='Version'" )) {
110+
// This only works if the current user has the privilege INIFILE ADMIN
111+
if ( rs.next() ) {
112+
databaseVersion = rs.getString( 1 );
113+
}
114+
}
115+
}
116+
catch (SQLException e) {
117+
// Ignore
118+
LOG.debug( "An error occurred while trying to determine the HANA Cloud version.", e );
119+
}
120+
}
121+
return databaseVersion == null
122+
? staticDetermineDatabaseVersion( info )
123+
: determineDatabaseVersion( databaseVersion );
124+
}
125+
126+
public static DatabaseVersion determineDatabaseVersion(String versionString) {
127+
if ( versionString == null ) {
128+
return HANADialect.MINIMUM_VERSION;
129+
}
130+
final String[] components = StringHelper.split( " ", versionString );
131+
final DatabaseVersion databaseVersion = staticDetermineDatabaseVersion( components[0] );
132+
if ( components.length == 1 || databaseVersion.isBefore( 4 ) ) {
133+
return databaseVersion;
134+
}
135+
else {
136+
// Parse the HANA Cloud version
137+
final Matcher matcher = CLOUD_VERSION_PATTERN.matcher( components[1] );
138+
if ( matcher.matches() ) {
139+
final int year = Integer.parseInt( matcher.group( 1 ) );
140+
final int week = Integer.parseInt( matcher.group( 2 ) );
141+
return new SimpleDatabaseVersion(
142+
databaseVersion.getDatabaseMajorVersion(),
143+
getHanaCloudVersion( LocalDate.of( year, 1, 1 ).plusWeeks( week ) ),
144+
databaseVersion.getDatabaseMicroVersion()
145+
);
146+
}
147+
else {
148+
return databaseVersion;
149+
}
150+
}
151+
}
152+
153+
private static int getHanaCloudVersion(LocalDate date) {
154+
final int quarter = switch (date.getMonth()) {
155+
case JANUARY, FEBRUARY, MARCH -> 1;
156+
case APRIL, MAY, JUNE -> 2;
157+
case JULY, AUGUST, SEPTEMBER -> 3;
158+
case OCTOBER, NOVEMBER, DECEMBER -> 4;
159+
};
160+
return date.getYear() * 10 + quarter;
161+
}
162+
163+
public static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
164+
return staticDetermineDatabaseVersion( info.getDatabaseVersion() );
90165
}
91166

92-
static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
167+
public static DatabaseVersion staticDetermineDatabaseVersion(String versionString) {
93168
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
94-
final String versionString = info.getDatabaseVersion();
95169
int majorVersion = 1;
96170
int minorVersion = 0;
97171
int patchLevel = 0;

hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
8181
}
8282
}
8383

84-
private static JavaType<?> elementJavaType(JavaType<?> javaTypeDescriptor) {
84+
protected static JavaType<?> elementJavaType(JavaType<?> javaTypeDescriptor) {
8585
if ( javaTypeDescriptor instanceof ByteArrayJavaType ) {
8686
// Special handling needed for Byte[], because that would conflict with the VARBINARY mapping
8787
return ByteJavaType.INSTANCE;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.dialect.unit;
6+
7+
import org.hibernate.dialect.DatabaseVersion;
8+
import org.hibernate.dialect.HANADialect;
9+
import org.hibernate.dialect.HANAServerConfiguration;
10+
import org.hibernate.testing.orm.junit.RequiresDialect;
11+
import org.junit.jupiter.api.Test;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertNotNull;
15+
16+
@RequiresDialect(HANADialect.class)
17+
public class HANADialectVersionTest {
18+
19+
@Test
20+
public void testStaticVersion() {
21+
// HANA database 2.0 SPS 07
22+
DatabaseVersion dv = HANAServerConfiguration.staticDetermineDatabaseVersion( "2.00.076.00.1705400033" );
23+
assertEquals( 2, dv.getMajor() );
24+
assertEquals( 0, dv.getMinor() );
25+
assertEquals( 76, dv.getMicro() );
26+
27+
// HANA Cloud version QRC 3/2024
28+
dv = HANAServerConfiguration.staticDetermineDatabaseVersion( "4.00.000.00.1730808477" );
29+
assertNotNull( dv );
30+
assertEquals( 4, dv.getMajor() );
31+
assertEquals( 0, dv.getMinor() );
32+
assertEquals( 0, dv.getMicro() );
33+
34+
// HANA Cloud version QRC 2/2025
35+
dv = HANAServerConfiguration.staticDetermineDatabaseVersion( "4.00.000.00.1755603748" );
36+
assertNotNull( dv );
37+
assertEquals( 4, dv.getMajor() );
38+
assertEquals( 0, dv.getMinor() );
39+
assertEquals( 0, dv.getMicro() );
40+
}
41+
42+
@Test
43+
public void testDynamicVersion() {
44+
// HANA database 2.0 SPS 07
45+
DatabaseVersion dv = HANAServerConfiguration.determineDatabaseVersion( "2.00.076.00.1705400033 (fa/hana2sp07)" );
46+
assertEquals( 2, dv.getMajor() );
47+
assertEquals( 0, dv.getMinor() );
48+
assertEquals( 76, dv.getMicro() );
49+
50+
// HANA Cloud version QRC 3/2024
51+
dv = HANAServerConfiguration.determineDatabaseVersion( "4.00.000.00.1730808477 (fa/CE2024.42)" );
52+
assertNotNull( dv );
53+
assertEquals( 4, dv.getMajor() );
54+
assertEquals( 2024_4, dv.getMinor() );
55+
assertEquals( 0, dv.getMicro() );
56+
57+
// HANA Cloud version QRC 2/2025
58+
dv = HANAServerConfiguration.determineDatabaseVersion( "4.00.000.00.1755603748 (fa/CE2025.14)" );
59+
assertNotNull( dv );
60+
assertEquals( 4, dv.getMajor() );
61+
assertEquals( 2025_2, dv.getMinor() );
62+
assertEquals( 0, dv.getMicro() );
63+
}
64+
}

0 commit comments

Comments
 (0)