HHH-9550 - Allow a dialect to specify the level of catalog/schema support

This commit is contained in:
Steve Ebersole 2015-04-07 14:31:41 -05:00
parent a96385a6e0
commit 9786409096
21 changed files with 626 additions and 187 deletions

View File

@ -180,6 +180,7 @@ subprojects { subProject ->
}
// mac-specific stuff ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// should really use Jvm.current().toolsJar
ext.toolsJar = file("${System.getProperty('java.home')}/../lib/tools.jar")
if ( ext.toolsJar.exists() ) {
dependencies{

View File

@ -51,6 +51,7 @@ import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.spi.ConversionContext;
@ -2803,4 +2804,13 @@ public abstract class Dialect implements ConversionContext {
// most databases do not support returning cursors (ref_cursor)...
return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE;
}
/**
* By default interpret this based on DatabaseMetaData.
*
* @return
*/
public NameQualifierSupport getNameQualifierSupport() {
return null;
}
}

View File

@ -45,8 +45,8 @@ import org.hibernate.engine.jdbc.spi.TypeInfo;
public class ExtractedDatabaseMetaDataImpl implements ExtractedDatabaseMetaData {
private final JdbcEnvironment jdbcEnvironment;
private final String connectionSchemaName;
private final String connectionCatalogName;
private final String connectionSchemaName;
private final boolean supportsRefCursors;
private final boolean supportsNamedParameters;
@ -63,8 +63,8 @@ public class ExtractedDatabaseMetaDataImpl implements ExtractedDatabaseMetaData
private ExtractedDatabaseMetaDataImpl(
JdbcEnvironment jdbcEnvironment,
String connectionSchemaName,
String connectionCatalogName,
String connectionSchemaName,
Set<String> extraKeywords,
LinkedHashSet<TypeInfo> typeInfoSet,
boolean supportsRefCursors,
@ -78,8 +78,8 @@ public class ExtractedDatabaseMetaDataImpl implements ExtractedDatabaseMetaData
boolean lobLocatorUpdateCopy) {
this.jdbcEnvironment = jdbcEnvironment;
this.connectionSchemaName = connectionSchemaName;
this.connectionCatalogName = connectionCatalogName;
this.connectionSchemaName = connectionSchemaName;
this.extraKeywords = extraKeywords != null
? extraKeywords

View File

@ -40,6 +40,7 @@ import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
@ -77,6 +78,12 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
public JdbcEnvironmentImpl(ServiceRegistryImplementor serviceRegistry, Dialect dialect) {
this.dialect = dialect;
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
// assume both catalogs and schemas are supported
nameQualifierSupport = NameQualifierSupport.BOTH;
}
this.sqlExceptionHelper = buildSqlExceptionHelper( dialect );
this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this ).build();
@ -90,10 +97,12 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
// a simple impl that works on H2
this.identifierHelper = new NormalizingIdentifierHelperImpl(
this,
nameQualifierSupport,
globallyQuoteIdentifiers,
true, // storesMixedCaseQuotedIdentifiers
false, // storesLowerCaseQuotedIdentifiers
false, // storesUpperCaseQuotedIdentifiers
false, // storesMixedCaseIdentifiers
true, // storesUpperCaseIdentifiers
false // storesLowerCaseIdentifiers
);
@ -108,7 +117,7 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
);
// again, a simple impl that works on H2
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl();
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport );
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
}
@ -127,6 +136,11 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
.apply( databaseMetaData )
.build();
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
nameQualifierSupport = determineNameQualifierSupport( databaseMetaData );
}
for ( String keyword : dialect.getKeywords() ) {
reservedWords.add( keyword.toUpperCase() );
}
@ -138,10 +152,12 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
// a simple impl that works on H2
this.identifierHelper = new NormalizingIdentifierHelperImpl(
this,
nameQualifierSupport,
globallyQuoteIdentifiers,
true, // storesMixedCaseQuotedIdentifiers
false, // storesLowerCaseQuotedIdentifiers
false, // storesUpperCaseQuotedIdentifiers
false, // storesMixedCaseIdentifiers
true, // storesUpperCaseIdentifiers
false // storesLowerCaseIdentifiers
);
@ -149,11 +165,41 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
this.currentCatalog = null;
this.currentSchema = null;
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( databaseMetaData );
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl(
nameQualifierSupport,
databaseMetaData
);
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
}
private NameQualifierSupport determineNameQualifierSupport(DatabaseMetaData databaseMetaData) throws SQLException {
final boolean supportsCatalogs = databaseMetaData.supportsCatalogsInTableDefinitions();
final boolean supportsSchemas = databaseMetaData.supportsSchemasInTableDefinitions();
if ( supportsCatalogs && supportsSchemas ) {
return NameQualifierSupport.BOTH;
}
else if ( supportsCatalogs ) {
return NameQualifierSupport.CATALOG;
}
else if ( supportsSchemas ) {
return NameQualifierSupport.SCHEMA;
}
else {
return NameQualifierSupport.NONE;
}
}
/**
* The main constructor form. Builds a JdbcEnvironment using the available DatabaseMetaData
*
* @param serviceRegistry The service registry
* @param dialect The resolved dialect
* @param databaseMetaData The available DatabaseMetaData
*
* @throws SQLException
*/
public JdbcEnvironmentImpl(
ServiceRegistryImplementor serviceRegistry,
Dialect dialect,
@ -167,6 +213,11 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
.setConnectionSchemaName( determineCurrentSchemaName( databaseMetaData, serviceRegistry, dialect ) )
.build();
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
nameQualifierSupport = determineNameQualifierSupport( databaseMetaData );
}
for ( String keyword : dialect.getKeywords() ) {
reservedWords.add( keyword.toUpperCase() );
}
@ -178,10 +229,12 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
this.identifierHelper = new NormalizingIdentifierHelperImpl(
this,
nameQualifierSupport,
globallyQuoteIdentifiers,
databaseMetaData.storesMixedCaseQuotedIdentifiers(),
databaseMetaData.storesLowerCaseQuotedIdentifiers(),
databaseMetaData.storesUpperCaseQuotedIdentifiers(),
databaseMetaData.storesMixedCaseIdentifiers(),
databaseMetaData.storesUpperCaseIdentifiers(),
databaseMetaData.storesLowerCaseIdentifiers()
);
@ -190,7 +243,10 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
this.currentCatalog = identifierHelper.toIdentifier( extractedMetaDataSupport.getConnectionCatalogName() );
this.currentSchema = identifierHelper.toIdentifier( extractedMetaDataSupport.getConnectionSchemaName() );
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( databaseMetaData );
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl(
nameQualifierSupport,
databaseMetaData
);
this.typeInfoSet.addAll( TypeInfo.extractTypeInfo( databaseMetaData ) );

