HHH-11647 - Use ALTER TABLE IF EXISTS on Postgres

This commit is contained in:
ChristophDreis 2017-04-19 18:41:49 +02:00 committed by Vlad Mihalcea
parent 72e0d593b9
commit f85737cb0b
14 changed files with 105 additions and 31 deletions

View File

@ -1451,6 +1451,22 @@ public abstract class Dialect implements ConversionContext {
return "create table"; return "create table";
} }
/**
* Command used to alter a table.
*
* @param tableName The name of the table to alter
* @return The command used to alter a table.
* @since 5.2.11
*/
public String getAlterTableString(String tableName) {
final StringBuilder sb = new StringBuilder( "alter table " );
if ( supportsIfExistsAfterAlterTable() ) {
sb.append( "if exists " );
}
sb.append( tableName );
return sb.toString();
}
/** /**
* Slight variation on {@link #getCreateTableString}. Here, we have the * Slight variation on {@link #getCreateTableString}. Here, we have the
* command used to create a table when there is no primary key and * command used to create a table when there is no primary key and
@ -2241,6 +2257,16 @@ public abstract class Dialect implements ConversionContext {
return false; return false;
} }
/**
* For an "alter table", can the phrase "if exists" be applied?
*
* @return {@code true} if the "if exists" can be applied after ALTER TABLE
* @since 5.2.11
*/
public boolean supportsIfExistsAfterAlterTable() {
return false;
}
/** /**
* Generate a DROP TABLE statement * Generate a DROP TABLE statement
* *

View File

@ -22,4 +22,10 @@ public class PostgreSQL92Dialect extends PostgreSQL91Dialect {
super(); super();
this.registerColumnType( Types.JAVA_OBJECT, "json" ); this.registerColumnType( Types.JAVA_OBJECT, "json" );
} }
@Override
public boolean supportsIfExistsAfterAlterTable() {
return true;
}
} }

View File

@ -53,7 +53,8 @@ public class DefaultUniqueDelegate implements UniqueDelegate {
); );
final String constraintName = dialect.quote( uniqueKey.getName() ); final String constraintName = dialect.quote( uniqueKey.getName() );
return "alter table " + tableName + " add constraint " + constraintName + " " + uniqueConstraintSql( uniqueKey ); return dialect.getAlterTableString( tableName )
+ " add constraint " + constraintName + " " + uniqueConstraintSql( uniqueKey );
} }
protected String uniqueConstraintSql(UniqueKey uniqueKey) { protected String uniqueConstraintSql(UniqueKey uniqueKey) {
@ -83,8 +84,7 @@ public class DefaultUniqueDelegate implements UniqueDelegate {
dialect dialect
); );
final StringBuilder buf = new StringBuilder( "alter table " ); final StringBuilder buf = new StringBuilder( dialect.getAlterTableString(tableName) );
buf.append( tableName );
buf.append( getDropUnique() ); buf.append( getDropUnique() );
if ( dialect.supportsIfExistsBeforeConstraintName() ) { if ( dialect.supportsIfExistsBeforeConstraintName() ) {
buf.append( "if exists " ); buf.append( "if exists " );

View File

@ -32,7 +32,8 @@ public class InformixUniqueDelegate extends DefaultUniqueDelegate {
metadata.getDatabase().getJdbcEnvironment().getDialect() metadata.getDatabase().getJdbcEnvironment().getDialect()
); );
final String constraintName = dialect.quote( uniqueKey.getName() ); final String constraintName = dialect.quote( uniqueKey.getName() );
return "alter table " + tableName + " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName; return dialect.getAlterTableString( tableName )
+ " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName;
} }
} }

View File

@ -165,10 +165,11 @@ public abstract class Constraint implements RelationalModel, Exportable, Seriali
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) { public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
if ( isGenerated( dialect ) ) { if ( isGenerated( dialect ) ) {
final String tableName = getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema );
return String.format( return String.format(
Locale.ROOT, Locale.ROOT,
"alter table %s drop constraint %s", "%s drop constraint %s",
getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ), dialect.getAlterTableString( tableName ),
dialect.quote( getName() ) dialect.quote( getName() )
); );
} }
@ -184,8 +185,8 @@ public abstract class Constraint implements RelationalModel, Exportable, Seriali
// empty string. Prevent blank "alter table" statements. // empty string. Prevent blank "alter table" statements.
String constraintString = sqlConstraintString( dialect, getName(), defaultCatalog, defaultSchema ); String constraintString = sqlConstraintString( dialect, getName(), defaultCatalog, defaultSchema );
if ( !StringHelper.isEmpty( constraintString ) ) { if ( !StringHelper.isEmpty( constraintString ) ) {
return "alter table " + getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ) final String tableName = getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema );
+ constraintString; return dialect.getAlterTableString( tableName ) + " " + constraintString;
} }
} }
return null; return null;

View File

@ -173,8 +173,8 @@ public class ForeignKey extends Constraint {
} }
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) { public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
final StringBuilder buf = new StringBuilder( "alter table " ); String tableName = getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema );
buf.append( getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ) ); final StringBuilder buf = new StringBuilder( dialect.getAlterTableString( tableName ) );
buf.append( dialect.getDropForeignKeyString() ); buf.append( dialect.getDropForeignKeyString() );
if ( dialect.supportsIfExistsBeforeConstraintName() ) { if ( dialect.supportsIfExistsBeforeConstraintName() ) {
buf.append( "if exists " ); buf.append( "if exists " );

View File

@ -447,13 +447,12 @@ public class Table implements RelationalModel, Serializable, Exportable {
final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment(); final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment();
StringBuilder root = new StringBuilder( "alter table " ) final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
.append( tableInfo.getName(),
jdbcEnvironment.getQualifiedObjectNameFormatter().format( dialect
tableInfo.getName(), );
dialect
) StringBuilder root = new StringBuilder( dialect.getAlterTableString( tableName ) )
)
.append( ' ' ) .append( ' ' )
.append( dialect.getAddColumnString() ); .append( dialect.getAddColumnString() );

View File

@ -100,8 +100,7 @@ public class StandardForeignKeyExporter implements Exporter<ForeignKey> {
dialect dialect
); );
final StringBuilder buffer = new StringBuilder( "alter table " ) final StringBuilder buffer = new StringBuilder( dialect.getAlterTableString( sourceTableName ) )
.append( sourceTableName )
.append( .append(
foreignKey.getKeyDefinition() != null ? foreignKey.getKeyDefinition() != null ?
dialect.getAddForeignKeyConstraintString( dialect.getAddForeignKeyConstraintString(
@ -146,7 +145,8 @@ public class StandardForeignKeyExporter implements Exporter<ForeignKey> {
dialect dialect
); );
return new String[] { return new String[] {
"alter table " + sourceTableName + dialect.getDropForeignKeyString() + foreignKey.getName() dialect.getAlterTableString( sourceTableName )
+ dialect.getDropForeignKeyString() + foreignKey.getName()
}; };
} }
} }

View File

@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test case for PostgreSQL 9.2 specific things.
*
* @author Christoph Dreis
*/
public class PostgreSQL92DialectTestCase extends BaseUnitTestCase {
/**
* Tests that getAlterTableString() will make use of IF EXISTS syntax
*/
@Test
@TestForIssue( jiraKey = "HHH-11647" )
public void testGetAlterTableString() {
PostgreSQL92Dialect dialect = new PostgreSQL92Dialect();
assertEquals("alter table if exists table_name", dialect.getAlterTableString( "table_name" ));
}
}

