Skip to content

Commit

Permalink
HHH-13917: Add support for HANA Cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
breglerj authored and Sanne committed Apr 17, 2020
1 parent e6e72fa commit db5b2ca
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 55 deletions.
Expand Up @@ -236,10 +236,10 @@ public void setDescription(String description) {
"select " +
" a.id as id, " +
" concat(concat(c.first_name, ' '), c.last_name) as clientName, " +
" sum(at.cents) as balance " +
" sum(atr.cents) as balance " +
"from account a " +
"join client c on c.id = a.client_id " +
"join account_transaction at on a.id = at.account_id " +
"join account_transaction atr on a.id = atr.account_id " +
"group by a.id, concat(concat(c.first_name, ' '), c.last_name)"
)
@Synchronize( {"client", "account", "account_transaction"} )
Expand Down
7 changes: 7 additions & 0 deletions gradle/databases.gradle
Expand Up @@ -107,6 +107,13 @@ ext {
'jdbc.pass' : 'H1bernate_test',
'jdbc.url' : 'jdbc:sap://localhost:30015/'
],
hana_cloud : [
'db.dialect' : 'org.hibernate.dialect.HANACloudColumnStoreDialect',
'jdbc.driver': 'com.sap.db.jdbc.Driver',
'jdbc.user' : 'HIBERNATE_TEST',
'jdbc.pass' : 'H1bernate_test',
'jdbc.url' : 'jdbc:sap://localhost:443/?encrypt=true&validateCertificate=false'
],
hana_vlad : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
'jdbc.driver': 'com.sap.db.jdbc.Driver',
Expand Down
Expand Up @@ -709,18 +709,17 @@ public int getMaxLobPrefetchSize() {

private static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
private static final Boolean USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE = Boolean.FALSE;
private static final Boolean USE_UNICODE_STRING_TYPES_DEFAULT_VALUE = Boolean.FALSE;
private static final Boolean TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE = Boolean.FALSE;

private HANANClobTypeDescriptor nClobTypeDescriptor = new HANANClobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );

private HANABlobTypeDescriptor blobTypeDescriptor = new HANABlobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );

private HANAClobTypeDescriptor clobTypeDescriptor = new HANAClobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE,
USE_UNICODE_STRING_TYPES_DEFAULT_VALUE.booleanValue() );
private HANAClobTypeDescriptor clobTypeDescriptor;

private boolean useLegacyBooleanType = USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE.booleanValue();
private boolean useUnicodeStringTypes = USE_UNICODE_STRING_TYPES_DEFAULT_VALUE.booleanValue();
private boolean useUnicodeStringTypes;

private boolean treatDoubleTypedFieldsAsDecimal = TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE.booleanValue();