View File

@ -23,10 +23,12 @@
*/
package org.hibernate.engine.jdbc.env.internal;
import java.util.Locale;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.jboss.logging.Logger;
@ -38,27 +40,33 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
private final JdbcEnvironment jdbcEnvironment;
private final NameQualifierSupport nameQualifierSupport;
private final boolean globallyQuoteIdentifiers;
private final boolean storesMixedCaseQuotedIdentifiers;
private final boolean storesLowerCaseQuotedIdentifiers;
private final boolean storesUpperCaseQuotedIdentifiers;
private final boolean storesMixedCaseIdentifiers;
private final boolean storesUpperCaseIdentifiers;
private final boolean storesLowerCaseIdentifiers;
public NormalizingIdentifierHelperImpl(
JdbcEnvironment jdbcEnvironment,
NameQualifierSupport nameQualifierSupport,
boolean globallyQuoteIdentifiers,
boolean storesMixedCaseQuotedIdentifiers,
boolean storesLowerCaseQuotedIdentifiers,
boolean storesUpperCaseQuotedIdentifiers,
boolean storesMixedCaseIdentifiers,
boolean storesUpperCaseIdentifiers,
boolean storesLowerCaseIdentifiers) {
this.jdbcEnvironment = jdbcEnvironment;
this.nameQualifierSupport = nameQualifierSupport;
this.globallyQuoteIdentifiers = globallyQuoteIdentifiers;
this.storesMixedCaseQuotedIdentifiers = storesMixedCaseQuotedIdentifiers;
this.storesLowerCaseQuotedIdentifiers = storesLowerCaseQuotedIdentifiers;
this.storesUpperCaseQuotedIdentifiers = storesUpperCaseQuotedIdentifiers;
this.storesMixedCaseIdentifiers = storesMixedCaseIdentifiers;
this.storesUpperCaseIdentifiers = storesUpperCaseIdentifiers;
this.storesLowerCaseIdentifiers = storesLowerCaseIdentifiers;
@ -124,11 +132,16 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
@Override
public String toMetaDataCatalogName(Identifier identifier) {
if ( !nameQualifierSupport.supportsCatalogs() ) {
// null is used to tell DBMD to not limit results based on catalog.
return null;
}
if ( identifier == null ) {
// todo : not sure if this is interpreted as <""> or <currentCatalog>
return jdbcEnvironment.getCurrentCatalog() == null
? null
: jdbcEnvironment.getCurrentCatalog().render( jdbcEnvironment.getDialect() );
if ( jdbcEnvironment.getCurrentCatalog() == null ) {
return "";
}
identifier = jdbcEnvironment.getCurrentCatalog();
}
return toMetaDataText( identifier );
@ -139,30 +152,48 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
throw new IllegalArgumentException( "Identifier cannot be null; bad usage" );
}
// todo : not actually sure the right thing to do here.
// Shouldn't the identifiers be quoted using db-quoting?
if ( identifier.isQuoted() && storesMixedCaseQuotedIdentifiers ) {
return identifier.getText();
if ( identifier.isQuoted() ) {
if ( storesMixedCaseQuotedIdentifiers ) {
return identifier.getText();
}
else if ( storesUpperCaseQuotedIdentifiers ) {
return identifier.getText().toUpperCase( Locale.ENGLISH );
}
else if ( storesLowerCaseQuotedIdentifiers ) {
return identifier.getText().toLowerCase( Locale.ENGLISH );
}
else {
return identifier.getText();
}
}
else if ( ( identifier.isQuoted() && storesUpperCaseQuotedIdentifiers )
|| ( !identifier.isQuoted() && storesUpperCaseIdentifiers ) ) {
return StringHelper.toUpperCase( identifier.getText() );
else {
if ( storesMixedCaseIdentifiers ) {
return identifier.getText();
}
else if ( storesUpperCaseIdentifiers ) {
return identifier.getText().toUpperCase( Locale.ENGLISH );
}
else if ( storesLowerCaseIdentifiers ) {
return identifier.getText().toLowerCase( Locale.ENGLISH );
}
else {
return identifier.getText();
}
}
else if ( ( identifier.isQuoted() && storesLowerCaseQuotedIdentifiers )
|| ( !identifier.isQuoted() && storesLowerCaseIdentifiers ) ) {
return StringHelper.toLowerCase( identifier.getText() );
}
return identifier.getText();
}
@Override
public String toMetaDataSchemaName(Identifier identifier) {
if ( !nameQualifierSupport.supportsSchemas() ) {
// null is used to tell DBMD to not limit results based on schema.
return null;
}
if ( identifier == null ) {
// todo : not sure if this is interpreted as <""> or <currentSchema>
return jdbcEnvironment.getCurrentSchema() == null
? null
: jdbcEnvironment.getCurrentSchema().getText();
if ( jdbcEnvironment.getCurrentSchema() == null ) {
return "";
}
identifier = jdbcEnvironment.getCurrentSchema();
}
return toMetaDataText( identifier );
@ -179,7 +210,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
@Override
public Identifier fromMetaDataCatalogName(String catalogName) {
if ( catalogName == null ) {
if ( catalogName == null || !nameQualifierSupport.supportsCatalogs() ) {
return null;
}
@ -223,7 +254,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
@Override
public Identifier fromMetaDataSchemaName(String schemaName) {
if ( schemaName == null ) {
if ( schemaName == null || !nameQualifierSupport.supportsSchemas() ) {
return null;
}

View File

@ -31,6 +31,7 @@ import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
/**
@ -40,59 +41,65 @@ import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
* @author Steve Ebersole
*/
public class QualifiedObjectNameFormatterStandardImpl implements QualifiedObjectNameFormatter {
private final String catalogSeparator;
private final boolean catalogAtEnd;
private final Format format;
public QualifiedObjectNameFormatterStandardImpl(DatabaseMetaData databaseMetaData) throws SQLException {
public QualifiedObjectNameFormatterStandardImpl(
NameQualifierSupport nameQualifierSupport,
String catalogSeparator,
boolean catalogAtEnd) {
this.format = buildFormat( nameQualifierSupport, catalogSeparator, catalogAtEnd );
}
private Format buildFormat(
NameQualifierSupport nameQualifierSupport,
String catalogSeparator,
boolean catalogAtEnd) {
switch ( nameQualifierSupport ) {
case NONE: {
return NoQualifierSupportFormat.INSTANCE;
}
case CATALOG: {
return catalogAtEnd
? new NameCatalogFormat( catalogSeparator )
: new CatalogNameFormat( catalogSeparator );
}
case SCHEMA: {
return SchemaNameFormat.INSTANCE;
}
default: {
return catalogAtEnd
? new SchemaNameCatalogFormat( catalogSeparator )
: new CatalogSchemaNameFormat( catalogSeparator );
}
}
}
public QualifiedObjectNameFormatterStandardImpl(NameQualifierSupport nameQualifierSupport) {
// most dbs simply do <catalog>.<schema>.<name>
this( nameQualifierSupport, ".", false );
}
public QualifiedObjectNameFormatterStandardImpl(
NameQualifierSupport nameQualifierSupport,
DatabaseMetaData databaseMetaData) throws SQLException {
this(
nameQualifierSupport,
databaseMetaData.getCatalogSeparator(),
!databaseMetaData.isCatalogAtStart()
);
}
public QualifiedObjectNameFormatterStandardImpl(String catalogSeparator, boolean catalogAtEnd) {
this.catalogSeparator = catalogSeparator;
this.catalogAtEnd = catalogAtEnd;
}
public QualifiedObjectNameFormatterStandardImpl() {
// most dbs simply do <catalog>.<schema>.<name>
this( ".", false );
}
@Override
public String format(QualifiedTableName qualifiedTableName, Dialect dialect) {
return format(
render( qualifiedTableName.getCatalogName(), dialect ),
render( qualifiedTableName.getSchemaName(), dialect ),
render( qualifiedTableName.getTableName(), dialect )
return format.format(
qualifiedTableName.getCatalogName(),
qualifiedTableName.getSchemaName(),
qualifiedTableName.getTableName(),
dialect
);
}
private String format(String catalogName, String schemaName, String objectName) {
StringBuilder buff = new StringBuilder();
if ( !catalogAtEnd ) {
if ( catalogName != null ) {
buff.append( catalogName ).append( catalogSeparator );
}
}
if ( schemaName != null ) {
buff.append( schemaName ).append( '.' );
}
buff.append( objectName );
if ( catalogAtEnd ) {
if ( catalogName != null ) {
buff.append( catalogSeparator ).append( catalogName );
}
}
return buff.toString();
}
private String render(Identifier identifier, Dialect dialect) {
private static String render(Identifier identifier, Dialect dialect) {
if ( identifier == null ) {
return null;
}
@ -102,21 +109,148 @@ public class QualifiedObjectNameFormatterStandardImpl implements QualifiedObject
@Override
public String format(QualifiedSequenceName qualifiedSequenceName, Dialect dialect) {
return format(
render( qualifiedSequenceName.getCatalogName(), dialect ),
render( qualifiedSequenceName.getSchemaName(), dialect ),
render( qualifiedSequenceName.getSequenceName(), dialect )
return format.format(
qualifiedSequenceName.getCatalogName(),
qualifiedSequenceName.getSchemaName(),
qualifiedSequenceName.getSequenceName(),
dialect
);
}
@Override
public String format(QualifiedName qualifiedName, Dialect dialect) {
return format(
render( qualifiedName.getCatalogName(), dialect ),
render( qualifiedName.getSchemaName(), dialect ),
render( qualifiedName.getObjectName(), dialect )
return format.format(
qualifiedName.getCatalogName(),
qualifiedName.getSchemaName(),
qualifiedName.getObjectName(),
dialect
);
}
private static interface Format {
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect);
}
private static class NoQualifierSupportFormat implements Format {
/**
* Singleton access
*/
public static final NoQualifierSupportFormat INSTANCE = new NoQualifierSupportFormat();
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
return render( name, dialect );
}
}
private static class SchemaNameCatalogFormat implements Format {
private final String catalogSeparator;
public SchemaNameCatalogFormat(String catalogSeparator) {
this.catalogSeparator = catalogSeparator;
}
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
StringBuilder buff = new StringBuilder();
if ( schema != null ) {
buff.append( render( schema, dialect ) ).append( '.' );
}
buff.append( render( name, dialect ) );
if ( catalog != null ) {
buff.append( catalogSeparator ).append( render( catalog, dialect ) );
}
return buff.toString();
}
}
private static class CatalogSchemaNameFormat implements Format {
private final String catalogSeparator;
public CatalogSchemaNameFormat(String catalogSeparator) {
this.catalogSeparator = catalogSeparator;
}
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
StringBuilder buff = new StringBuilder();
if ( catalog != null ) {
buff.append( render( catalog, dialect ) ).append( catalogSeparator );
}
if ( schema != null ) {
buff.append( render( schema, dialect ) ).append( '.' );
}
buff.append( render( name, dialect ) );
return buff.toString();
}
}
private static class NameCatalogFormat implements Format {
private final String catalogSeparator;
public NameCatalogFormat(String catalogSeparator) {
this.catalogSeparator = catalogSeparator;
}
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
StringBuilder buff = new StringBuilder();
buff.append( render( name, dialect ) );
if ( catalog != null ) {
buff.append( catalogSeparator ).append( render( catalog, dialect ) );
}
return buff.toString();
}
}
private static class CatalogNameFormat implements Format {
private final String catalogSeparator;
public CatalogNameFormat(String catalogSeparator) {
this.catalogSeparator = catalogSeparator;
}
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
StringBuilder buff = new StringBuilder();
if ( catalog != null ) {
buff.append( render( catalog, dialect ) ).append( catalogSeparator );
}
buff.append( render( name, dialect ) );
return buff.toString();
}
}
private static class SchemaNameFormat implements Format {
/**
* Singleton access
*/
public static final SchemaNameFormat INSTANCE = new SchemaNameFormat();
@Override
public String format(Identifier catalog, Identifier schema, Identifier name, Dialect dialect) {
StringBuilder buff = new StringBuilder();
if ( schema != null ) {
buff.append( render( schema, dialect ) ).append( '.' );
}
buff.append( render( name, dialect ) );
return buff.toString();
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.jdbc.env.spi;
/**
* Enumerated values representing the level of support for catalog and schema.
*
* @author Steve Ebersole
*/
public enum NameQualifierSupport {
/**
* Only catalog is supported
*/
CATALOG,
/**
* Only schema is supported
*/
SCHEMA,
/**
* Both catalog and schema are supported.
*/
BOTH,
/**
* Neither catalog nor schema are supported.
*/
NONE;
public boolean supportsCatalogs() {
return this == CATALOG || this == BOTH;
}
public boolean supportsSchemas() {
return this == SCHEMA || this == BOTH;
}
}

View File

@ -110,12 +110,22 @@ public class SchemaExport {
/**
* Builds a SchemaExport object.
*
* @param metadata The metadata object holding the mapping info to be exported
*/
public SchemaExport(MetadataImplementor metadata) {
this( metadata.getMetadataBuildingOptions().getServiceRegistry(), metadata );
}
/**
* Builds a SchemaExport object.
*
* @param metadata The metadata object holding the mapping info to be exported
*/
public SchemaExport(MetadataImplementor metadata, boolean exportSchemas) {
this( metadata.getMetadataBuildingOptions().getServiceRegistry(), metadata, exportSchemas );
}
/**
* Builds a SchemaExport object.
*
@ -129,14 +139,34 @@ public class SchemaExport {
serviceRegistry.getService( ConnectionProvider.class )
),
serviceRegistry,
metadata
metadata,
false
);
}
/**
* Builds a SchemaExport object.
*
* @param serviceRegistry The registry of services available for use. Should, at a minimum, contain
* the JdbcServices service.
* @param metadata The metadata object holding the mapping info to be exported
*/
public SchemaExport(ServiceRegistry serviceRegistry, MetadataImplementor metadata, boolean exportSchemas) {
this(
new SuppliedConnectionProviderConnectionHelper(
serviceRegistry.getService( ConnectionProvider.class )
),
serviceRegistry,
metadata,
exportSchemas
);
}
private SchemaExport(
ConnectionHelper connectionHelper,
ServiceRegistry serviceRegistry,
MetadataImplementor metadata) {
MetadataImplementor metadata,
boolean exportSchemas) {
this.connectionHelper = connectionHelper;
this.sqlStatementLogger = serviceRegistry.getService( JdbcServices.class ).getSqlStatementLogger();
this.formatter = ( sqlStatementLogger.isFormat() ? FormatStyle.DDL : FormatStyle.NONE ).getFormatter();
@ -177,10 +207,10 @@ public class SchemaExport {
final Map settings = serviceRegistry.getService( ConfigurationService.class ).getSettings();
schemaManagementTool.getSchemaDropper( settings ).doDrop( metadata, false, target );
schemaManagementTool.getSchemaDropper( settings ).doDrop( metadata, exportSchemas, target );
this.dropSQL = commands.toArray( new String[commands.size()] );
schemaManagementTool.getSchemaCreator( settings ).doCreation( metadata, false, target );
schemaManagementTool.getSchemaCreator( settings ).doCreation( metadata, exportSchemas, target );
this.createSQL = commands.toArray( new String[commands.size()] );
}
@ -196,7 +226,8 @@ public class SchemaExport {
this(
connectionHelper,
metadata.getMetadataBuildingOptions().getServiceRegistry(),
metadata
metadata,
false
);
}
@ -520,7 +551,7 @@ public class SchemaExport {
try {
final MetadataImplementor metadata = buildMetadata( commandLineArgs, serviceRegistry );
SchemaExport schemaExport = new SchemaExport( serviceRegistry, metadata )
SchemaExport schemaExport = new SchemaExport( serviceRegistry, metadata, commandLineArgs.exportSchemas )
.setHaltOnError( commandLineArgs.halt )
.setOutputFile( commandLineArgs.outputFile )
.setDelimiter( commandLineArgs.delimiter )
@ -634,6 +665,8 @@ public class SchemaExport {
boolean export = true;
boolean format = false;
boolean exportSchemas = false;
String delimiter = null;
String outputFile = null;
@ -661,6 +694,9 @@ public class SchemaExport {
else if ( arg.equals( "--create" ) ) {
parsedArgs.create = true;
}
else if ( arg.equals( "--schemas" ) ) {
parsedArgs.exportSchemas = true;
}
else if ( arg.equals( "--haltonerror" ) ) {
parsedArgs.halt = true;
}

View File

@ -48,6 +48,11 @@ public class DatabaseInformationImpl implements DatabaseInformation, ExtractionC
// DatabaseInformation implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean schemaExists(Schema.Name schema) {
return false;
}
@Override
public TableInformation getTableInformation(Identifier catalogName, Identifier schemaName, Identifier tableName) {
return locateRegisteredTableInformation( new QualifiedTableName( catalogName, schemaName, tableName ) );

View File

@ -65,12 +65,6 @@ import org.hibernate.tool.schema.spi.SchemaManagementException;
public class InformationExtractorJdbcDatabaseMetaDataImpl implements InformationExtractor {
private static final CoreMessageLogger log = CoreLogging.messageLogger( InformationExtractorJdbcDatabaseMetaDataImpl.class );
public static final String ALL_CATALOGS_FILTER = null;
public static final String SANS_CATALOG_FILTER = "";
public static final String ALL_SCHEMAS_FILTER = null;
public static final String SANS_SCHEMA_FILTER = "";
private final String[] tableTypes;
private final ExtractionContext extractionContext;
@ -96,6 +90,41 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
return extractionContext.getJdbcEnvironment().getSqlExceptionHelper().convert( sqlException, message );
}
@Override
public boolean schemaExists(Identifier catalog, Identifier schema) {
try {
final ResultSet resultSet = extractionContext.getJdbcDatabaseMetaData().getSchemas(
determineCatalogFilter( catalog ),
determineSchemaFilter( schema )
);
try {
if ( !resultSet.next() ) {
return false;
}
if ( resultSet.next() ) {
log.debugf(
"Multiple schemas found with that name [%s.%s]",
catalog.getCanonicalName(),
schema.getCanonicalName()
);
}
return true;
}
finally {
try {
resultSet.close();
}
catch (SQLException ignore) {
}
}
}
catch (SQLException sqlException) {
throw convertSQLException( sqlException, "Unable to query DatabaseMetaData for existing schemas" );
}
}
@Override
public Collection<TableInformation> getTables(Identifier catalog, Identifier schema) {
try {
@ -136,16 +165,8 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
if ( identifierToUse == null ) {
identifierToUse = extractionContext.getDefaultCatalog();
}
// if ( identifierToUse == null ) {
// identifierToUse = extractionContext.getJdbcEnvironment().getCurrentCatalog();
// }
if ( identifierToUse == null ) {
return ALL_CATALOGS_FILTER;
// return SANS_CATALOG_FILTER;
}
return determineAppropriateCapitalization( identifierToUse );
return extractionContext.getJdbcEnvironment().getIdentifierHelper().toMetaDataCatalogName( identifierToUse );
}
private String determineSchemaFilter(Identifier schema) throws SQLException {
@ -153,47 +174,8 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
if ( identifierToUse == null ) {
identifierToUse = extractionContext.getDefaultSchema();
}
// if ( identifierToUse == null ) {
// identifierToUse = extractionContext.getJdbcEnvironment().getCurrentSchema();
// }
if ( identifierToUse == null ) {
return ALL_SCHEMAS_FILTER;
// return SANS_SCHEMA_FILTER;
}
return determineAppropriateCapitalization( identifierToUse );
}
private String determineAppropriateCapitalization(Identifier identifierToUse) throws SQLException {
if ( identifierToUse.isQuoted() ) {
if ( extractionContext.getJdbcDatabaseMetaData().storesMixedCaseQuotedIdentifiers() ) {
return identifierToUse.getText();
}
else if ( extractionContext.getJdbcDatabaseMetaData().storesUpperCaseQuotedIdentifiers() ) {
return identifierToUse.getText().toUpperCase( Locale.ENGLISH );
}
else if ( extractionContext.getJdbcDatabaseMetaData().storesLowerCaseQuotedIdentifiers() ) {
return identifierToUse.getText().toLowerCase( Locale.ENGLISH );
}
else {
return identifierToUse.getText();
}
}
else {
if ( extractionContext.getJdbcDatabaseMetaData().storesMixedCaseIdentifiers() ) {
return identifierToUse.getText();
}
else if ( extractionContext.getJdbcDatabaseMetaData().storesUpperCaseIdentifiers() ) {
return identifierToUse.getText().toUpperCase( Locale.ENGLISH );
}
else if ( extractionContext.getJdbcDatabaseMetaData().storesLowerCaseIdentifiers() ) {
return identifierToUse.getText().toLowerCase( Locale.ENGLISH );
}
else {
return identifierToUse.getText();
}
}
return extractionContext.getJdbcEnvironment().getIdentifierHelper().toMetaDataSchemaName( identifierToUse );
}
public TableInformation extractTableInformation(ResultSet resultSet) throws SQLException {
@ -227,7 +209,7 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
ResultSet resultSet = extractionContext.getJdbcDatabaseMetaData().getTables(
catalogFilter,
schemaFilter,
determineAppropriateCapitalization( tableName ),
extractionContext.getJdbcEnvironment().getIdentifierHelper().toMetaDataObjectName( tableName ),
tableTypes
);

View File

@ -111,6 +111,11 @@ public class DatabaseInformationImpl implements DatabaseInformation, ExtractionC
}
}
@Override
public boolean schemaExists(Schema.Name schema) {
return extractor.schemaExists( schema.getCatalog(), schema.getSchema() );
}
@Override
public TableInformation getTableInformation(
Identifier catalogName,

View File

@ -36,6 +36,15 @@ import org.hibernate.boot.model.relational.Schema;
* @author Steve Ebersole
*/
public interface DatabaseInformation {
/**
* Check to see if the given schema already exists.
*
* @param schema The schema name
*
* @return {@code true} indicates a schema with the given name already exists
*/
boolean schemaExists(Schema.Name schema);
/**
* Obtain reference to the named TableInformation
*

View File

@ -40,6 +40,8 @@ import org.hibernate.tool.schema.extract.internal.TableInformationImpl;
*/
public interface InformationExtractor {
boolean schemaExists(Identifier catalog, Identifier schema);
/**
* Return information about all matching tables, depending on whether to apply filter for
* catalog/schema.
@ -104,5 +106,4 @@ public interface InformationExtractor {
* @return The extracted foreign-key information
*/
public Iterable<ForeignKeyInformation> getForeignKeys(TableInformation tableInformation);
}

View File

@ -93,13 +93,15 @@ public class SchemaMigratorImpl implements SchemaMigrator {
for ( Schema schema : database.getSchemas() ) {
if ( createSchemas ) {
if ( schema.getName().getSchema() != null ) {
applySqlString(
database.getJdbcEnvironment().getDialect().getCreateSchemaCommand(
schema.getName().getSchema().render( database.getJdbcEnvironment().getDialect() )
),
targets,
false
);
if ( !existingDatabase.schemaExists( schema.getName() ) ) {
applySqlString(
database.getJdbcEnvironment().getDialect().getCreateSchemaCommand(
schema.getName().getSchema().render( database.getJdbcEnvironment().getDialect() )
),
targets,
false
);
}
}
}

View File

@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
@ -49,7 +50,7 @@ public class EmbeddableIntegratorTest extends BaseUnitTestCase {
/**
* Throws a mapping exception because DollarValue is not mapped
*/
@Test(expected = GenericJDBCException.class)
@Test(expected = JDBCException.class)
public void testWithoutIntegrator() {
SessionFactory sf = new Configuration().addAnnotatedClass( Investor.class )
.setProperty( "hibernate.hbm2ddl.auto", "create-drop" )

View File

@ -132,37 +132,4 @@ public class QuoteGlobalTest extends BaseNonConfigCoreFunctionalTestCase {
return new String[] { "quote/DataPoint.hbm.xml" };
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// this really should be in a different test class
@Test
@TestForIssue(jiraKey = "HHH-7927")
public void testTableGeneratorQuoting() {
final Metadata metadata = new MetadataSources( serviceRegistry() ).addAnnotatedClass( TestEntity.class ).buildMetadata();
final ConnectionProvider connectionProvider = serviceRegistry().getService( ConnectionProvider.class );
final Target target = new TargetDatabaseImpl( new JdbcConnectionAccessImpl( connectionProvider ) );
final SchemaManagementTool tool = serviceRegistry().getService( SchemaManagementTool.class );
tool.getSchemaDropper( null ).doDrop( metadata, false, target );
tool.getSchemaCreator( null ).doCreation( metadata, false, target );
try {
new SchemaValidator( serviceRegistry(), (MetadataImplementor) metadata ).validate();
}
catch (HibernateException e) {
fail( "The identifier generator table should have validated. " + e.getMessage() );
}
finally {
tool.getSchemaDropper( null ).doDrop( metadata, false, target );
}
}
@Entity
private static class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;
}
}

View File

@ -0,0 +1,104 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.quote;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.HibernateException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.tool.hbm2ddl.SchemaValidator;
import org.hibernate.tool.schema.internal.TargetDatabaseImpl;
import org.hibernate.tool.schema.spi.SchemaManagementTool;
import org.hibernate.tool.schema.spi.Target;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.test.common.JdbcConnectionAccessImpl;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.fail;
/**
* @author Steve Ebersole
*/
public class TableGeneratorQuotingTest extends BaseUnitTestCase {
private StandardServiceRegistry serviceRegistry;
@Before
public void setUp() {
serviceRegistry = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS, "true" )
.build();
}
@After
public void tearDown() {
if ( serviceRegistry != null ) {
StandardServiceRegistryBuilder.destroy( serviceRegistry );
}
}
@Test
@TestForIssue(jiraKey = "HHH-7927")
public void testTableGeneratorQuoting() {
final Metadata metadata = new MetadataSources( serviceRegistry ).addAnnotatedClass( TestEntity.class ).buildMetadata();
final ConnectionProvider connectionProvider = serviceRegistry.getService( ConnectionProvider.class );
final Target target = new TargetDatabaseImpl( new JdbcConnectionAccessImpl( connectionProvider ) );
final SchemaManagementTool tool = serviceRegistry.getService( SchemaManagementTool.class );
tool.getSchemaDropper( null ).doDrop( metadata, false, target );
tool.getSchemaCreator( null ).doCreation( metadata, false, target );
try {
new SchemaValidator( serviceRegistry, (MetadataImplementor) metadata ).validate();
}
catch (HibernateException e) {
fail( "The identifier generator table should have validated. " + e.getMessage() );
}
finally {
tool.getSchemaDropper( null ).doDrop( metadata, false, target );
}
}
@Entity
private static class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;
}
}

View File

@ -166,5 +166,42 @@ public class MigrationTest extends BaseUnitTestCase {
public Integer id;
public String name;
}
@Test
@TestForIssue( jiraKey = "HHH-9550" )
public void testSameTableNameDifferentExplicitSchemas() {
MetadataImplementor metadata = (MetadataImplementor) new MetadataSources( serviceRegistry )
.addAnnotatedClass( CustomerInfo.class )
.addAnnotatedClass( PersonInfo.class )
.buildMetadata();
// export the schema
new SchemaExport( metadata, true ).execute( Target.EXPORT, SchemaExport.Type.CREATE );
try {
// update the schema
new SchemaUpdate( metadata ).execute( Target.EXPORT );
}
finally {
// drop the schema
new SchemaExport( metadata, true ).execute( Target.EXPORT, SchemaExport.Type.DROP );
}
}
@Entity
@Table( name = "PERSON", schema = "CRM" )
public static class CustomerInfo {
@Id
private Integer id;
}
@Entity
@Table( name = "PERSON", schema = "ERP" )
public static class PersonInfo {
@Id
private Integer id;
}
}

View File

@ -23,7 +23,8 @@
hibernate.dialect org.hibernate.dialect.H2Dialect
hibernate.connection.driver_class org.h2.Driver
hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
#hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1
hibernate.connection.username sa
hibernate.connection.pool_size 5

View File

@ -38,7 +38,8 @@ import org.hibernate.testing.DialectCheck;
*/
public class ConnectionProviderBuilder implements DialectCheck {
public static final String DRIVER = "org.h2.Driver";
public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;MVCC=TRUE";
// public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;MVCC=TRUE";
public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1";
public static final String USER = "sa";
public static final String PASS = "";

View File

@ -27,8 +27,8 @@
ext {
junitVersion = '4.11'
// h2Version = '1.4.186'
h2Version = '1.2.145'
// h2Version = '1.2.145'
h2Version = '1.3.176'
bytemanVersion = '2.1.2'
infinispanVersion = '7.1.0.Final'
jnpVersion = '5.0.6.CR1'