HHH-18643 - Remove support for SAP HANA versions older than 2.0 SPS 05, create a legacy HANA dialect in the community dialects module
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
f8e4e6e49f
commit
58ee919feb
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.community.dialect;
|
||||
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
|
||||
import static org.hibernate.cfg.DialectSpecificSettings.HANA_MAX_LOB_PREFETCH_SIZE;
|
||||
|
||||
/**
|
||||
* Utility class that extracts some initial configuration from the database for {@link HANALegacyDialect}.
|
||||
*/
|
||||
public class HANALegacyServerConfiguration {
|
||||
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HANALegacyServerConfiguration.class );
|
||||
public static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
|
||||
|
||||
private final DatabaseVersion fullVersion;
|
||||
private final int maxLobPrefetchSize;
|
||||
|
||||
public HANALegacyServerConfiguration(DatabaseVersion fullVersion) {
|
||||
this( fullVersion, MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
|
||||
}
|
||||
|
||||
public HANALegacyServerConfiguration(DatabaseVersion fullVersion, int maxLobPrefetchSize) {
|
||||
this.fullVersion = fullVersion;
|
||||
this.maxLobPrefetchSize = maxLobPrefetchSize;
|
||||
}
|
||||
|
||||
public DatabaseVersion getFullVersion() {
|
||||
return fullVersion;
|
||||
}
|
||||
|
||||
public int getMaxLobPrefetchSize() {
|
||||
return maxLobPrefetchSize;
|
||||
}
|
||||
|
||||
public static HANALegacyServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
|
||||
Integer maxLobPrefetchSize = null;
|
||||
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
|
||||
if ( databaseMetaData != null ) {
|
||||
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
|
||||
try ( ResultSet rs = statement.executeQuery(
|
||||
"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" ) ) {
|
||||
// This only works if the current user has the privilege INIFILE ADMIN
|
||||
if ( rs.next() ) {
|
||||
maxLobPrefetchSize = rs.getInt( 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
// Ignore
|
||||
LOG.debug(
|
||||
"An error occurred while trying to determine the value of the HANA parameter indexserver.ini / session / max_lob_prefetch_size.",
|
||||
e );
|
||||
}
|
||||
}
|
||||
// default to the dialect-specific configuration settings
|
||||
if ( maxLobPrefetchSize == null ) {
|
||||
maxLobPrefetchSize = ConfigurationHelper.getInt(
|
||||
HANA_MAX_LOB_PREFETCH_SIZE,
|
||||
info.getConfigurationValues(),
|
||||
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
|
||||
);
|
||||
}
|
||||
return new HANALegacyServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
|
||||
}
|
||||
|
||||
static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
|
||||
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
|
||||
final String versionString = info.getDatabaseVersion();
|
||||
int majorVersion = 1;
|
||||
int minorVersion = 0;
|
||||
int patchLevel = 0;
|
||||
if ( versionString == null ) {
|
||||
return HANALegacyDialect.DEFAULT_VERSION;
|
||||
}
|
||||
final String[] components = StringHelper.split( ".", versionString );
|
||||
if ( components.length >= 3 ) {
|
||||
try {
|
||||
majorVersion = Integer.parseInt( components[0] );
|
||||
minorVersion = Integer.parseInt( components[1] );
|
||||
patchLevel = Integer.parseInt( components[2] );
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.community.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
|
||||
/**
|
||||
* An SQL AST translator for the Legacy HANA dialect.
|
||||
*/
|
||||
public class HANALegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
|
||||
private boolean inLateral;
|
||||
|
||||
public HANALegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
|
||||
if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) {
|
||||
appendSql( "cast(" );
|
||||
visitArithmeticOperand( arithmeticExpression.getLeftHandOperand() );
|
||||
appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() );
|
||||
visitArithmeticOperand( arithmeticExpression.getRightHandOperand() );
|
||||
appendSql( " as int)" );
|
||||
}
|
||||
else {
|
||||
super.visitBinaryArithmeticExpression( arithmeticExpression );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitArithmeticOperand(Expression expression) {
|
||||
render( expression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
}
|
||||
|
||||
private boolean isHanaCloud() {
|
||||
return ( (HANALegacyDialect) getDialect() ).isCloud();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
|
||||
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
|
||||
// Render plain insert statement and possibly run into unique constraint violation
|
||||
super.visitInsertStatementOnly( statement );
|
||||
}
|
||||
else {
|
||||
visitInsertStatementEmulateMerge( statement );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitUpdateStatementOnly(UpdateStatement statement) {
|
||||
// HANA Cloud does not support the FROM clause in UPDATE statements
|
||||
if ( isHanaCloud() && hasNonTrivialFromClause( statement.getFromClause() ) ) {
|
||||
visitUpdateStatementEmulateMerge( statement );
|
||||
}
|
||||
else {
|
||||
super.visitUpdateStatementOnly( statement );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderUpdateClause(UpdateStatement updateStatement) {
|
||||
// HANA Cloud does not support the FROM clause in UPDATE statements
|
||||
if ( isHanaCloud() ) {
|
||||
super.renderUpdateClause( updateStatement );
|
||||
}
|
||||
else {
|
||||
appendSql( "update" );
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.UPDATE );
|
||||
renderTableReferenceIdentificationVariable( updateStatement.getTargetTable() );
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
|
||||
// HANA Cloud does not support the FROM clause in UPDATE statements
|
||||
if ( !isHanaCloud() ) {
|
||||
if ( statement.getFromClause().getRoots().isEmpty() ) {
|
||||
appendSql( " from " );
|
||||
renderDmlTargetTableExpression( statement.getTargetTable() );
|
||||
}
|
||||
else {
|
||||
visitFromClause( statement.getFromClause() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
|
||||
super.renderDmlTargetTableExpression( tableReference );
|
||||
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitConflictClause(ConflictClause conflictClause) {
|
||||
if ( conflictClause != null ) {
|
||||
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
|
||||
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||
// HANA only supports the LIMIT + OFFSET syntax but also window functions
|
||||
// Check if current query part is already row numbering to avoid infinite recursion
|
||||
return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart
|
||||
&& !isRowsOnlyFetchClauseType( queryPart );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsWithClauseInSubquery() {
|
||||
// HANA doesn't seem to support correlation, so we just report false here for simplicity
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCorrelated(CteStatement cteStatement) {
|
||||
// Report false here, because apparently HANA does not need the "lateral" keyword to correlate a from clause subquery in a subquery
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||
if ( shouldEmulateFetchClause( queryGroup ) ) {
|
||||
emulateFetchOffsetWithWindowFunctions( queryGroup, true );
|
||||
}
|
||||
else {
|
||||
super.visitQueryGroup( queryGroup );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQuerySpec(QuerySpec querySpec) {
|
||||
if ( shouldEmulateFetchClause( querySpec ) ) {
|
||||
emulateFetchOffsetWithWindowFunctions( querySpec, true );
|
||||
}
|
||||
else {
|
||||
super.visitQuerySpec( querySpec );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
|
||||
if ( tableReference.isLateral() && !inLateral ) {
|
||||
inLateral = true;
|
||||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||
inLateral = false;
|
||||
}
|
||||
else {
|
||||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
|
||||
// HANA does not support parameters in lateral subqueries for some reason, so inline all the parameters in this case
|
||||
return inLateral ? SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS : super.getParameterRenderingMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
||||
tableReference.getFunctionExpression().accept( this );
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||
renderLimitOffsetClause( queryPart );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
|
||||
// HANA does not support plain parameters in the select clause of the intersect emulation
|
||||
withParameterRenderingMode(
|
||||
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
||||
() -> renderComparisonEmulateIntersect( lhs, operator, rhs )
|
||||
);
|
||||
}
|
||||
else {
|
||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderPartitionItem(Expression expression) {
|
||||
if ( expression instanceof Literal ) {
|
||||
appendSql( "grouping sets (())" );
|
||||
}
|
||||
else if ( expression instanceof Summarization ) {
|
||||
throw new UnsupportedOperationException( "Summarization is not supported by DBMS" );
|
||||
}
|
||||
else {
|
||||
expression.accept( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorGtLtSyntax() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"The INSERT statement for table [%s] contains no column, and this is not supported by [%s]",
|
||||
tableInsert.getMutatingTable().getTableId(),
|
||||
getDialect()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValuesTableReference(ValuesTableReference tableReference) {
|
||||
emulateValuesTableReferenceColumnAliasing( tableReference );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSkipLocked() {
|
||||
return " ignore locked";
|
||||
}
|
||||
}
|
|
@ -168,7 +168,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM
|
|||
*/
|
||||
public class HANADialect extends Dialect {
|
||||
|
||||
static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 1, 0, 120 );
|
||||
static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 2, 0, 50 );
|
||||
|
||||
public HANADialect(DialectResolutionInfo info) {
|
||||
this( HANAServerConfiguration.fromDialectResolutionInfo( info ), true );
|
||||
|
@ -176,7 +176,7 @@ public class HANADialect extends Dialect {
|
|||
}
|
||||
|
||||
public HANADialect() {
|
||||
// SAP HANA 1.0 SPS12 R0 is the default
|
||||
// SAP HANA 2.0 SPS 05 is the default
|
||||
this( MINIMUM_VERSION );
|
||||
}
|
||||
|
||||
|
@ -392,6 +392,7 @@ public class HANADialect extends Dialect {
|
|||
return 7;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultDecimalPrecision() {
|
||||
//the maximum on HANA
|
||||
return 34;
|
||||
|
@ -489,20 +490,16 @@ public class HANADialect extends Dialect {
|
|||
typeConfiguration
|
||||
);
|
||||
|
||||
if ( getVersion().isSameOrAfter(2, 0, 20) ) {
|
||||
// Introduced in 2.0 SPS 02
|
||||
functionFactory.jsonValue_no_passing();
|
||||
functionFactory.jsonQuery_no_passing();
|
||||
functionFactory.jsonExists_hana();
|
||||
if ( getVersion().isSameOrAfter(2, 0, 40) ) {
|
||||
// Introduced in 2.0 SPS 04
|
||||
functionFactory.jsonObject_hana();
|
||||
functionFactory.jsonArray_hana();
|
||||
functionFactory.jsonArrayAgg_hana();
|
||||
functionFactory.jsonObjectAgg_hana();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||
|
@ -1130,7 +1127,7 @@ public class HANADialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public boolean supportsLateral() {
|
||||
return getVersion().isSameOrAfter( 2, 0, 40 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1992,7 +1989,7 @@ public class HANADialect extends Dialect {
|
|||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
// HANA supports IGNORE LOCKED since HANA 2.0 SPS3 (2.0.030)
|
||||
return getVersion().isSameOrAfter(2, 0, 30);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.hibernate.internal.util.config.ConfigurationHelper;
|
|||
import static org.hibernate.cfg.DialectSpecificSettings.HANA_MAX_LOB_PREFETCH_SIZE;
|
||||
|
||||
/**
|
||||
* Utility class that extract some initial configuration from the database for {@link HANADialect}.
|
||||
* Utility class that extracts some initial configuration from the database for {@link HANADialect}.
|
||||
*/
|
||||
public class HANAServerConfiguration {
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
|
|||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for HANA.
|
||||
* An SQL AST translator for HANA.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.hibernate.sql.ast.tree.select.SortSpecification;
|
|||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* SQL Server json_arrayagg function.
|
||||
* SAP HANA json_arrayagg function.
|
||||
*/
|
||||
public class HANAJsonArrayAggFunction extends JsonArrayAggFunction {
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
|
|||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* HANA json_objectagg function.
|
||||
* SAP HANA json_objectagg function.
|
||||
*/
|
||||
public class HANAJsonObjectAggFunction extends JsonObjectAggFunction {
|
||||
|
||||
|
|
Loading…
Reference in New Issue