/*
Expand Down Expand Up @@ -766,6 +765,10 @@ private String[] quoteTypeIfNecessary(org.hibernate.mapping.Table table, String[
public AbstractHANADialect() {
super();

this.useUnicodeStringTypes = useUnicodeStringTypesDefault().booleanValue();
this.clobTypeDescriptor = new HANAClobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE,
useUnicodeStringTypesDefault().booleanValue() );

registerColumnType( Types.DECIMAL, "decimal($p, $s)" );
registerColumnType( Types.NUMERIC, "decimal($p, $s)" );
registerColumnType( Types.DOUBLE, "double" );
Expand Down Expand Up @@ -804,7 +807,7 @@ public AbstractHANADialect() {
registerHibernateType( Types.NCLOB, StandardBasicTypes.MATERIALIZED_NCLOB.getName() );
registerHibernateType( Types.CLOB, StandardBasicTypes.MATERIALIZED_CLOB.getName() );
registerHibernateType( Types.BLOB, StandardBasicTypes.MATERIALIZED_BLOB.getName() );
registerHibernateType( Types.NVARCHAR, StandardBasicTypes.STRING.getName() );
registerHibernateType( Types.NVARCHAR, StandardBasicTypes.NSTRING.getName() );

registerFunction( "to_date", new StandardSQLFunction( "to_date", StandardBasicTypes.DATE ) );
registerFunction( "to_seconddate", new StandardSQLFunction( "to_seconddate", StandardBasicTypes.TIMESTAMP ) );
Expand Down Expand Up @@ -1143,9 +1146,9 @@ protected SqlTypeDescriptor getSqlTypeDescriptorOverride(final int sqlCode) {
case Types.BOOLEAN:
return this.useLegacyBooleanType ? BitTypeDescriptor.INSTANCE : BooleanTypeDescriptor.INSTANCE;
case Types.VARCHAR:
return this.useUnicodeStringTypes ? NVarcharTypeDescriptor.INSTANCE : VarcharTypeDescriptor.INSTANCE;
return this.isUseUnicodeStringTypes() ? NVarcharTypeDescriptor.INSTANCE : VarcharTypeDescriptor.INSTANCE;
case Types.CHAR:
return this.useUnicodeStringTypes ? NCharTypeDescriptor.INSTANCE : CharTypeDescriptor.INSTANCE;
return this.isUseUnicodeStringTypes() ? NCharTypeDescriptor.INSTANCE : CharTypeDescriptor.INSTANCE;
case Types.DOUBLE:
return this.treatDoubleTypedFieldsAsDecimal ? DecimalTypeDescriptor.INSTANCE : DoubleTypeDescriptor.INSTANCE;
default:
Expand Down Expand Up @@ -1572,23 +1575,25 @@ public Integer convert(Object value) {
this.blobTypeDescriptor = new HANABlobTypeDescriptor( maxLobPrefetchSize );
}

this.useUnicodeStringTypes = configurationService.getSetting( USE_UNICODE_STRING_TYPES_PARAMETER_NAME, StandardConverters.BOOLEAN,
USE_UNICODE_STRING_TYPES_DEFAULT_VALUE ).booleanValue();
if ( supportsAsciiStringTypes() ) {
this.useUnicodeStringTypes = configurationService.getSetting( USE_UNICODE_STRING_TYPES_PARAMETER_NAME, StandardConverters.BOOLEAN,
useUnicodeStringTypesDefault() ).booleanValue();

if ( this.useUnicodeStringTypes ) {
registerColumnType( Types.CHAR, "nvarchar(1)" );
registerColumnType( Types.VARCHAR, 5000, "nvarchar($l)" );
registerColumnType( Types.LONGVARCHAR, 5000, "nvarchar($l)" );
if ( this.isUseUnicodeStringTypes() ) {
registerColumnType( Types.CHAR, "nvarchar(1)" );
registerColumnType( Types.VARCHAR, 5000, "nvarchar($l)" );
registerColumnType( Types.LONGVARCHAR, 5000, "nvarchar($l)" );

// for longer values map to clob/nclob
registerColumnType( Types.LONGVARCHAR, "nclob" );
registerColumnType( Types.VARCHAR, "nclob" );
registerColumnType( Types.CLOB, "nclob" );
}
// for longer values map to clob/nclob
registerColumnType( Types.LONGVARCHAR, "nclob" );
registerColumnType( Types.VARCHAR, "nclob" );
registerColumnType( Types.CLOB, "nclob" );
}

if ( this.clobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize
|| this.clobTypeDescriptor.isUseUnicodeStringTypes() != this.useUnicodeStringTypes ) {
this.clobTypeDescriptor = new HANAClobTypeDescriptor( maxLobPrefetchSize, this.useUnicodeStringTypes );
if ( this.clobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize
|| this.clobTypeDescriptor.isUseUnicodeStringTypes() != this.isUseUnicodeStringTypes() ) {
this.clobTypeDescriptor = new HANAClobTypeDescriptor( maxLobPrefetchSize, this.isUseUnicodeStringTypes() );
}
}

this.useLegacyBooleanType = configurationService.getSetting( USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME, StandardConverters.BOOLEAN,
Expand Down Expand Up @@ -1660,7 +1665,16 @@ public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaDa
return false;
}

@Override
public boolean supportsNoColumnsInsert() {
return false;
}

public boolean isUseUnicodeStringTypes() {
return this.useUnicodeStringTypes;
}

protected abstract boolean supportsAsciiStringTypes();

protected abstract Boolean useUnicodeStringTypesDefault();
}
Expand Up @@ -66,7 +66,6 @@ public Dialect resolveDialect(DialectResolutionInfo info) {
}
}


if ( databaseName.startsWith( "DB2/" ) ) {
return new DB2Dialect();
}
Expand Down Expand Up @@ -176,9 +175,13 @@ public Class<? extends Dialect> latestDialect() {
@Override
public Dialect resolveDialect(DialectResolutionInfo info) {
final String databaseName = info.getDatabaseName();
int databaseMajorVersion = info.getDatabaseMajorVersion();

if ( "HDB".equals( databaseName ) ) {
// SAP recommends defaulting to column store.
if ( databaseMajorVersion >= 4 ) {
return new HANACloudColumnStoreDialect();
}
return latestDialectInstance( this );
}

Expand Down Expand Up @@ -358,7 +361,7 @@ else if ( minorVersion < 7 ) {
return new MySQL57Dialect();
}
}
else if ( majorVersion < 8) {
else if ( majorVersion < 8 ) {
// There is no MySQL 6 or 7.
// Adding this just in case.
return new MySQL57Dialect();
Expand Down
@@ -0,0 +1,123 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect;

import java.sql.Types;

import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
import org.hibernate.hql.spi.id.local.AfterUseAction;
import org.hibernate.type.StandardBasicTypes;

/**
* An SQL dialect for the SAP HANA Cloud column store.
* <p>
* For more information on interacting with the SAP HANA Cloud database, refer to the
* <a href="https://help.sap.com/viewer/c1d3f60099654ecfb3fe36ac93c121bb/cloud/">SAP HANA Cloud SQL Reference Guide</a>
* and the <a href=
* "https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/latest/en-US/434e2962074540e18c802fd478de86d6.html">SAP
* HANA Client Interface Programming Reference</a>.
* <p>
* Column tables are created by this dialect when using the auto-ddl feature.
*
* @author <a href="mailto:jonathan.bregler@sap.com">Jonathan Bregler</a>
*/
public class HANACloudColumnStoreDialect extends AbstractHANADialect {

public HANACloudColumnStoreDialect() {
super();

registerColumnType( Types.CHAR, "nvarchar(1)" );
registerColumnType( Types.VARCHAR, 5000, "nvarchar($l)" );
registerColumnType( Types.LONGVARCHAR, 5000, "nvarchar($l)" );

// for longer values map to clob/nclob
registerColumnType( Types.LONGVARCHAR, "nclob" );
registerColumnType( Types.VARCHAR, "nclob" );
registerColumnType( Types.CLOB, "nclob" );

registerHibernateType( Types.CLOB, StandardBasicTypes.MATERIALIZED_NCLOB.getName() );
registerHibernateType( Types.NCHAR, StandardBasicTypes.NSTRING.getName() );
registerHibernateType( Types.CHAR, StandardBasicTypes.CHARACTER.getName() );
registerHibernateType( Types.CHAR, 1, StandardBasicTypes.CHARACTER.getName() );
registerHibernateType( Types.CHAR, 5000, StandardBasicTypes.NSTRING.getName() );
registerHibernateType( Types.VARCHAR, StandardBasicTypes.NSTRING.getName() );
registerHibernateType( Types.LONGVARCHAR, StandardBasicTypes.NTEXT.getName() );

// register additional keywords
registerHanaCloudKeywords();

// full-text search functions
registerFunction( "score", new StandardSQLFunction( "score", StandardBasicTypes.DOUBLE ) );
registerFunction( "contains", new VarArgsSQLFunction( StandardBasicTypes.BOOLEAN, "contains(", ",", ") /*" ) );
registerFunction( "contains_rhs", new SQLFunctionTemplate( StandardBasicTypes.BOOLEAN, "*/" ) );
registerFunction( "not_contains", new VarArgsSQLFunction( StandardBasicTypes.BOOLEAN, "not contains(", ",", ") /*" ) );
}

private void registerHanaCloudKeywords() {
registerKeyword( "array" );
registerKeyword( "at" );
registerKeyword( "authorization" );
registerKeyword( "between" );
registerKeyword( "by" );
registerKeyword( "collate" );
registerKeyword( "empty" );
registerKeyword( "filter" );
registerKeyword( "grouping" );
registerKeyword( "no" );
registerKeyword( "not" );
registerKeyword( "of" );
registerKeyword( "over" );
registerKeyword( "recursive" );
registerKeyword( "row" );
registerKeyword( "table" );
registerKeyword( "to" );
registerKeyword( "window" );
registerKeyword( "within" );
}

@Override
public String getCreateTableString() {
return "create column table";
}

@Override
public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() {
return new GlobalTemporaryTableBulkIdStrategy( new IdTableSupportStandardImpl() {

@Override
public String getCreateIdTableCommand() {
return "create global temporary column table";
}

@Override
public String getTruncateIdTableCommand() {
return "truncate table";
}

}, AfterUseAction.CLEAN );
}

@Override
protected boolean supportsAsciiStringTypes() {
return false;
}

@Override
protected Boolean useUnicodeStringTypesDefault() {
return Boolean.TRUE;
}

@Override
public boolean isUseUnicodeStringTypes() {
return true;
}
}
Expand Up @@ -30,7 +30,7 @@
* @author <a href="mailto:jonathan.bregler@sap.com">Jonathan Bregler</a>
*/
public class HANAColumnStoreDialect extends AbstractHANADialect {

public HANAColumnStoreDialect() {
super();

Expand Down Expand Up @@ -64,4 +64,14 @@ public String getTruncateIdTableCommand() {

}, AfterUseAction.CLEAN );
}

