diff --git a/hibernate-core/src/main/java/org/hibernate/boot/MetadataBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/MetadataBuilder.java index df93ea72d1..3e2c46dfbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataBuilder.java @@ -6,6 +6,7 @@ */ package org.hibernate.boot; +import java.sql.DatabaseMetaData; import javax.persistence.AttributeConverter; import javax.persistence.SharedCacheMode; @@ -23,6 +24,7 @@ import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.AttributeConverterDefinition; import org.hibernate.cfg.MetadataSourceType; import org.hibernate.dialect.function.SQLFunction; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.type.BasicType; import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.UserType; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java index 23210ea916..3cc724aa80 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java @@ -540,6 +540,8 @@ public class MetadataBuilderImpl implements MetadataBuilder, TypeContributions { private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl(); + private boolean autoQuoteKeywords; + // private PersistentAttributeMemberResolver persistentAttributeMemberResolver = // StandardPersistentAttributeMemberResolver.INSTANCE; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MappingDefaults.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MappingDefaults.java index f3cb163c09..9d56737103 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MappingDefaults.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MappingDefaults.java @@ -18,24 +18,24 @@ import org.hibernate.cache.spi.access.AccessType; * @since 5.0 */ public interface MappingDefaults { - public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id"; - public static final String DEFAULT_TENANT_IDENTIFIER_COLUMN_NAME = "tenant_id"; - public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class"; - public static final String DEFAULT_CASCADE_NAME = "none"; - public static final String DEFAULT_PROPERTY_ACCESS_NAME = "property"; + String DEFAULT_IDENTIFIER_COLUMN_NAME = "id"; + String DEFAULT_TENANT_IDENTIFIER_COLUMN_NAME = "tenant_id"; + String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class"; + String DEFAULT_CASCADE_NAME = "none"; + String DEFAULT_PROPERTY_ACCESS_NAME = "property"; /** * Identifies the database schema name to use if none specified in the mapping. * * @return The implicit schema name; may be {@code null} */ - public String getImplicitSchemaName(); + String getImplicitSchemaName(); /** * Identifies the database catalog name to use if none specified in the mapping. * * @return The implicit catalog name; may be {@code null} */ - public String getImplicitCatalogName(); + String getImplicitCatalogName(); /** * Should all database identifiers encountered in this context be implicitly quoted? @@ -46,7 +46,7 @@ public interface MappingDefaults { * * @return {@code true}/{@code false} */ - public boolean shouldImplicitlyQuoteIdentifiers(); + boolean shouldImplicitlyQuoteIdentifiers(); /** * Identifies the column name to use for the identifier column if none specified in @@ -54,7 +54,7 @@ public interface MappingDefaults { * * @return The implicit identifier column name */ - public String getImplicitIdColumnName(); + String getImplicitIdColumnName(); /** * Identifies the column name to use for the tenant identifier column if none is @@ -62,7 +62,7 @@ public interface MappingDefaults { * * @return The implicit tenant identifier column name */ - public String getImplicitTenantIdColumnName(); + String getImplicitTenantIdColumnName(); /** * Identifies the column name to use for the discriminator column if none specified @@ -70,7 +70,7 @@ public interface MappingDefaults { * * @return The implicit discriminator column name */ - public String getImplicitDiscriminatorColumnName(); + String getImplicitDiscriminatorColumnName(); /** * Identifies the package name to use if none specified in the mapping. Really only @@ -78,21 +78,21 @@ public interface MappingDefaults { * * @return The implicit package name. */ - public String getImplicitPackageName(); + String getImplicitPackageName(); /** * Is auto-importing of entity (short) names enabled? * * @return {@code true} if auto-importing is enabled; {@code false} otherwise. */ - public boolean isAutoImportEnabled(); + boolean isAutoImportEnabled(); /** * Identifies the cascade style to apply to associations if none specified in the mapping. * * @return The implicit cascade style */ - public String getImplicitCascadeStyleName(); + String getImplicitCascadeStyleName(); /** * Identifies the default {@link org.hibernate.property.access.spi.PropertyAccessStrategy} name to use if none specified in the @@ -102,7 +102,7 @@ public interface MappingDefaults { * * @see org.hibernate.property.access.spi.PropertyAccessStrategy */ - public String getImplicitPropertyAccessorName(); + String getImplicitPropertyAccessorName(); /** * Identifies whether singular associations (many-to-one, one-to-one) are lazy @@ -110,19 +110,19 @@ public interface MappingDefaults { * * @return The implicit association laziness */ - public boolean areEntitiesImplicitlyLazy(); + boolean areEntitiesImplicitlyLazy(); /** * Identifies whether plural attributes are lazy by default if not specified in the mapping. * * @return The implicit association laziness */ - public boolean areCollectionsImplicitlyLazy(); + boolean areCollectionsImplicitlyLazy(); /** * The cache access type to use if none is specified * * @return The implicit cache access type. */ - public AccessType getImplicitCacheAccessType(); + AccessType getImplicitCacheAccessType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 8cb84dc9b6..d59539488e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -746,9 +746,11 @@ public interface AvailableSettings { */ String LOG_SESSION_METRICS = "hibernate.session.events.log"; + /** + * Defines a default {@link org.hibernate.SessionEventListener} to be applied to opened Sessions. + */ String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto"; - /** * The deprecated name. Use {@link #SCANNER} or {@link #SCANNER_ARCHIVE_INTERPRETER} instead. */ @@ -820,4 +822,12 @@ public interface AvailableSettings { * annotations (combined with {@code orm.xml} mappings). */ String ARTIFACT_PROCESSING_ORDER = "hibernate.mapping.precedence"; + + /** + * Specifies whether to automatically quote any names that are deemed keywords. Auto-quoting + * is enabled by default. Set to false to disable. + * + * @since 5.0 + */ + String KEYWORD_AUTO_QUOTING_ENABLED = "hibernate.auto_quote_keyword"; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java index c8fd1ea845..e6ef862b09 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java @@ -20,7 +20,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; -import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; @@ -78,6 +77,7 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment { final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this ); identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) ); + identifierHelperBuilder.setGloballyQuoteIdentifiers( autoQuoting( cfgService ) ); identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport ); IdentifierHelper identifierHelper = null; @@ -113,6 +113,14 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment { ); } + private static boolean autoQuoting(ConfigurationService cfgService) { + return cfgService.getSetting( + AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, + StandardConverters.BOOLEAN, + true + ); + } + /** * Constructor form used from testing * @@ -207,6 +215,7 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment { final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this ); identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) ); + identifierHelperBuilder.setAutoQuoteKeywords( autoQuoting( cfgService ) ); identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport ); IdentifierHelper identifierHelper = null; try { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/NormalizingIdentifierHelperImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/NormalizingIdentifierHelperImpl.java index 1de09a03f0..73fc15604b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/NormalizingIdentifierHelperImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/NormalizingIdentifierHelperImpl.java @@ -28,6 +28,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { private final NameQualifierSupport nameQualifierSupport; private final boolean globallyQuoteIdentifiers; + private final boolean autoQuoteKeywords; private final Set reservedWords = new TreeSet( String.CASE_INSENSITIVE_ORDER ); private final IdentifierCaseStrategy unquotedCaseStrategy; private final IdentifierCaseStrategy quotedCaseStrategy; @@ -36,12 +37,14 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { JdbcEnvironment jdbcEnvironment, NameQualifierSupport nameQualifierSupport, boolean globallyQuoteIdentifiers, + boolean autoQuoteKeywords, Set reservedWords, IdentifierCaseStrategy unquotedCaseStrategy, IdentifierCaseStrategy quotedCaseStrategy) { this.jdbcEnvironment = jdbcEnvironment; this.nameQualifierSupport = nameQualifierSupport; this.globallyQuoteIdentifiers = globallyQuoteIdentifiers; + this.autoQuoteKeywords = autoQuoteKeywords; if ( reservedWords != null ) { this.reservedWords.addAll( reservedWords ); } @@ -66,7 +69,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { return Identifier.toIdentifier( identifier.getText(), true ); } - if ( isReservedWord( identifier.getText() ) ) { + if ( autoQuoteKeywords && isReservedWord( identifier.getText() ) ) { log.tracef( "Forcing identifier [%s] to quoted as recognized reserveed word", identifier ); return Identifier.toIdentifier( identifier.getText(), true ); } @@ -208,7 +211,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { return Identifier.toIdentifier( text, true ); } - if ( isReservedWord( text ) ) { + if ( autoQuoteKeywords && isReservedWord( text ) ) { // unequivocally it needs to be quoted... log.trace( "Forcing DatabaseMetaData return value as quoted as it was recognized as a reserved word" ); return Identifier.toIdentifier( text, true ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/IdentifierHelperBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/IdentifierHelperBuilder.java index 4bfc8565ee..ef2d7ffea2 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/IdentifierHelperBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/IdentifierHelperBuilder.java @@ -34,6 +34,7 @@ public class IdentifierHelperBuilder { private Set reservedWords = new TreeSet( String.CASE_INSENSITIVE_ORDER ); private boolean globallyQuoteIdentifiers = false; + private boolean autoQuoteKeywords = true; private IdentifierCaseStrategy unquotedCaseStrategy = IdentifierCaseStrategy.UPPER; private IdentifierCaseStrategy quotedCaseStrategy = IdentifierCaseStrategy.MIXED; @@ -135,6 +136,10 @@ public class IdentifierHelperBuilder { this.globallyQuoteIdentifiers = globallyQuoteIdentifiers; } + public void setAutoQuoteKeywords(boolean autoQuoteKeywords) { + this.autoQuoteKeywords = autoQuoteKeywords; + } + public NameQualifierSupport getNameQualifierSupport() { return nameQualifierSupport; } @@ -186,6 +191,7 @@ public class IdentifierHelperBuilder { jdbcEnvironment, nameQualifierSupport, globallyQuoteIdentifiers, + autoQuoteKeywords, reservedWords, unquotedCaseStrategy, quotedCaseStrategy diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/IdentifierHelperTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/IdentifierHelperTest.java new file mode 100644 index 0000000000..d4d80a363c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/IdentifierHelperTest.java @@ -0,0 +1,51 @@ +/* + * 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 . + */ +package org.hibernate.test.schemaupdate; + +import java.util.Collections; + +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.service.ServiceRegistry; + +import org.hibernate.testing.boot.ServiceRegistryTestingImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +public class IdentifierHelperTest extends BaseUnitTestCase { + @Test + public void testAutoQuotingDisabled() { + ServiceRegistry sr = ServiceRegistryTestingImpl.forUnitTesting( + Collections.singletonMap( + AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, + // true is the default, but to be sure... + true + ) + ); + Identifier identifier = sr.getService( JdbcEnvironment.class ).getIdentifierHelper().toIdentifier( "select" ); + assertTrue( identifier.isQuoted() ); + StandardServiceRegistryBuilder.destroy( sr ); + + sr = ServiceRegistryTestingImpl.forUnitTesting( + Collections.singletonMap( + AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, + false + ) + ); + identifier = sr.getService( JdbcEnvironment.class ).getIdentifierHelper().toIdentifier( "select" ); + assertFalse( identifier.isQuoted() ); + StandardServiceRegistryBuilder.destroy( sr ); + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/boot/ServiceRegistryTestingImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/boot/ServiceRegistryTestingImpl.java index 6a99cf4107..582ef06c06 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/boot/ServiceRegistryTestingImpl.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/boot/ServiceRegistryTestingImpl.java @@ -45,6 +45,19 @@ public class ServiceRegistryTestingImpl ); } + public static ServiceRegistryTestingImpl forUnitTesting(Map settings) { + return new ServiceRegistryTestingImpl( + true, + new BootstrapServiceRegistryBuilder().build(), + StandardServiceInitiators.LIST, + Arrays.asList( + dialectFactoryService(), + connectionProviderService() + ), + settings + ); + } + private static ProvidedService dialectFactoryService() { return new ProvidedService( DialectFactory.class, new DialectFactoryTestingImpl() ); }