diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDatabase.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDatabase.java index 23e9b3bc4dfb..38704ad505ff 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDatabase.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDatabase.java @@ -185,6 +185,21 @@ public boolean matchesUrl(String jdbcUrl) { } }, + TERADATA { + @Override + public Dialect createDialect(DialectResolutionInfo info) { + return new TeradataDialect( info ); + } + @Override + public boolean productNameMatches(String databaseName) { + return "Teradata".equals( databaseName ); + } + @Override + public String getDriverClassName(String jdbcUrl) { + return "com.teradata.jdbc.TeraDriver"; + } + }, + TIMESTEN { @Override public Dialect createDialect(DialectResolutionInfo info) { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDialectSelector.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDialectSelector.java index a14a8b495e90..307f7fb9fed5 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDialectSelector.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CommunityDialectSelector.java @@ -43,6 +43,9 @@ public Class resolve(String name) { return MaxDBDialect.class; case "SybaseAnywhere": return SybaseAnywhereDialect.class; + case "Teradata14": + case "Teradata": + return TeradataDialect.class; case "TimesTen": return TimesTenDialect.class; case "SingleStore": diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java new file mode 100644 index 000000000000..a1be6402b6aa --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java @@ -0,0 +1,656 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect; + +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Map; + +import org.hibernate.LockOptions; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.model.FunctionContributions; +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.boot.model.relational.QualifiedNameImpl; +import org.hibernate.boot.model.relational.QualifiedTableName; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.community.dialect.identity.Teradata14IdentityColumnSupport; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.TopLimitHandler; +import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; +import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Index; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.sqm.IntervalType; +import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.sql.ForUpdateFragment; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.tool.schema.internal.StandardIndexExporter; +import org.hibernate.tool.schema.spi.Exporter; +import org.hibernate.type.BasicType; +import org.hibernate.type.BasicTypeRegistry; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +import jakarta.persistence.TemporalType; + +import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.type.SqlTypes.BIGINT; +import static org.hibernate.type.SqlTypes.BINARY; +import static org.hibernate.type.SqlTypes.BOOLEAN; +import static org.hibernate.type.SqlTypes.TINYINT; +import static org.hibernate.type.SqlTypes.VARBINARY; + +/** + * A dialect for the Teradata database created by MCR as part of the + * dialect certification process. + * + * @author Jay Nance + */ +public class TeradataDialect extends Dialect { + + private static final DatabaseVersion DEFAULT_VERSION = DatabaseVersion.make( 12, 0 ); + + private static final int PARAM_LIST_SIZE_LIMIT = 1024; + + public TeradataDialect(DialectResolutionInfo info) { + this( info.makeCopyOrDefault( DEFAULT_VERSION ) ); + registerKeywords( info ); + } + + public TeradataDialect() { + this( DEFAULT_VERSION ); + } + + public TeradataDialect(DatabaseVersion version) { + super( version ); + } + + @Override + protected void registerDefaultKeywords() { + super.registerDefaultKeywords(); + registerKeyword( "password" ); + registerKeyword( "type" ); + registerKeyword( "title" ); + registerKeyword( "year" ); + registerKeyword( "month" ); + registerKeyword( "summary" ); + registerKeyword( "alias" ); + registerKeyword( "value" ); + registerKeyword( "first" ); + registerKeyword( "role" ); + registerKeyword( "account" ); + registerKeyword( "class" ); + } + + @Override + protected String columnType(int sqlTypeCode) { + switch ( sqlTypeCode ) { + case BOOLEAN: + case TINYINT: + return "byteint"; + //'bigint' has been there since at least version 13 + case BIGINT: + return getVersion().isBefore( 13 ) ? "numeric(19,0)" : "bigint"; + case BINARY: + return "byte($l)"; + case VARBINARY: + return "varbyte($l)"; + default: + return super.columnType( sqlTypeCode ); + } + } + + @Override + public int getDefaultStatementBatchSize() { + return getVersion().isBefore( 14 ) + ? 0 : 15; + } + + @Override + public boolean useInputStreamToInsertBlob() { + return getVersion().isSameOrAfter( 14 ); + } + + @Override + public boolean useConnectionToCreateLob() { + return false; + } + + @Override + public int getMaxVarcharLength() { + //for the unicode server character set + return 32_000; + } + + @Override + public int getMaxVarbinaryLength() { + return 64_000; + } + + @Override + public JdbcType resolveSqlTypeDescriptor( + String columnTypeName, int jdbcTypeCode, + int precision, + int scale, + JdbcTypeRegistry jdbcTypeRegistry) { + switch ( jdbcTypeCode ) { + case Types.BIT: + return jdbcTypeRegistry.getDescriptor( Types.BOOLEAN ); + case Types.NUMERIC: + case Types.DECIMAL: + if ( precision == 19 && scale == 0 ) { + return jdbcTypeRegistry.getDescriptor( Types.BIGINT ); + } + } + return super.resolveSqlTypeDescriptor( + columnTypeName, + jdbcTypeCode, + precision, + scale, + jdbcTypeRegistry + ); + } + + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return new StandardSqlAstTranslatorFactory() { + @Override + protected SqlAstTranslator buildTranslator( + SessionFactoryImplementor sessionFactory, Statement statement) { + return new TeradataSqlAstTranslator<>( sessionFactory, statement ); + } + }; + } + + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return Types.BIT; + } + + @Override + public int getDefaultDecimalPrecision() { + return getVersion().isBefore( 14 ) ? 18 : 38; + } + + @Override + public long getFractionalSecondPrecisionInNanos() { + // Do duration arithmetic in a seconds, but + // with the fractional part + return 1_000_000_000; //seconds!! + } + + public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + StringBuilder pattern = new StringBuilder(); + //TODO: TOTALLY UNTESTED CODE! + pattern.append("cast((?3-?2) "); + switch (unit) { + case NANOSECOND: + case NATIVE: + //default fractional precision is 6, the maximum + pattern.append("second"); + break; + case WEEK: + pattern.append("day"); + break; + case QUARTER: + pattern.append("month"); + break; + default: + pattern.append( "?1" ); + } + pattern.append("(4) as bigint)"); + switch (unit) { + case WEEK: + pattern.append("/7"); + break; + case QUARTER: + pattern.append("/3"); + break; + case NANOSECOND: + pattern.append("*1e9"); + break; + } + return pattern.toString(); + } + + @Override + public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { + //TODO: TOTALLY UNTESTED CODE! + switch ( unit ) { + case NANOSECOND: + return "(?3+(?2)/1e9*interval '1' second)"; + case NATIVE: + return "(?3+(?2)*interval '1' second)"; + case QUARTER: + return "(?3+(?2)*interval '3' month)"; + case WEEK: + return "(?3+(?2)*interval '7' day)"; + default: + return "(?3+(?2)*interval '1' ?1)"; + } + } + + @Override + public void initializeFunctionRegistry(FunctionContributions functionContributions) { + super.initializeFunctionRegistry(functionContributions); + final BasicTypeRegistry basicTypeRegistry = functionContributions.getTypeConfiguration().getBasicTypeRegistry(); + final BasicType stringType = basicTypeRegistry.resolve( StandardBasicTypes.STRING ); + + CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions); + functionFactory.concat_pipeOperator(); + functionFactory.octetLength(); + functionFactory.moreHyperbolic(); + functionFactory.instr(); + functionFactory.substr(); + functionFactory.substring_substr(); + //also natively supports ANSI-style substring() + functionFactory.position(); + functionFactory.bitLength_pattern( "octet_length(cast(?1 as char))*4" ); + + functionContributions.getFunctionRegistry().patternDescriptorBuilder( "mod", "(?1 mod ?2)" ) + .setInvariantType( stringType ) + .setExactArgumentCount( 2 ) + .register(); + + if ( getVersion().isSameOrAfter( 14 ) ) { + + //list actually taken from Teradata 15 docs + functionFactory.lastDay(); + functionFactory.initcap(); + functionFactory.trim2(); + functionFactory.soundex(); + functionFactory.ascii(); + functionFactory.char_chr(); + functionFactory.trunc(); + functionFactory.moreHyperbolic(); + functionFactory.monthsBetween(); + functionFactory.addMonths(); + functionFactory.stddevPopSamp(); + functionFactory.varPopSamp(); + } + + // No idea since when this is supported + functionFactory.windowFunctions(); + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.hypotheticalOrderedSetAggregates(); + } + + /** + * Does this dialect support the {@code FOR UPDATE} syntax? + * + * @return empty string ... Teradata does not support {@code FOR UPDATE} syntax + */ + @Override + public String getForUpdateString() { + return ""; + } + + @Override + public String getAddColumnString() { + return getVersion().isBefore( 14 ) ? super.getAddColumnString() : "add"; + } + + @Override + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new GlobalTemporaryTableMutationStrategy( + TemporaryTable.createIdTable( + rootEntityDescriptor, + basename -> TemporaryTable.ID_TABLE_PREFIX + basename, + this, + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + + @Override + public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new GlobalTemporaryTableInsertStrategy( + TemporaryTable.createEntityTable( + rootEntityDescriptor, + name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, + this, + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + + @Override + public TemporaryTableKind getSupportedTemporaryTableKind() { + return TemporaryTableKind.GLOBAL; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "on commit preserve rows"; + } + + @Override + public int getMaxAliasLength() { + // Max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that + return 20; + } + + @Override + public int getMaxIdentifierLength() { + return 30; + } + + @Override + public boolean supportsCascadeDelete() { + return false; + } + + @Override + public boolean supportsCircularCascadeDeleteConstraints() { + return false; + } + + @Override + public boolean supportsTupleDistinctCounts() { + return false; + } + + @Override + public boolean supportsExistsInSelect() { + return false; + } + + @Override + public boolean supportsOrderByInSubquery() { + // This is just a guess + return false; + } + + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 16, 10 ); + } + + @Override + public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) { + String v = "null"; + + switch ( sqlType ) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: + v = "cast(null as decimal)"; + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + v = "cast(null as varchar(255))"; + break; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: + v = "cast(null as timestamp)"; + break; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.NULL: + case Types.OTHER: + case Types.JAVA_OBJECT: + case Types.DISTINCT: + case Types.STRUCT: + case Types.ARRAY: + case Types.BLOB: + case Types.CLOB: + case Types.REF: + case Types.DATALINK: + case Types.BOOLEAN: + break; + } + return v; + } + + @Override + public boolean supportsUnboundedLobLocatorMaterialization() { + return false; + } + + @Override + public String getCreateMultisetTableString() { + return "create multiset table "; + } + + @Override + public boolean supportsLobValueChangePropagation() { + return false; + } + + @Override + public boolean doesReadCommittedCauseWritersToBlockReaders() { + return true; + } + + @Override + public boolean doesRepeatableReadCauseReadersToBlockWriters() { + return true; + } + + @Override + public boolean supportsBindAsCallableArgument() { + return false; + } + + @Override + public int getInExpressionCountLimit() { + return PARAM_LIST_SIZE_LIMIT; + } + + @Override + public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException { + statement.registerOutParameter( col, Types.REF ); + col++; + return col; + } + + @Override + public ResultSet getResultSet(CallableStatement cs) throws SQLException { + boolean isResultSet = cs.execute(); + while ( !isResultSet && cs.getUpdateCount() != -1 ) { + isResultSet = cs.getMoreResults(); + } + return cs.getResultSet(); + } + + @Override + public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { + return getVersion().isBefore( 14 ) ? super.getViolatedConstraintNameExtractor() : EXTRACTOR; + } + + private static final ViolatedConstraintNameExtractor EXTRACTOR = + new TemplatedViolatedConstraintNameExtractor( sqle -> { + String constraintName; + switch ( sqle.getErrorCode() ) { + case 27003: + constraintName = extractUsingTemplate( "Unique constraint (", ") violated.", sqle.getMessage() ); + break; + case 2700: + constraintName = extractUsingTemplate( "Referential constraint", "violation:", sqle.getMessage() ); + break; + case 5317: + constraintName = extractUsingTemplate( "Check constraint (", ") violated.", sqle.getMessage() ); + break; + default: + return null; + } + + if ( constraintName != null ) { + int i = constraintName.indexOf( '.' ); + if ( i != -1 ) { + constraintName = constraintName.substring( i + 1 ); + } + } + return constraintName; + } ); + + @Override + public boolean supportsLockTimeouts() { + return false; + } + + @Override + public boolean supportsNoWait() { + return true; + } + + @Override + public boolean supportsWait() { + return false; + } + + @Override + public boolean useFollowOnLocking(String sql, QueryOptions queryOptions) { + return getVersion().isSameOrAfter( 14 ); + } + + @Override + public String getWriteLockString(int timeout) { + if ( getVersion().isBefore( 14 ) ) { + return super.getWriteLockString( timeout ); + } + String sMsg = " Locking row for write "; + if ( timeout == LockOptions.NO_WAIT ) { + return sMsg + " nowait "; + } + return sMsg; + } + + @Override + public String getReadLockString(int timeout) { + if ( getVersion().isBefore( 14 ) ) { + return super.getReadLockString( timeout ); + } + String sMsg = " Locking row for read "; + if ( timeout == LockOptions.NO_WAIT ) { + return sMsg + " nowait "; + } + return sMsg; + } + + @Override + public Exporter getIndexExporter() { + return new TeradataIndexExporter(this); + } + + private static class TeradataIndexExporter extends StandardIndexExporter implements Exporter { + + private TeradataIndexExporter(Dialect dialect) { + super(dialect); + } + + @Override + public String[] getSqlCreateStrings(Index index, Metadata metadata, SqlStringGenerationContext context) { + QualifiedTableName qualifiedTableName = index.getTable().getQualifiedTableName(); + final String tableName = context.format( qualifiedTableName ); + + final String indexNameForCreation; + if ( getDialect().qualifyIndexName() ) { + indexNameForCreation = context.format( + new QualifiedNameImpl( + qualifiedTableName.getCatalogName(), + qualifiedTableName.getSchemaName(), + Identifier.toIdentifier( index.getName() ) + ) + ); + } + else { + indexNameForCreation = index.getName(); + } + + final StringBuilder columnList = new StringBuilder(); + boolean first = true; + for ( Column column : index.getColumns() ) { + if ( first ) { + first = false; + } + else { + columnList.append( ", " ); + } + columnList.append( column.getName() ); + } + + return new String[] { + "create index " + indexNameForCreation + + "(" + columnList + ") on " + tableName + }; + } + } + + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return getVersion().isBefore( 14 ) + ? super.getIdentityColumnSupport() + : Teradata14IdentityColumnSupport.INSTANCE; + } + + @Override + public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { + return getVersion().isBefore( 14 ) + ? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames ) + : new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString() + " " + sql; + } + + @Override + public LimitHandler getLimitHandler() { + return new TopLimitHandler( false ); + } + + @Override + public boolean supportsRowValueConstructorSyntax() { + return false; + } + + @Override + public boolean supportsRowValueConstructorSyntaxInInList() { + return false; + } + + @Override + public boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { + return false; + } + +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataSqlAstTranslator.java new file mode 100644 index 000000000000..2d6158654590 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataSqlAstTranslator.java @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect; + +import java.util.List; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.sqm.ComparisonOperator; +import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.ast.tree.select.SelectClause; +import org.hibernate.sql.exec.spi.JdbcOperation; + +/** + * A SQL AST translator for Teradata. + * + * @author Christian Beikov + */ +public class TeradataSqlAstTranslator extends AbstractSqlAstTranslator { + + public TeradataSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { + super( sessionFactory, statement ); + } + + @Override + public void visitQuerySpec(QuerySpec querySpec) { + if ( querySpec.isRoot() && getDialect().getVersion().isSameOrAfter( 14 ) ) { + final ForUpdateClause forUpdateClause = new ForUpdateClause(); + forUpdateClause.merge( getLockOptions() ); + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + super.visitQuerySpec( querySpec ); + } + + @Override + protected String getForUpdate() { + return "locking row for write "; + } + + @Override + protected String getForShare(int timeoutMillis) { + return "locking row for read "; + } + + @Override + protected String getNoWait() { + return "nowait "; + } + + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Teradata does not support the FOR UPDATE clause but has a proprietary LOCKING clause + } + + @Override + protected boolean needsRowsToSkip() { + return true; + } + + @Override + protected void renderFetchPlusOffsetExpression( + Expression fetchClauseExpression, + Expression offsetClauseExpression, + int offset) { + renderFetchPlusOffsetExpressionAsSingleParameter( fetchClauseExpression, offsetClauseExpression, offset ); + } + + @Override + protected void visitSqlSelections(SelectClause selectClause) { + renderTopClause( (QuerySpec) getQueryPartStack().getCurrent(), true, true ); + super.visitSqlSelections( selectClause ); + } + + @Override + public void visitOffsetFetchClause(QueryPart queryPart) { + // Teradata only supports the TOP clause + if ( !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) { + throw new IllegalArgumentException( "Can't emulate offset clause in subquery" ); + } + } + + @Override + protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { + renderComparisonEmulateIntersect( lhs, operator, rhs ); + } + + @Override + protected void renderSelectTupleComparison( + List lhsExpressions, + SqlTuple tuple, + ComparisonOperator operator) { + emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true ); + } + + @Override + protected void renderPartitionItem(Expression expression) { + if ( expression instanceof Literal ) { + appendSql( "()" ); + } + else if ( expression instanceof Summarization ) { + // This could theoretically be emulated by rendering all grouping variations of the query and + // connect them via union all but that's probably pretty inefficient and would have to happen + // on the query spec level + throw new UnsupportedOperationException( "Summarization is not supported by DBMS!" ); + } + else { + expression.accept( this ); + } + } + +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/identity/Teradata14IdentityColumnSupport.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/identity/Teradata14IdentityColumnSupport.java new file mode 100644 index 000000000000..5fd546725720 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/identity/Teradata14IdentityColumnSupport.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.identity; + +import org.hibernate.dialect.identity.IdentityColumnSupportImpl; + +/** + * @author Andrea Boriero + */ +public class Teradata14IdentityColumnSupport extends IdentityColumnSupportImpl { + + public static Teradata14IdentityColumnSupport INSTANCE = new Teradata14IdentityColumnSupport(); + + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public String getIdentityColumnString(int type) { + return "generated by default as identity not null"; + } + + @Override + public String getIdentityInsertString() { + return "null"; + } +} diff --git a/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/CommunityDialectSelectorTest.java b/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/CommunityDialectSelectorTest.java index a5898270b69a..9167514d611f 100644 --- a/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/CommunityDialectSelectorTest.java +++ b/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/CommunityDialectSelectorTest.java @@ -24,7 +24,7 @@ public void verifyAllDialectNamingResolve() { testDialectNamingResolution( MimerSQLDialect.class ); testDialectNamingResolution( SybaseAnywhereDialect.class ); - + testDialectNamingResolution( TeradataDialect.class ); testDialectNamingResolution( TimesTenDialect.class ); testDialectNamingResolution( SingleStoreDialect.class ); testDialectNamingResolution( DerbyDialect.class );