@Override
protected boolean supportsAsciiStringTypes() {
return true;
}

@Override
protected Boolean useUnicodeStringTypesDefault() {
return Boolean.FALSE;
}
}
Expand Up @@ -46,4 +46,14 @@ public String getCreateIdTableCommand() {
}
}, AfterUseAction.CLEAN );
}

@Override
protected boolean supportsAsciiStringTypes() {
return true;
}

@Override
protected Boolean useUnicodeStringTypesDefault() {
return Boolean.FALSE;
}
}
Expand Up @@ -29,6 +29,7 @@
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.HANACloudColumnStoreDialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.ast.tree.JavaConstantNode;
import org.hibernate.internal.util.ConfigHelper;
Expand Down Expand Up @@ -332,7 +333,12 @@ public void testEnumConverter() {
}
AbstractStandardBasicType basicType = assertTyping( AbstractStandardBasicType.class, type );
assertTyping( EnumJavaTypeDescriptor.class, basicType.getJavaTypeDescriptor() );
assertEquals( Types.VARCHAR, basicType.getSqlTypeDescriptor().getSqlType() );
if (metadata.getDatabase().getDialect() instanceof HANACloudColumnStoreDialect) {
assertEquals( Types.NVARCHAR, basicType.getSqlTypeDescriptor().getSqlType() );
}
else {
assertEquals( Types.VARCHAR, basicType.getSqlTypeDescriptor().getSqlType() );
}

// then lets build the SF and verify its use...
final SessionFactory sf = metadata.buildSessionFactory();
Expand Down

0 comments on commit db5b2ca

Please sign in to comment.