HHH-16125 introduce SqlTypes.NAMED_ENUM to make pg enums non-default

and clean up the impl of enums
This commit is contained in:
Gavin 2023-04-29 11:26:51 +02:00 committed by Gavin King
parent bfb0fc6aba
commit 7514f8ad00
21 changed files with 117 additions and 123 deletions

View File

@ -806,12 +806,6 @@ public class MySQLLegacyDialect extends Dialect {
return type.append( ')' ).toString();
}
@Override
public String getCheckCondition(String columnName, String[] values) {
//not needed, because we use an 'enum' type
return null;
}
@Override
public String getQueryHintString(String query, String hints) {
return getMySQLVersion().isBefore( 5 )

View File

@ -799,6 +799,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements AggregateJdbcT
case SqlTypes.LONG32VARCHAR:
case SqlTypes.LONG32NVARCHAR:
case SqlTypes.ENUM:
case SqlTypes.NAMED_ENUM:
appender.quoteStart();
jdbcJavaType.appendEncodedString(
appender,

View File

@ -739,10 +739,6 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
}
}
public boolean hasNativeEnums() {
return false;
}
/**
* If this database has a special MySQL-style {@code enum} column type,
* return the type declaration for the given enumeration of values.

View File

@ -152,6 +152,7 @@ public class JsonHelper {
case SqlTypes.LONG32VARCHAR:
case SqlTypes.LONG32NVARCHAR:
case SqlTypes.ENUM:
case SqlTypes.NAMED_ENUM:
// These literals can contain the '"' character, so we need to escape it
appender.append( '"' );
appender.startEscaping();

View File

@ -874,11 +874,6 @@ public class MySQLDialect extends Dialect {
return getMySQLVersion().isSameOrAfter( 8, 0, 16 );
}
@Override
public boolean hasNativeEnums() {
return true;
}
@Override
public String getEnumTypeDeclaration(String name, String[] values) {
StringBuilder type = new StringBuilder();
@ -891,12 +886,6 @@ public class MySQLDialect extends Dialect {
return type.append( ')' ).toString();
}
@Override
public String getCheckCondition(String columnName, String[] values) {
//not needed, because we use an 'enum' type
return null;
}
@Override
public String getQueryHintString(String query, String hints) {
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );

View File

@ -24,6 +24,14 @@ import java.sql.SQLException;
import static org.hibernate.type.SqlTypes.ENUM;
/**
* Represents an {@code enum} type on MySQL.
* <p>
* Hibernate will automatically use this for enums mapped
* as {@link jakarta.persistence.EnumType#STRING}.
*
* @see org.hibernate.type.SqlTypes#ENUM
* @see MySQLDialect#getEnumTypeDeclaration(String, String[])
*
* @author Gavin King
*/
public class MySQLEnumJdbcType implements JdbcType {

View File

@ -97,34 +97,7 @@ import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
import static org.hibernate.type.SqlTypes.ARRAY;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.CLOB;
import static org.hibernate.type.SqlTypes.FLOAT;
import static org.hibernate.type.SqlTypes.GEOGRAPHY;
import static org.hibernate.type.SqlTypes.GEOMETRY;
import static org.hibernate.type.SqlTypes.INET;
import static org.hibernate.type.SqlTypes.JSON;
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
import static org.hibernate.type.SqlTypes.NCHAR;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.OTHER;
import static org.hibernate.type.SqlTypes.SQLXML;
import static org.hibernate.type.SqlTypes.STRUCT;
import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TIME_UTC;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import static org.hibernate.type.SqlTypes.*;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
@ -273,7 +246,7 @@ public class PostgreSQLDialect extends Dialect {
// Prefer jsonb if possible
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
ddlTypeRegistry.addDescriptor( NAMED_ENUM, new NamedNativeEnumDdlTypeImpl( this ) );
}
@Override
@ -387,21 +360,12 @@ public class PostgreSQLDialect extends Dialect {
return super.resolveSqlTypeCode( columnTypeName, typeConfiguration );
}
@Override
public boolean hasNativeEnums() {
return true;
}
@Override
public String getEnumTypeDeclaration(String name, String[] values) {
return name;
}
@Override
public String getCheckCondition(String columnName, Class<? extends Enum<?>> enumType) {
return null;
}
public String[] getCreateEnumTypeCommand(String name, String[] values) {
StringBuilder type = new StringBuilder();
type.append( "create type " )

View File

@ -25,16 +25,29 @@ import java.sql.SQLException;
import java.sql.Types;
import static java.util.Collections.emptySet;
import static org.hibernate.type.SqlTypes.ENUM;
import static org.hibernate.type.SqlTypes.NAMED_ENUM;
/**
* Represents a named {@code enum} type on PostgreSQL.
* <p>
* Hibernate does <em>not</em> automatically use this for enums
* mapped as {@link jakarta.persistence.EnumType#STRING}, and
* instead this type must be explicitly requested using:
* <pre>
* &#64;JdbcTypeCode(SqlTypes.NAMED_ENUM)
* </pre>
*
* @see org.hibernate.type.SqlTypes#NAMED_ENUM
* @see PostgreSQLDialect#getEnumTypeDeclaration(String, String[])
* @see PostgreSQLDialect#getCreateEnumTypeCommand(String, String[])
*
* @author Gavin King
*/
public class PostgreSQLEnumJdbcType implements JdbcType {
@Override
public int getJdbcTypeCode() {
return ENUM;
return NAMED_ENUM;
}
@Override

View File

@ -338,10 +338,10 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
column.setSqlTypeCode( resolution.getJdbcType().getDdlTypeCode() );
}
final String declaration = resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration( dialect );
if ( declaration != null ) {
column.setSpecializedTypeDeclaration( declaration );
}
// final String declaration = resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration( dialect );
// if ( declaration != null ) {
// column.setSpecializedTypeDeclaration( declaration );
// }
if ( dialect.supportsColumnCheck() ) {
final String checkCondition = resolution.getLegacyResolvedBasicType()

View File

@ -13,6 +13,7 @@ import java.util.Locale;
import java.util.Objects;
import org.hibernate.AssertionFailure;
import org.hibernate.Incubating;
import org.hibernate.MappingException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.TruthValue;
@ -67,7 +68,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
private String customWrite;
private String customRead;
private Size columnSize;
private String specializedTypeDeclaration;
// private String specializedTypeDeclaration;
private java.util.List<CheckConstraint> checkConstraints = new ArrayList<>();
public Column() {
@ -502,17 +503,20 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
return getClass().getSimpleName() + '(' + getName() + ')';
}
public void setSpecializedTypeDeclaration(String specializedTypeDeclaration) {
this.specializedTypeDeclaration = specializedTypeDeclaration;
}
public String getSpecializedTypeDeclaration() {
return specializedTypeDeclaration;
}
public boolean hasSpecializedTypeDeclaration() {
return specializedTypeDeclaration != null;
}
// @Incubating
// public void setSpecializedTypeDeclaration(String specializedTypeDeclaration) {
// this.specializedTypeDeclaration = specializedTypeDeclaration;
// }
//
// @Incubating
// public String getSpecializedTypeDeclaration() {
// return specializedTypeDeclaration;
// }
//
// @Incubating
// public boolean hasSpecializedTypeDeclaration() {
// return specializedTypeDeclaration != null;
// }
public void addCheckConstraint(CheckConstraint checkConstraint) {
if ( !checkConstraints.contains( checkConstraint) ) {
@ -710,7 +714,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
copy.assignmentExpression = assignmentExpression;
copy.customRead = customRead;
copy.customWrite = customWrite;
copy.specializedTypeDeclaration = specializedTypeDeclaration;
// copy.specializedTypeDeclaration = specializedTypeDeclaration;
copy.columnSize = columnSize;
return copy;
}

View File

@ -141,7 +141,6 @@ public class ForeignKey extends Constraint {
referencingColumn.setLength( referencedColumn.getLength() );
referencingColumn.setScale( referencedColumn.getScale() );
referencingColumn.setPrecision( referencedColumn.getPrecision() );
referencingColumn.setSpecializedTypeDeclaration( referencedColumn.getSpecializedTypeDeclaration() );
}
}
}

View File

@ -1865,8 +1865,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
private <T> boolean inlineValue(T value) {
return criteriaValueHandlingMode == ValueHandlingMode.INLINE || value instanceof Enum
&& queryEngine.getCriteriaBuilder().getSessionFactory().getJdbcServices().getDialect().hasNativeEnums();
return criteriaValueHandlingMode == ValueHandlingMode.INLINE;
// || is a literal enum mapped to a PostgreSQL named 'enum' type
}
@Override

View File

@ -38,6 +38,7 @@ import static org.hibernate.type.SqlTypes.hasDatePart;
import static org.hibernate.type.SqlTypes.hasTimePart;
import static org.hibernate.type.SqlTypes.isCharacterOrClobType;
import static org.hibernate.type.SqlTypes.isCharacterType;
import static org.hibernate.type.SqlTypes.isEnumType;
import static org.hibernate.type.SqlTypes.isIntegral;
import static org.hibernate.type.SqlTypes.isNumericType;
import static org.hibernate.type.SqlTypes.isSpatialType;
@ -201,7 +202,7 @@ public class ArgumentTypesValidator implements ArgumentsValidator {
}
break;
case STRING:
if ( !isCharacterType(code) && code != SqlTypes.ENUM && code != ENUM_UNKNOWN_JDBC_TYPE) {
if ( !isCharacterType(code) && !isEnumType(code) && code != ENUM_UNKNOWN_JDBC_TYPE) {
throwError(type, javaType, functionName, count);
}
break;

View File

@ -162,16 +162,16 @@ class ColumnDefinitions {
}
else {
final String columnType;
if ( column.hasSpecializedTypeDeclaration() ) {
columnType = column.getSpecializedTypeDeclaration();
definition.append( ' ' ).append( columnType );
}
else {
// if ( column.hasSpecializedTypeDeclaration() ) {
// columnType = column.getSpecializedTypeDeclaration();
// definition.append( ' ' ).append( columnType );
// }
// else {
columnType = column.getSqlType( metadata );
if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
definition.append( ' ' ).append( columnType );
}
}
// }
final String defaultValue = column.getDefaultValue();
if ( defaultValue != null ) {

View File

@ -163,13 +163,13 @@ public interface BasicType<T> extends Type, BasicDomainType<T>, MappingType, Bas
return checkCondition;
}
@Incubating
default String getSpecializedTypeDeclaration(Dialect dialect) {
return getMappedJavaType().getSpecializedTypeDeclaration( getJdbcType(), getValueConverter(), dialect );
}
@Override
default int compare(Object x, Object y, SessionFactoryImplementor sessionFactory) {
return compare( x, y );
}
// @Incubating
// default String getSpecializedTypeDeclaration(Dialect dialect) {
// return getMappedJavaType().getSpecializedTypeDeclaration( getJdbcType(), getValueConverter(), dialect );
// }
}

View File

@ -523,12 +523,23 @@ public class SqlTypes {
public static final int GEOGRAPHY = 3250;
/**
* A type code representing a SQL {@code ENUM} type.
* A type code representing a SQL {@code ENUM} type for databases like
* {@link org.hibernate.dialect.MySQLDialect MySQL} where {@code ENUM}
* types do not have names.
*
* @since 6.3
*/
public static final int ENUM = 6000;
/**
* A type code representing a SQL {@code ENUM} type for databases like
* {@link org.hibernate.dialect.PostgreSQLDialect PostgreSQL} where
* {@code ENUM} types must have names.
*
* @since 6.3
*/
public static final int NAMED_ENUM = 6001;
private SqlTypes() {
}
@ -775,4 +786,14 @@ public class SqlTypes {
return false;
}
}
public static boolean isEnumType(int typeCode) {
switch ( typeCode ) {
case ENUM:
case NAMED_ENUM:
return true;
default:
return false;
}
}
}

View File

@ -182,23 +182,6 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
return 0;
}
@Override @Deprecated
public String getSpecializedTypeDeclaration(JdbcType jdbcType, BasicValueConverter<?, ?> converter, Dialect dialect) {
if ( converter != null && dialect.hasNativeEnums() ) {
if ( jdbcType.isString() ) {
@SuppressWarnings("unchecked")
BasicValueConverter<Boolean, ?> stringConverter = (BasicValueConverter<Boolean, ?>) converter;
String[] values = new String[] {
stringConverter.toRelationalValue(false).toString(),
stringConverter.toRelationalValue(true).toString()
};
return dialect.getEnumTypeDeclaration( null, values );
}
}
return null;
}
@Override
public String getCheckCondition(String columnName, JdbcType jdbcType, BasicValueConverter<?, ?> converter, Dialect dialect) {
if ( converter != null ) {
@ -225,4 +208,18 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
}
return null;
}
// @Override @Deprecated
// public String getSpecializedTypeDeclaration(JdbcType jdbcType, BasicValueConverter<?, ?> converter, Dialect dialect) {
// if ( converter != null && jdbcType.isString() ) {
// @SuppressWarnings("unchecked")
// BasicValueConverter<Boolean, ?> stringConverter = (BasicValueConverter<Boolean, ?>) converter;
// String[] values = new String[] {
// stringConverter.toRelationalValue(false).toString(),
// stringConverter.toRelationalValue(true).toString()
// };
// return dialect.getEnumTypeDeclaration( null, values );
// }
// return null;
// }
}