View File

@ -161,8 +161,7 @@ public class ForeignKeyGenerationTest extends BaseUnitTestCase {
final List<String> sqlLines = Files.readAllLines( output.toPath(), Charset.defaultCharset() ); final List<String> sqlLines = Files.readAllLines( output.toPath(), Charset.defaultCharset() );
boolean found = false; boolean found = false;
for ( String line : sqlLines ) { for ( String line : sqlLines ) {
if ( line.contains( expectedAlterTableStatement ) ) { if ( line.matches( expectedAlterTableStatement ) ) {
found = true;
return; return;
} }
} }
@ -187,7 +186,7 @@ public class ForeignKeyGenerationTest extends BaseUnitTestCase {
} }
public String toSQL() { public String toSQL() {
return "alter table " + tableName + " add constraint " + fkConstraintName + " foreign key (" + fkColumnName + ") references " + referenceTableName; return "alter table (?:if exists) " + tableName + " add constraint " + fkConstraintName + " foreign key \\(" + fkColumnName + "\\) references " + referenceTableName;
} }
} }

View File

@ -78,8 +78,7 @@ public class JoinedInheritanceForeignKeyTest extends BaseUnitTestCase {
final List<String> sqlLines = Files.readAllLines( output.toPath(), Charset.defaultCharset() ); final List<String> sqlLines = Files.readAllLines( output.toPath(), Charset.defaultCharset() );
boolean found = false; boolean found = false;
for ( String line : sqlLines ) { for ( String line : sqlLines ) {
if ( line.contains( expectedAlterTableStatement ) ) { if ( line.matches( expectedAlterTableStatement ) ) {
found = true;
return; return;
} }
} }
@ -104,7 +103,7 @@ public class JoinedInheritanceForeignKeyTest extends BaseUnitTestCase {
} }
public String toSQL() { public String toSQL() {
return "alter table " + tableName + " add constraint " + fkConstraintName + " foreign key (" + fkColumnName + ") references " + referenceTableName; return "alter table (?:if exists) " + tableName + " add constraint " + fkConstraintName + " foreign key \\(" + fkColumnName + "\\) references " + referenceTableName;
} }
} }

