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 committed Apr 15, 2020
1 parent 0b4bcce commit bd1444a
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 55 deletions.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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();
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,14 @@ public String getCreateIdTableCommand() {
}
}, AfterUseAction.CLEAN );
}

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

@Override
protected Boolean useUnicodeStringTypesDefault() {
return Boolean.FALSE;
}
}
Original file line number Diff line number Diff line change
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 bd1444a

Please sign in to comment.