View File

@ -14,6 +14,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import static jakarta.persistence.EnumType.ORDINAL;
import static org.hibernate.type.SqlTypes.CHAR;
@ -36,6 +37,7 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
final JdbcTypeRegistry jdbcTypeRegistry = context.getTypeConfiguration().getJdbcTypeRegistry();
final EnumType type = context.getEnumeratedType();
int sqlType;
switch ( type == null ? ORDINAL : type ) {
@ -43,7 +45,7 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
sqlType = hasManyValues() ? SMALLINT : TINYINT;
break;
case STRING:
if ( context.getDialect().hasNativeEnums() ) {
if ( jdbcTypeRegistry.hasRegisteredDescriptor( ENUM ) ) {
sqlType = ENUM;
}
else if ( context.getColumnLength() == 1 ) {
@ -56,7 +58,7 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
default:
throw new AssertionFailure("unknown EnumType");
}
return context.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( sqlType );
return jdbcTypeRegistry.getDescriptor( sqlType );
}
public boolean hasManyValues() {

View File

@ -349,11 +349,11 @@ public interface JavaType<T> extends Serializable {
return null;
}
/**
* @deprecated this was an experimental approach that we have moved away from
*/
@Incubating @Deprecated
default String getSpecializedTypeDeclaration(JdbcType jdbcType, BasicValueConverter<?, ?> converter, Dialect dialect) {
return null;
}
// /**
// * @deprecated this was an experimental approach that we have moved away from
// */
// @Incubating @Deprecated
// default String getSpecializedTypeDeclaration(JdbcType jdbcType, BasicValueConverter<?, ?> converter, Dialect dialect) {
// return null;
// }
}

View File

@ -89,8 +89,5 @@ public class JdbcTypeBaseline {
target.addDescriptor( new LongVarcharJdbcType(SqlTypes.LONG32NVARCHAR) );
target.addDescriptor( RowIdJdbcType.INSTANCE );
// by default, map named enums to varchar columns
target.addDescriptor( SqlTypes.ENUM, VarcharJdbcType.INSTANCE );
}
}

View File

@ -15,6 +15,12 @@ import org.hibernate.type.descriptor.sql.DdlType;
import static org.hibernate.type.SqlTypes.ENUM;
/**
* A {@link DdlType} representing a native SQL {@code enum} type.
*
* @see org.hibernate.type.SqlTypes#ENUM
* @see org.hibernate.type.SqlTypes#NAMED_ENUM
* @see Dialect#getEnumTypeDeclaration(Class)
*
* @author Gavin King
*/
public class NamedNativeEnumDdlTypeImpl implements DdlType {
@ -26,6 +32,7 @@ public class NamedNativeEnumDdlTypeImpl implements DdlType {
@Override
public int getSqlTypeCode() {
// note: also used for NAMED_ENUM
return ENUM;
}