View File

@ -18,6 +18,7 @@ import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.tool.hbm2ddl.SchemaExport; import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType; import org.hibernate.tool.schema.TargetType;
@ -73,7 +74,7 @@ public class SchemaCreationTest {
for ( String statement : sqlLines ) { for ( String statement : sqlLines ) {
assertThat( assertThat(
"Should not try to create the unique constraint for the non existing table element", "Should not try to create the unique constraint for the non existing table element",
statement.toLowerCase().contains( "alter table element" ), statement.toLowerCase().matches( "alter table (?:if exists) element" ),
is( false ) is( false )
); );
if (ssr.getService(JdbcEnvironment.class).getDialect() instanceof DB2Dialect) { if (ssr.getService(JdbcEnvironment.class).getDialect() instanceof DB2Dialect) {
@ -81,7 +82,14 @@ public class SchemaCreationTest {
&& statement.toLowerCase().contains("category (code)")) { && statement.toLowerCase().contains("category (code)")) {
isUniqueConstraintCreated = true; isUniqueConstraintCreated = true;
} }
} else { }
else if (ssr.getService(JdbcEnvironment.class).getDialect() instanceof PostgreSQL81Dialect) {
if (statement.toLowerCase().startsWith("alter table if exists category add constraint")
&& statement.toLowerCase().contains("unique (code)")) {
isUniqueConstraintCreated = true;
}
}
else {
if (statement.toLowerCase().startsWith("alter table category add constraint") if (statement.toLowerCase().startsWith("alter table category add constraint")
&& statement.toLowerCase().contains("unique (code)")) { && statement.toLowerCase().contains("unique (code)")) {
isUniqueConstraintCreated = true; isUniqueConstraintCreated = true;

View File

@ -126,7 +126,7 @@ public class UniqueConstraintDropTest {
private boolean checkDropConstraint(String tableName, String columnName) throws IOException { private boolean checkDropConstraint(String tableName, String columnName) throws IOException {
boolean matches = false; boolean matches = false;
String regex = "alter table " + tableName + " drop constraint"; String regex = "alter table (?:if exists) " + tableName + " drop constraint";
if ( getDialect().supportsIfExistsBeforeConstraintName() ) { if ( getDialect().supportsIfExistsBeforeConstraintName() ) {
regex += " if exists"; regex += " if exists";

View File

@ -19,6 +19,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.tool.hbm2ddl.SchemaExport; import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType; import org.hibernate.tool.schema.TargetType;
@ -70,7 +71,8 @@ public class UniqueConstraintGenerationTest {
isCreateUniqueIndexGenerated("test_entity_item", "item"), isCreateUniqueIndexGenerated("test_entity_item", "item"),
is(true) is(true)
); );
} else { }
else {
assertThat( assertThat(
"The test_entity_item table unique constraint has not been generated", "The test_entity_item table unique constraint has not been generated",
isUniqueConstraintGenerated("test_entity_item", "item"), isUniqueConstraintGenerated("test_entity_item", "item"),
@ -87,7 +89,7 @@ public class UniqueConstraintGenerationTest {
private boolean isUniqueConstraintGenerated(String tableName, String columnName) throws IOException { private boolean isUniqueConstraintGenerated(String tableName, String columnName) throws IOException {
boolean matches = false; boolean matches = false;
final String regex = "alter table " + tableName + " add constraint uk_(.)* unique \\(" + columnName + "\\)"; final String regex = "alter table (?:if exists) " + tableName + " add constraint uk_(.)* unique \\(" + columnName + "\\)";
final String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase(); final String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase();
final String[] split = fileContent.split( System.lineSeparator() ); final String[] split = fileContent.split( System.lineSeparator() );