From 32f4122470c3f1d103f2a768bfe88c9fdfc84909 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 7 Jan 2022 17:08:06 +0100 Subject: [PATCH] autoquote column names with initial _ on those dbs that require it --- .../java/org/hibernate/dialect/DB2Dialect.java | 10 ++++++++++ .../java/org/hibernate/dialect/DerbyDialect.java | 11 +++++++++++ .../java/org/hibernate/dialect/HSQLDialect.java | 9 +++++++++ .../org/hibernate/dialect/OracleDialect.java | 10 ++++++++++ .../NormalizingIdentifierHelperImpl.java | 10 +++++++++- .../jdbc/env/spi/IdentifierHelperBuilder.java | 16 ++++++++++------ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 3e8a0c7c5f..db05046753 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -21,6 +21,8 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.unique.DB2UniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; @@ -53,6 +55,7 @@ import org.hibernate.type.descriptor.jdbc.*; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; @@ -727,4 +730,11 @@ public class DB2Dialect extends Dialect { public String generatedAs(String generatedAs) { return " generated always as (" + generatedAs + ")"; } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + builder.setAutoQuoteInitialUnderscore(true); + return super.buildIdentifierHelper(builder, dbMetaData); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index 3ce0d66dae..2d16694dc7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -24,6 +24,8 @@ import org.hibernate.dialect.sequence.DerbySequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; @@ -64,6 +66,8 @@ import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType; import org.hibernate.type.descriptor.jdbc.TimestampJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; import java.sql.Types; import jakarta.persistence.TemporalType; @@ -876,4 +880,11 @@ public class DerbyDialect extends Dialect { // It seems at least the row_number function is supported as of 10.4 return getVersion().isSameOrAfter( 10, 4 ); } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + builder.setAutoQuoteInitialUnderscore(true); + return super.buildIdentifierHelper(builder, dbMetaData); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index 4e27a6774b..600584dac2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -7,6 +7,7 @@ package org.hibernate.dialect; import java.sql.DatabaseMetaData; +import java.sql.SQLException; import java.sql.Types; import org.hibernate.JDBCException; @@ -31,6 +32,8 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -785,4 +788,10 @@ public class HSQLDialect extends Dialect { } } + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + builder.setAutoQuoteInitialUnderscore(true); + return super.buildIdentifierHelper(builder, dbMetaData); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 5b3160964c..a8518fe573 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -7,6 +7,7 @@ package org.hibernate.dialect; import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; @@ -32,6 +33,8 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.LockAcquisitionException; @@ -1262,4 +1265,11 @@ public class OracleDialect extends Dialect { public String generatedAs(String generatedAs) { return " generated always as (" + generatedAs + ")"; } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + builder.setAutoQuoteInitialUnderscore(true); + return super.buildIdentifierHelper(builder, dbMetaData); + } } 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 2c95d6b8b6..fd0ab2e028 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 @@ -31,6 +31,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { private final boolean globallyQuoteIdentifiers; private final boolean globallyQuoteIdentifiersSkipColumnDefinitions; private final boolean autoQuoteKeywords; + private final boolean autoQuoteInitialUnderscore; private final TreeSet reservedWords; private final IdentifierCaseStrategy unquotedCaseStrategy; private final IdentifierCaseStrategy quotedCaseStrategy; @@ -41,6 +42,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { boolean globallyQuoteIdentifiers, boolean globallyQuoteIdentifiersSkipColumnDefinitions, boolean autoQuoteKeywords, + boolean autoQuoteInitialUnderscore, TreeSet reservedWords, //careful, we intentionally omit making a defensive copy to not waste memory IdentifierCaseStrategy unquotedCaseStrategy, IdentifierCaseStrategy quotedCaseStrategy) { @@ -49,6 +51,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { this.globallyQuoteIdentifiers = globallyQuoteIdentifiers; this.globallyQuoteIdentifiersSkipColumnDefinitions = globallyQuoteIdentifiersSkipColumnDefinitions; this.autoQuoteKeywords = autoQuoteKeywords; + this.autoQuoteInitialUnderscore = autoQuoteInitialUnderscore; this.reservedWords = reservedWords; this.unquotedCaseStrategy = unquotedCaseStrategy == null ? IdentifierCaseStrategy.UPPER : unquotedCaseStrategy; this.quotedCaseStrategy = quotedCaseStrategy == null ? IdentifierCaseStrategy.MIXED : quotedCaseStrategy; @@ -76,6 +79,11 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { return Identifier.toIdentifier( identifier.getText(), true ); } + if ( autoQuoteInitialUnderscore && identifier.getText().startsWith("_") ) { + log.tracef( "Forcing identifier [%s] to quoted due to initial underscore", identifier ); + return Identifier.toIdentifier( identifier.getText(), true ); + } + return identifier; } @@ -96,7 +104,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper { @Override public boolean isReservedWord(String word) { - if ( autoQuoteKeywords == false ) { + if ( !autoQuoteKeywords ) { throw new AssertionFailure( "The reserved keywords map is only initialized if autoQuoteKeywords is true" ); } return reservedWords.contains( word ); 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 7aa6892dae..808a2eff95 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 @@ -40,6 +40,7 @@ public class IdentifierHelperBuilder { private boolean globallyQuoteIdentifiers = false; private boolean skipGlobalQuotingForColumnDefinitions = false; private boolean autoQuoteKeywords = true; + private boolean autoQuoteInitialUnderscore = false; private IdentifierCaseStrategy unquotedCaseStrategy = IdentifierCaseStrategy.UPPER; private IdentifierCaseStrategy quotedCaseStrategy = IdentifierCaseStrategy.MIXED; @@ -64,10 +65,9 @@ public class IdentifierHelperBuilder { } //Important optimisation: skip loading all keywords from the DB when autoQuoteKeywords is disabled - if ( autoQuoteKeywords == false ) { - return; + if ( autoQuoteKeywords ) { + this.reservedWords.addAll( parseKeywords( metaData.getSQLKeywords() ) ); } - this.reservedWords.addAll( parseKeywords( metaData.getSQLKeywords() ) ); } private static List parseKeywords(String extraKeywordsString) { @@ -153,6 +153,10 @@ public class IdentifierHelperBuilder { this.autoQuoteKeywords = autoQuoteKeywords; } + public void setAutoQuoteInitialUnderscore(boolean autoQuoteInitialUnderscore) { + this.autoQuoteInitialUnderscore = autoQuoteInitialUnderscore; + } + public NameQualifierSupport getNameQualifierSupport() { return nameQualifierSupport; } @@ -187,10 +191,9 @@ public class IdentifierHelperBuilder { public void applyReservedWords(Collection words) { //No use when autoQuoteKeywords is disabled - if ( autoQuoteKeywords == false ) { - return; + if ( autoQuoteKeywords ) { + this.reservedWords.addAll( words ); } - this.reservedWords.addAll( words ); } public void applyReservedWords(Set words) { @@ -218,6 +221,7 @@ public class IdentifierHelperBuilder { globallyQuoteIdentifiers, skipGlobalQuotingForColumnDefinitions, autoQuoteKeywords, + autoQuoteInitialUnderscore, reservedWords, unquotedCaseStrategy, quotedCaseStrategy