diff --git a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/DatabaseMetaDataProvider.java b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/DatabaseMetaDataProvider.java index b3c7a9165..0daab2d64 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/DatabaseMetaDataProvider.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/DatabaseMetaDataProvider.java @@ -117,7 +117,6 @@ public abstract class DatabaseMetaDataProvider implements Schema { private final Supplier> databaseInformation = Suppliers.memoize(this::loadDatabaseInformation); - /** * @param connection The database connection from which meta data should be provided. * @param schemaName The name of the schema in which the data is stored. This might be null. @@ -677,7 +676,8 @@ protected Table loadTable(AName tableName) { final Map primaryKey = loadTablePrimaryKey(realTableName); final Supplier> columns = Suppliers.memoize(() -> loadTableColumns(realTableName, primaryKey)); - final Supplier> indexes = Suppliers.memoize(() -> loadTableIndexes(realTableName)); + final Supplier> indexes = Suppliers.memoize(() -> loadTableIndexes(realTableName, false)); + final Supplier> ignoredIndexes = Suppliers.memoize(() -> loadTableIndexes(realTableName, true)); return new Table() { @Override @@ -695,6 +695,11 @@ public List indexes() { return indexes.get(); } + @Override + public List ignoredIndexes() { + return ignoredIndexes.get(); + } + @Override public boolean isTemporary() { return false; @@ -789,10 +794,12 @@ protected static List createColumnsFrom(Collection origin * Loads the indexes for the given table name, except for the primary key index. * * @param tableName Name of the table. + * @param returnIgnored if true return ignored indexes, when false return all the non ignored. * @return List of table indexes. */ - protected List loadTableIndexes(RealName tableName) { + protected List loadTableIndexes(RealName tableName, boolean returnIgnored) { final Map> indexColumns = new HashMap<>(); + final Map> ignoredIndexColumns = new HashMap<>(); final Map indexUniqueness = new HashMap<>(); if (log.isTraceEnabled()) log.trace("Reading table indexes for " + tableName); @@ -811,10 +818,6 @@ protected List loadTableIndexes(RealName tableName) { if (isPrimaryKeyIndex(indexName)) { continue; } - if (DatabaseMetaDataProviderUtils.shouldIgnoreIndex(indexName.getDbName())) { - log.info("Ignoring index: ["+indexName.getDbName()+"]"); - continue; - } String dbColumnName = indexResultSet.getString(INDEX_COLUMN_NAME); String realColumnName = allColumns.get().get(tableName).get(named(dbColumnName)).getName(); @@ -827,8 +830,7 @@ protected List loadTableIndexes(RealName tableName) { indexUniqueness.put(indexName, unique); - indexColumns.computeIfAbsent(indexName, k -> ImmutableList.builder()) - .add(columnName); + appendIndexColumn(indexName, columnName, ignoredIndexColumns, indexColumns); } catch (SQLException e) { throw new RuntimeSqlException("Error reading metadata for index ["+indexName+"] on table ["+tableName+"]", e); @@ -838,9 +840,15 @@ protected List loadTableIndexes(RealName tableName) { long end = System.currentTimeMillis(); if (log.isTraceEnabled()) log.trace(String.format("Read table indexes for %s in %dms; %d indexes; %d unique", tableName, end-start, indexColumns.size(), indexUniqueness.size())); - return indexColumns.entrySet().stream() + if (returnIgnored) { + return ignoredIndexColumns.entrySet().stream() .map(e -> createIndexFrom(e.getKey(), indexUniqueness.get(e.getKey()), e.getValue().build())) .collect(Collectors.toList()); + } else { + return indexColumns.entrySet().stream() + .map(e -> createIndexFrom(e.getKey(), indexUniqueness.get(e.getKey()), e.getValue().build())) + .collect(Collectors.toList()); + } } } catch (SQLException e) { @@ -849,6 +857,19 @@ protected List loadTableIndexes(RealName tableName) { } + private static void appendIndexColumn(RealName indexName, RealName columnName, Map> ignoredIndexColumns, Map> indexColumns) + { + if (DatabaseMetaDataProviderUtils.shouldIgnoreIndex(indexName.getDbName())) { + ignoredIndexColumns.computeIfAbsent(indexName, k -> ImmutableList.builder()) + .add(columnName); + } else { + indexColumns.computeIfAbsent(indexName, k -> ImmutableList.builder()) + .add(columnName); + } + } + + /** * Retrieves index name from a result set. * diff --git a/morf-core/src/main/java/org/alfasoftware/morf/metadata/SchemaUtils.java b/morf-core/src/main/java/org/alfasoftware/morf/metadata/SchemaUtils.java index 8aaa9744a..dcee14b20 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/metadata/SchemaUtils.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/metadata/SchemaUtils.java @@ -517,6 +517,26 @@ public interface TableBuilder extends Table { public TableBuilder indexes(Iterable indexes); + /** + * Sets the indexes for the table. + * + * @param ignoredIndexes The ignored indexes to set, probably provided by calls to + * {@link SchemaUtils#index(String)} + * @return this table builder, for method chaining. + */ + public TableBuilder ignoredIndexes(Index... ignoredIndexes); + + + /** + * Sets the indexes for the table. + * + * @param ignoredIndexes The ignored indexes to set, probably provided by calls to + * {@link SchemaUtils#index(String)} + * @return this table builder, for method chaining. + */ + public TableBuilder ignoredIndexes(Iterable ignoredIndexes); + + /** * Creates a temporary table. * @@ -653,8 +673,9 @@ private TableBuilderImpl(String name) { } - private TableBuilderImpl(String name, Iterable columns, Iterable indexes, boolean isTemporary) { - super(name, columns, indexes, isTemporary); + private TableBuilderImpl(String name, Iterable columns, Iterable indexes, + Iterable ignoredIndexes, boolean isTemporary) { + super(name, columns, indexes, ignoredIndexes, isTemporary); } @@ -672,7 +693,7 @@ public TableBuilder columns(Column... columns) { */ @Override public TableBuilder columns(Iterable columns) { - return new TableBuilderImpl(getName(), columns, indexes(), isTemporary()); + return new TableBuilderImpl(getName(), columns, indexes(), ignoredIndexes(), isTemporary()); } @@ -685,12 +706,30 @@ public TableBuilder indexes(Index... indexes) { } + /** + * @see org.alfasoftware.morf.metadata.SchemaUtils.TableBuilder#ignoredIndexes(java.lang.Iterable) + */ + @Override + public TableBuilder ignoredIndexes(Iterable ignoredIndexes) { + return new TableBuilderImpl(getName(), columns(), indexes(), ignoredIndexes, isTemporary()); + } + + + /** + * @see org.alfasoftware.morf.metadata.SchemaUtils.TableBuilder#ignoredIndexes(org.alfasoftware.morf.metadata.Index[]) + */ + @Override + public TableBuilder ignoredIndexes(Index... ignoredIndexes) { + return ignoredIndexes(Arrays.asList(ignoredIndexes)); + } + + /** * @see org.alfasoftware.morf.metadata.SchemaUtils.TableBuilder#indexes(java.lang.Iterable) */ @Override public TableBuilder indexes(Iterable indexes) { - return new TableBuilderImpl(getName(), columns(), indexes, isTemporary()); + return new TableBuilderImpl(getName(), columns(), indexes, ignoredIndexes(), isTemporary()); } @@ -699,7 +738,7 @@ public TableBuilder indexes(Iterable indexes) { */ @Override public TableBuilder temporary() { - return new TableBuilderImpl(getName(), columns(), indexes(), true); + return new TableBuilderImpl(getName(), columns(), indexes(), ignoredIndexes(), true); } } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/metadata/Table.java b/morf-core/src/main/java/org/alfasoftware/morf/metadata/Table.java index 99d80d462..9bc4e45f7 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/metadata/Table.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/metadata/Table.java @@ -61,6 +61,14 @@ public default List primaryKey() { public List indexes(); + /** + * @return The ignored indexes on this table. + */ + public default List ignoredIndexes() { + return List.of(); + } + + /** * @return Indicates whether the table is temporary */ diff --git a/morf-core/src/main/java/org/alfasoftware/morf/metadata/TableBean.java b/morf-core/src/main/java/org/alfasoftware/morf/metadata/TableBean.java index 8c896267e..22db48597 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/metadata/TableBean.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/metadata/TableBean.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; - import com.google.common.collect.Iterables; @@ -44,6 +43,11 @@ class TableBean implements Table { */ private final List indexes = new ArrayList<>(); + /** + * Stores the ordered list of ignored indexes. + */ + private final List ignoredIndexes = new ArrayList<>(); + /** * Indicates whether the table is temporary. */ @@ -77,13 +81,16 @@ private TableBean(String tableName, boolean isTemporary) { * @param tableName Name of the table to represent. * @param columns Columns for the table * @param indexes indexes for the table; + * @param ignoredIndexes ignored indexes for the table * @param isTemporary Whether the table is a temporary table. */ - TableBean(String tableName, Iterable columns, Iterable indexes, boolean isTemporary) { + TableBean(String tableName, Iterable columns, Iterable indexes, + Iterable ignoredIndexes, boolean isTemporary) { this(tableName, isTemporary); Iterables.addAll(this.columns, columns); Iterables.addAll(this.indexes, indexes); + Iterables.addAll(this.ignoredIndexes, ignoredIndexes); } @@ -106,6 +113,10 @@ private TableBean(String tableName, boolean isTemporary) { for (Index index : toCopy.indexes()) { indexes.add(new IndexBean(index)); } + + for (Index index : toCopy.ignoredIndexes()) { + ignoredIndexes.add(new IndexBean(index)); + } } @@ -136,6 +147,15 @@ public List indexes() { } + /** + * @see org.alfasoftware.morf.metadata.Table#ignoredIndexes() + */ + @Override + public List ignoredIndexes() { + return ignoredIndexes; + } + + /** * Finds a column with the specified name * diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AbstractSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AbstractSchemaChangeVisitor.java new file mode 100644 index 000000000..2764f278d --- /dev/null +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AbstractSchemaChangeVisitor.java @@ -0,0 +1,53 @@ +package org.alfasoftware.morf.upgrade; + +import java.util.Collection; +import java.util.List; + +import org.alfasoftware.morf.jdbc.SqlDialect; +import org.alfasoftware.morf.metadata.Index; +import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; +import org.alfasoftware.morf.metadata.Table; + +/** + * Common code between SchemaChangeVisitor implementors + */ +public abstract class AbstractSchemaChangeVisitor implements SchemaChangeVisitor { + + protected Schema sourceSchema; + protected SqlDialect sqlDialect; + protected final SchemaResource schemaResource; + + protected abstract void writeStatements(Collection statements); + + + public AbstractSchemaChangeVisitor(Schema sourceSchema, SchemaResource schemaResource, SqlDialect sqlDialect) { + this.sourceSchema = sourceSchema; + this.schemaResource = schemaResource; + this.sqlDialect = sqlDialect; + } + + + @Override + public void visit(AddIndex addIndex) { + sourceSchema = addIndex.apply(sourceSchema); + Index foundIndex = null; + Table table = schemaResource.getTable(addIndex.getTableName()); + if (!table.ignoredIndexes().isEmpty()) { + List tableIgnoredIndexes = table.ignoredIndexes(); + for (Index index : tableIgnoredIndexes) { + if (index.columnNames().equals(addIndex.getNewIndex().columnNames())) { + foundIndex = index; + break; + } + } + } + + if (foundIndex != null) { + writeStatements(sqlDialect.renameIndexStatements(sourceSchema.getTable(addIndex.getTableName()), foundIndex.getName(), addIndex.getNewIndex().getName())); + } else { + writeStatements(sqlDialect.addIndexStatements(sourceSchema.getTable(addIndex.getTableName()), addIndex.getNewIndex())); + } + } + +} diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeBuilder.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeBuilder.java index 9691e6b31..af467750f 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeBuilder.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeBuilder.java @@ -12,6 +12,7 @@ import org.alfasoftware.morf.jdbc.ConnectionResources; import org.alfasoftware.morf.jdbc.SqlDialect; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.upgrade.GraphBasedUpgradeSchemaChangeVisitor.GraphBasedUpgradeSchemaChangeVisitorFactory; import org.alfasoftware.morf.upgrade.GraphBasedUpgradeScriptGenerator.GraphBasedUpgradeScriptGeneratorFactory; @@ -97,20 +98,24 @@ public GraphBasedUpgrade prepareGraphBasedUpgrade(List initialisationSql GraphBasedUpgradeNode root = prepareGraph(nodes); + List preUpgStatements; + List postUpgStatements; Table idTable = SqlDialect.IdTable.withPrefix(connectionResources.sqlDialect(), "temp_id_", false); + try (SchemaResource schemaResource = connectionResources.openSchemaResource()) { + GraphBasedUpgradeSchemaChangeVisitor visitor = visitorFactory.create( + sourceSchema, + schemaResource, + connectionResources.sqlDialect(), + idTable, + nodes.stream().collect(Collectors.toMap(GraphBasedUpgradeNode::getName, Function.identity()))); - GraphBasedUpgradeSchemaChangeVisitor visitor = visitorFactory.create( - sourceSchema, - connectionResources.sqlDialect(), - idTable, - nodes.stream().collect(Collectors.toMap(GraphBasedUpgradeNode::getName, Function.identity()))); - - GraphBasedUpgradeScriptGenerator scriptGenerator = scriptGeneratorFactory.create(sourceSchema, targetSchema, connectionResources, idTable, viewChanges, initialisationSql); + GraphBasedUpgradeScriptGenerator scriptGenerator = scriptGeneratorFactory.create(sourceSchema, targetSchema, connectionResources, idTable, viewChanges, initialisationSql); - List preUpgStatements = scriptGenerator.generatePreUpgradeStatements(); - schemaChangeSequence.applyTo(visitor); - List postUpgStatements = scriptGenerator.generatePostUpgradeStatements(); + preUpgStatements = scriptGenerator.generatePreUpgradeStatements(); + schemaChangeSequence.applyTo(visitor); + postUpgStatements = scriptGenerator.generatePostUpgradeStatements(); + } if (LOG.isDebugEnabled()) { logGraph(root); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java index 78709e01c..2e4f1c1c9 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java @@ -6,6 +6,7 @@ import org.alfasoftware.morf.jdbc.SqlDialect; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.sql.Statement; @@ -16,10 +17,8 @@ * * @author Copyright (c) Alfa Financial Software Limited. 2022 */ -class GraphBasedUpgradeSchemaChangeVisitor implements SchemaChangeVisitor { +class GraphBasedUpgradeSchemaChangeVisitor extends AbstractSchemaChangeVisitor implements SchemaChangeVisitor { - private Schema sourceSchema; - private final SqlDialect sqlDialect; private final Table idTable; private final TableNameResolver tracker; private final Map upgradeNodes; @@ -30,12 +29,14 @@ class GraphBasedUpgradeSchemaChangeVisitor implements SchemaChangeVisitor { * Default constructor. * * @param sourceSchema schema prior to upgrade step. + * @param schemaResource schema resource * @param sqlDialect dialect to generate statements for the target database. * @param idTable table for id generation. * @param upgradeNodes all the {@link GraphBasedUpgradeNode} instances in the * upgrade for which the visitor will generate statements */ - GraphBasedUpgradeSchemaChangeVisitor(Schema sourceSchema, SqlDialect sqlDialect, Table idTable, Map upgradeNodes) { + GraphBasedUpgradeSchemaChangeVisitor(Schema sourceSchema, SchemaResource schemaResource, SqlDialect sqlDialect, Table idTable, Map upgradeNodes) { + super(sourceSchema, schemaResource, sqlDialect); this.sourceSchema = sourceSchema; this.sqlDialect = sqlDialect; this.idTable = idTable; @@ -47,7 +48,8 @@ class GraphBasedUpgradeSchemaChangeVisitor implements SchemaChangeVisitor { /** * Write statements to the current node */ - private void writeStatements(Collection statements) { + @Override + protected void writeStatements(Collection statements) { currentNode.addAllUpgradeStatements(statements); } @@ -74,13 +76,6 @@ public void visit(RemoveTable removeTable) { } - @Override - public void visit(AddIndex addIndex) { - sourceSchema = addIndex.apply(sourceSchema); - writeStatements(sqlDialect.addIndexStatements(sourceSchema.getTable(addIndex.getTableName()), addIndex.getNewIndex())); - } - - @Override public void visit(AddColumn addColumn) { sourceSchema = addColumn.apply(sourceSchema); @@ -242,15 +237,16 @@ static class GraphBasedUpgradeSchemaChangeVisitorFactory { * Creates {@link GraphBasedUpgradeSchemaChangeVisitor} instance. * * @param sourceSchema schema prior to upgrade step + * @param schemaResource schema resource * @param sqlDialect dialect to generate statements for the target database * @param idTable table for id generation * @param upgradeNodes all the {@link GraphBasedUpgradeNode} instances in the upgrade for * which the visitor will generate statements * @return new {@link GraphBasedUpgradeSchemaChangeVisitor} instance */ - GraphBasedUpgradeSchemaChangeVisitor create(Schema sourceSchema, SqlDialect sqlDialect, Table idTable, - Map upgradeNodes) { - return new GraphBasedUpgradeSchemaChangeVisitor(sourceSchema, sqlDialect, idTable, upgradeNodes); + GraphBasedUpgradeSchemaChangeVisitor create(Schema sourceSchema, SchemaResource schemaResource, SqlDialect sqlDialect, Table idTable, + Map upgradeNodes) { + return new GraphBasedUpgradeSchemaChangeVisitor(sourceSchema, schemaResource, sqlDialect, idTable, upgradeNodes); } } } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java index 0f4d9a96a..754f20416 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java @@ -21,6 +21,7 @@ import org.alfasoftware.morf.jdbc.SqlDialect; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.sql.Statement; @@ -29,10 +30,9 @@ * * @author Copyright (c) Alfa Financial Software 2010 */ -public class InlineTableUpgrader implements SchemaChangeVisitor { +public class InlineTableUpgrader extends AbstractSchemaChangeVisitor implements SchemaChangeVisitor { private Schema currentSchema; - private final SqlDialect sqlDialect; private final SqlStatementWriter sqlStatementWriter; private final Table idTable; private final TableNameResolver tracker; @@ -42,11 +42,13 @@ public class InlineTableUpgrader implements SchemaChangeVisitor { * Default constructor. * * @param startSchema schema prior to upgrade step. + * @param schemaResource schema resource * @param sqlDialect Dialect to generate statements for the target database. * @param sqlStatementWriter recipient for all upgrade SQL statements. * @param idTable table for id generation. */ - public InlineTableUpgrader(Schema startSchema, SqlDialect sqlDialect, SqlStatementWriter sqlStatementWriter, Table idTable) { + public InlineTableUpgrader(Schema startSchema, SchemaResource schemaResource, SqlDialect sqlDialect, SqlStatementWriter sqlStatementWriter, Table idTable) { + super(startSchema, schemaResource, sqlDialect); this.currentSchema = startSchema; this.sqlDialect = sqlDialect; this.sqlStatementWriter = sqlStatementWriter; @@ -93,16 +95,6 @@ public void visit(RemoveTable removeTable) { } - /** - * @see org.alfasoftware.morf.upgrade.SchemaChangeVisitor#visit(org.alfasoftware.morf.upgrade.AddIndex) - */ - @Override - public void visit(AddIndex addIndex) { - currentSchema = addIndex.apply(currentSchema); - writeStatements(sqlDialect.addIndexStatements(currentSchema.getTable(addIndex.getTableName()), addIndex.getNewIndex())); - } - - /** * @see org.alfasoftware.morf.upgrade.SchemaChangeVisitor#visit(org.alfasoftware.morf.upgrade.AddColumn) */ @@ -226,7 +218,8 @@ private void visitStatement(Statement statement) { /** * Write out SQL */ - private void writeStatements(Collection statements) { + @Override + protected void writeStatements(Collection statements) { sqlStatementWriter.writeSql(statements); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/Upgrade.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/Upgrade.java index 068b28805..209f8d8cc 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/Upgrade.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/Upgrade.java @@ -249,15 +249,17 @@ else if (upgradeAuditCount != getUpgradeAuditRowCount(upgradeAuditRowProcessor)) // if (!schemaChangeSequence.getUpgradeSteps().isEmpty()) { // Run the upgrader over all the ElementarySchemaChanges in the upgrade steps - InlineTableUpgrader upgrader = new InlineTableUpgrader(sourceSchema, dialect, new SqlStatementWriter() { - @Override - public void writeSql(Collection sql) { - upgradeStatements.addAll(sql); - } - }, SqlDialect.IdTable.withPrefix(dialect, "temp_id_")); - upgrader.preUpgrade(); - schemaChangeSequence.applyTo(upgrader); - upgrader.postUpgrade(); + try (SchemaResource schemaResource = connectionResources.openSchemaResource(dataSource)) { + InlineTableUpgrader upgrader = new InlineTableUpgrader(sourceSchema, schemaResource, dialect, new SqlStatementWriter() { + @Override + public void writeSql(Collection sql) { + upgradeStatements.addAll(sql); + } + }, SqlDialect.IdTable.withPrefix(dialect, "temp_id_")); + upgrader.preUpgrade(); + schemaChangeSequence.applyTo(upgrader); + upgrader.postUpgrade(); + } } // -- Upgrade path... diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestGraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestGraphBasedUpgradeSchemaChangeVisitor.java index c64d49592..0772562d4 100644 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestGraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestGraphBasedUpgradeSchemaChangeVisitor.java @@ -6,11 +6,13 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,6 +23,7 @@ import org.alfasoftware.morf.metadata.Column; import org.alfasoftware.morf.metadata.Index; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; import org.alfasoftware.morf.metadata.Sequence; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.sql.SelectStatement; @@ -55,6 +58,9 @@ public class TestGraphBasedUpgradeSchemaChangeVisitor { @Mock private GraphBasedUpgradeNode n1, n2; + @Mock + SchemaResource schemaResource; + private final static List STATEMENTS = Lists.newArrayList("a", "b"); private Map nodes; @@ -70,7 +76,7 @@ public void setup() { nodes.put(U1.class.getName(), n1); nodes.put(U2.class.getName(), n2); - visitor = new GraphBasedUpgradeSchemaChangeVisitor(sourceSchema, sqlDialect, idTable, nodes); + visitor = new GraphBasedUpgradeSchemaChangeVisitor(sourceSchema, schemaResource, sqlDialect, idTable, nodes); } @@ -112,9 +118,14 @@ public void testRemoveTableVisit() { public void testAddIndexVisit() { // given visitor.startStep(U1.class); + String idTableName = "IdTableName"; AddIndex addIndex = mock(AddIndex.class); when(addIndex.apply(sourceSchema)).thenReturn(sourceSchema); + when(addIndex.getTableName()).thenReturn(idTableName); when(sqlDialect.addIndexStatements(nullable(Table.class), nullable(Index.class))).thenReturn(STATEMENTS); + when(idTable.getName()).thenReturn(idTableName); + when(idTable.ignoredIndexes()).thenReturn(Collections.emptyList()); + when(schemaResource.getTable(idTableName)).thenReturn(idTable); // when visitor.visit(addIndex); @@ -125,6 +136,71 @@ public void testAddIndexVisit() { } + /** + * Test method for {@link org.alfasoftware.morf.upgrade.InlineTableUpgrader#visit(org.alfasoftware.morf.upgrade.AddIndex)}. + */ + @Test + public void testVisitAddIndexWithPRFIndex() { + // given + visitor.startStep(U1.class); + + String idTableName = "IdTableName"; + Index newIndex = mock(Index.class); + when(newIndex.getName()).thenReturn(idTableName + "_1"); + when(newIndex.columnNames()).thenReturn(Collections.singletonList("column_1")); + + AddIndex addIndex = mock(AddIndex.class); + given(addIndex.apply(sourceSchema)).willReturn(sourceSchema); + when(addIndex.getTableName()).thenReturn(idTableName); + when(addIndex.getNewIndex()).thenReturn(newIndex); + + Index indexPrf = mock(Index.class); + when(indexPrf.getName()).thenReturn(idTableName + "_PRF1"); + when(indexPrf.columnNames()).thenReturn(List.of("column_1")); + + Index indexPrf1 = mock(Index.class); + when(indexPrf1.getName()).thenReturn(idTableName + "_PRF2"); + when(indexPrf1.columnNames()).thenReturn(List.of("column_2")); + + Table newTable = mock(Table.class); + when(newTable.getName()).thenReturn(idTableName); + when(newTable.ignoredIndexes()).thenReturn(Lists.newArrayList(indexPrf, indexPrf1)); + when(schemaResource.getTable(idTableName)).thenReturn(newTable); + + when(sqlDialect.renameIndexStatements(nullable(Table.class), eq(idTableName + "_PRF1"), eq(idTableName + "_1"))).thenReturn(STATEMENTS); + when(sqlDialect.renameIndexStatements(nullable(Table.class), eq(idTableName + "_PRF2"), eq(idTableName + "_2"))).thenReturn(STATEMENTS); + when(sqlDialect.addIndexStatements(nullable(Table.class), nullable(Index.class))).thenReturn(STATEMENTS); + + Index newIndex1 = mock(Index.class); + when(newIndex1.getName()).thenReturn(idTableName + "_2"); + when(newIndex1.columnNames()).thenReturn(Collections.singletonList("column_2")); + AddIndex addIndex1 = mock(AddIndex.class); + given(addIndex1.apply(sourceSchema)).willReturn(sourceSchema); + when(addIndex1.getTableName()).thenReturn(idTableName); + when(addIndex1.getNewIndex()).thenReturn(newIndex1); + + Index newIndex2 = mock(Index.class); + when(newIndex2.getName()).thenReturn(idTableName + "_3"); + when(newIndex2.columnNames()).thenReturn(Collections.singletonList("column_3")); + AddIndex addIndex2 = mock(AddIndex.class); + given(addIndex2.apply(sourceSchema)).willReturn(sourceSchema); + when(addIndex2.getTableName()).thenReturn(idTableName); + when(addIndex2.getNewIndex()).thenReturn(newIndex2); + + // when + visitor.visit(addIndex); + visitor.visit(addIndex1); + visitor.visit(addIndex2); + + // then + verify(addIndex).apply(sourceSchema); + verify(sqlDialect).renameIndexStatements(nullable(Table.class), eq(idTableName + "_PRF1"), eq(idTableName + "_1")); + verify(sqlDialect).renameIndexStatements(nullable(Table.class), eq(idTableName + "_PRF2"), eq(idTableName + "_2")); + verify(sqlDialect).addIndexStatements(nullable(Table.class), nullable(Index.class)); + verify(n1, times(3)).addAllUpgradeStatements(ArgumentMatchers.argThat(c-> c.containsAll(STATEMENTS))); + } + + @Test public void testAddColumnVisit() { // given @@ -415,7 +491,7 @@ public void testFactory() { GraphBasedUpgradeSchemaChangeVisitorFactory factory = new GraphBasedUpgradeSchemaChangeVisitorFactory(); // when - GraphBasedUpgradeSchemaChangeVisitor created = factory.create(sourceSchema, sqlDialect, idTable, nodes); + GraphBasedUpgradeSchemaChangeVisitor created = factory.create(sourceSchema, schemaResource, sqlDialect, idTable, nodes); // then assertNotNull(created); diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestInlineTableUpgrader.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestInlineTableUpgrader.java index 80563869b..29def1722 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestInlineTableUpgrader.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestInlineTableUpgrader.java @@ -21,21 +21,25 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.BDDMockito.given; import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collections; +import java.util.List; + import org.alfasoftware.morf.jdbc.DatabaseType; import org.alfasoftware.morf.jdbc.SqlDialect; import org.alfasoftware.morf.metadata.Column; import org.alfasoftware.morf.metadata.Index; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; import org.alfasoftware.morf.metadata.Sequence; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.sql.DeleteStatement; @@ -48,6 +52,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import com.google.common.collect.Lists; + /** * */ @@ -59,6 +65,7 @@ public class TestInlineTableUpgrader { private Schema schema; private SqlDialect sqlDialect; private SqlStatementWriter sqlStatementWriter; + private SchemaResource schemaResource; /** * Setup method run before each test. @@ -68,7 +75,8 @@ public void setUp() { schema = mock(Schema.class); sqlDialect = mock(SqlDialect.class); sqlStatementWriter = mock(SqlStatementWriter.class); - upgrader = new InlineTableUpgrader(schema, sqlDialect, sqlStatementWriter, SqlDialect.IdTable.withDeterministicName(ID_TABLE_NAME)); + schemaResource = mock(SchemaResource.class); + upgrader = new InlineTableUpgrader(schema, schemaResource, sqlDialect, sqlStatementWriter, SqlDialect.IdTable.withDeterministicName(ID_TABLE_NAME)); } @@ -147,6 +155,12 @@ public void testVisitAddIndex() { // given AddIndex addIndex = mock(AddIndex.class); given(addIndex.apply(schema)).willReturn(schema); + when(addIndex.getTableName()).thenReturn(ID_TABLE_NAME); + + Table newTable = mock(Table.class); + when(newTable.getName()).thenReturn(ID_TABLE_NAME); + when(newTable.ignoredIndexes()).thenReturn(Lists.newArrayList()); + when(schemaResource.getTable(ID_TABLE_NAME)).thenReturn(newTable); // when upgrader.visit(addIndex); @@ -158,6 +172,65 @@ public void testVisitAddIndex() { } + /** + * Test method for {@link org.alfasoftware.morf.upgrade.InlineTableUpgrader#visit(org.alfasoftware.morf.upgrade.AddIndex)}. + */ + @Test + public void testVisitAddIndexWithPRFIndex() { + // given + Index newIndex = mock(Index.class); + when(newIndex.getName()).thenReturn(ID_TABLE_NAME + "_1"); + when(newIndex.columnNames()).thenReturn(Collections.singletonList("column_1")); + + AddIndex addIndex = mock(AddIndex.class); + given(addIndex.apply(schema)).willReturn(schema); + when(addIndex.getTableName()).thenReturn(ID_TABLE_NAME); + when(addIndex.getNewIndex()).thenReturn(newIndex); + + Index indexPrf = mock(Index.class); + when(indexPrf.getName()).thenReturn(ID_TABLE_NAME + "_PRF1"); + when(indexPrf.columnNames()).thenReturn(List.of("column_1")); + Index indexPrf1 = mock(Index.class); + when(indexPrf1.getName()).thenReturn(ID_TABLE_NAME + "_PRF2"); + when(indexPrf1.columnNames()).thenReturn(List.of("column_2")); + + Table newTable = mock(Table.class); + when(newTable.getName()).thenReturn(ID_TABLE_NAME); + when(newTable.ignoredIndexes()).thenReturn(Lists.newArrayList(indexPrf, indexPrf1)); + when(schemaResource.getTable(ID_TABLE_NAME)).thenReturn(newTable); + + Index newIndex1 = mock(Index.class); + when(newIndex1.getName()).thenReturn(ID_TABLE_NAME + "_2"); + when(newIndex1.columnNames()).thenReturn(Collections.singletonList("column_2")); + AddIndex addIndex1 = mock(AddIndex.class); + given(addIndex1.apply(schema)).willReturn(schema); + when(addIndex1.getTableName()).thenReturn(ID_TABLE_NAME); + when(addIndex1.getNewIndex()).thenReturn(newIndex1); + + Index newIndex2 = mock(Index.class); + when(newIndex2.getName()).thenReturn(ID_TABLE_NAME + "_3"); + when(newIndex2.columnNames()).thenReturn(Collections.singletonList("column_3")); + AddIndex addIndex2 = mock(AddIndex.class); + given(addIndex2.apply(schema)).willReturn(schema); + when(addIndex2.getTableName()).thenReturn(ID_TABLE_NAME); + when(addIndex2.getNewIndex()).thenReturn(newIndex2); + + when(newTable.indexes()).thenReturn(Lists.newArrayList(newIndex1, newIndex2)); + + // when + upgrader.visit(addIndex); + upgrader.visit(addIndex1); + upgrader.visit(addIndex2); + + // then + verify(addIndex).apply(schema); + verify(sqlDialect).renameIndexStatements(nullable(Table.class), eq(ID_TABLE_NAME + "_PRF1"), eq(ID_TABLE_NAME + "_1")); + verify(sqlDialect).renameIndexStatements(nullable(Table.class), eq(ID_TABLE_NAME + "_PRF2"), eq(ID_TABLE_NAME + "_2")); + verify(sqlDialect).addIndexStatements(nullable(Table.class), nullable(Index.class)); + verify(sqlStatementWriter, times(3)).writeSql(anyCollection()); + } + + /** * Test method for {@link org.alfasoftware.morf.upgrade.InlineTableUpgrader#visit(org.alfasoftware.morf.upgrade.AddColumn)}. */ diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestDatabaseUpgradeIntegration.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestDatabaseUpgradeIntegration.java index c569554a0..ce0674b52 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestDatabaseUpgradeIntegration.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestDatabaseUpgradeIntegration.java @@ -49,7 +49,12 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.stream.Collectors; import javax.sql.DataSource; @@ -66,6 +71,7 @@ import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.AddColumnWithoutDropDefaultValue; import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.AddDataToAutonumberedColumn; import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.AddDataToIdColumn; +import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.AddIndex; import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.AddPrimaryKeyColumns; import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.ChangeColumnDataType; import org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0.ChangeColumnLengthAndCase; @@ -103,9 +109,9 @@ import org.alfasoftware.morf.metadata.DataType; import org.alfasoftware.morf.metadata.Schema; import org.alfasoftware.morf.metadata.SchemaHomology; -import org.alfasoftware.morf.metadata.Sequence; import org.alfasoftware.morf.metadata.SchemaHomology.CollectingDifferenceWriter; import org.alfasoftware.morf.metadata.SchemaUtils.TableBuilder; +import org.alfasoftware.morf.metadata.Sequence; import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.metadata.View; import org.alfasoftware.morf.sql.InsertStatement; @@ -114,11 +120,11 @@ import org.alfasoftware.morf.testing.DatabaseSchemaManager; import org.alfasoftware.morf.testing.DatabaseSchemaManager.TruncationBehavior; import org.alfasoftware.morf.testing.TestingDataSourceModule; -import org.alfasoftware.morf.upgrade.ViewDeploymentValidator; import org.alfasoftware.morf.upgrade.LoggingSqlScriptVisitor; import org.alfasoftware.morf.upgrade.Upgrade; import org.alfasoftware.morf.upgrade.UpgradePathFinder.NoUpgradePathExistsException; import org.alfasoftware.morf.upgrade.UpgradeStep; +import org.alfasoftware.morf.upgrade.ViewDeploymentValidator; import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Before; @@ -351,7 +357,24 @@ public void before() { schemaManager.get().dropAllSequences(); schemaManager.get().dropAllViews(); schemaManager.get().dropAllTables(); - schemaManager.get().mutateToSupportSchema(schema, TruncationBehavior.ALWAYS); + + // add the PRF index - it isn't added in shared variable because then it wouldn't be possible to compare that + // schema with the one upgraded as the PRF index is ignored when reading the database definition. + // if left there, the comparison would expect to see a PRF index and it doesn't. + Table tableWithNewAddIndex = table("BasicTableWithIndex") + .columns( + column("stringCol", DataType.STRING, 20).primaryKey(), + column("nullableStringCol", DataType.STRING, 10).nullable(), + column("decimalTenZeroCol", DataType.DECIMAL, 10), + column("decimalNineFiveCol", DataType.DECIMAL, 9, 5), + column("bigIntegerCol", DataType.BIG_INTEGER), + column("nullableBigIntegerCol", DataType.BIG_INTEGER).nullable()) + .indexes( + index("WrongIndexName_1").columns("bigIntegerCol"), + index("BasicTableWithIndex_PRF1").columns("decimalTenZeroCol")); + Schema schema1 = replaceTablesInSchema(tableWithNewAddIndex); + + schemaManager.get().mutateToSupportSchema(schema1, TruncationBehavior.ALWAYS); new DataSetConnector(dataSet, databaseDataSetConsumer.get()).connect(); } @@ -526,6 +549,29 @@ public void testAddPrimaryKeyColumns() { } + /** + * Test that adding primary key columns to a table with no primary key columns work + */ + @Test + public void testAddIndexWithExistingPRFIndex() { + Table tableWithNewAddIndex = table("BasicTableWithIndex") + .columns( + column("stringCol", DataType.STRING, 20).primaryKey(), + column("nullableStringCol", DataType.STRING, 10).nullable(), + column("decimalTenZeroCol", DataType.DECIMAL, 10), + column("decimalNineFiveCol", DataType.DECIMAL, 9, 5), + column("bigIntegerCol", DataType.BIG_INTEGER), + column("nullableBigIntegerCol", DataType.BIG_INTEGER).nullable()) + .indexes( + index("WrongIndexName_1").columns("bigIntegerCol"), + index("BasicTableWithIndex_1").columns("decimalTenZeroCol")); + + Schema reAdded = replaceTablesInSchema(tableWithNewAddIndex); + + verifyUpgrade(reAdded, AddIndex.class); + } + + /** * Test that removing a column from a composite primary key works. * diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/testdatabaseupgradeintegration/upgrade/v1_0_0/AddIndex.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/testdatabaseupgradeintegration/upgrade/v1_0_0/AddIndex.java new file mode 100644 index 000000000..59411bf34 --- /dev/null +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/testdatabaseupgradeintegration/upgrade/v1_0_0/AddIndex.java @@ -0,0 +1,17 @@ +package org.alfasoftware.morf.integration.testdatabaseupgradeintegration.upgrade.v1_0_0; + +import static org.alfasoftware.morf.metadata.SchemaUtils.index; + +import org.alfasoftware.morf.upgrade.DataEditor; +import org.alfasoftware.morf.upgrade.SchemaEditor; +import org.alfasoftware.morf.upgrade.Sequence; +import org.alfasoftware.morf.upgrade.UUID; + +@Sequence(1) +@UUID("1ade56c0-b1d7-11e2-9e96-080020011112") +public class AddIndex extends AbstractTestUpgradeStep { + @Override + public void execute(SchemaEditor schema, DataEditor data) { + schema.addIndex("BasicTableWithIndex", index("BasicTableWithIndex_1").columns("decimalTenZeroCol")); + } +} diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/jdbc/TestDatabaseMetaDataProvider.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/jdbc/TestDatabaseMetaDataProvider.java index 1bf088389..816c2ad8c 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/jdbc/TestDatabaseMetaDataProvider.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/jdbc/TestDatabaseMetaDataProvider.java @@ -16,10 +16,16 @@ package org.alfasoftware.morf.jdbc; import static java.util.stream.Collectors.toList; -import static org.alfasoftware.morf.metadata.SchemaUtils.*; +import static org.alfasoftware.morf.metadata.SchemaUtils.column; +import static org.alfasoftware.morf.metadata.SchemaUtils.index; +import static org.alfasoftware.morf.metadata.SchemaUtils.schema; +import static org.alfasoftware.morf.metadata.SchemaUtils.sequence; +import static org.alfasoftware.morf.metadata.SchemaUtils.table; +import static org.alfasoftware.morf.metadata.SchemaUtils.view; import static org.alfasoftware.morf.sql.SqlUtils.field; import static org.alfasoftware.morf.sql.SqlUtils.select; import static org.alfasoftware.morf.sql.SqlUtils.tableRef; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.either; @@ -28,7 +34,6 @@ import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -37,10 +42,16 @@ import java.util.List; import java.util.function.Function; -import net.jcip.annotations.NotThreadSafe; - import org.alfasoftware.morf.guicesupport.InjectMembersRule; -import org.alfasoftware.morf.metadata.*; +import org.alfasoftware.morf.metadata.Column; +import org.alfasoftware.morf.metadata.DataType; +import org.alfasoftware.morf.metadata.Index; +import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.SchemaResource; +import org.alfasoftware.morf.metadata.SchemaUtils; +import org.alfasoftware.morf.metadata.Sequence; +import org.alfasoftware.morf.metadata.Table; +import org.alfasoftware.morf.metadata.View; import org.alfasoftware.morf.sql.SelectStatement; import org.alfasoftware.morf.testing.DatabaseSchemaManager; import org.alfasoftware.morf.testing.DatabaseSchemaManager.TruncationBehavior; @@ -58,6 +69,8 @@ import com.google.common.collect.Iterables; import com.google.inject.Inject; +import net.jcip.annotations.NotThreadSafe; + /** * Tests for {@link DatabaseMetaDataProvider}. * @@ -102,7 +115,8 @@ public class TestDatabaseMetaDataProvider { column("nullableDateCol", DataType.DATE).nullable()) .indexes( index("WithTypes_1").columns("booleanCol", "dateCol"), - index("NaturalKey").columns("decimalElevenCol").unique()), + index("NaturalKey").columns("decimalElevenCol").unique(), + index("WithTypes_PRF1").columns("decimalNineFiveCol", "bigIntegerCol")), table("WithLobs") .columns( SchemaUtils.autonumber("autonumfield", 17), @@ -268,6 +282,11 @@ public void testTableWithTypes() throws SQLException { indexMatcher( index("NaturalKey").columns("decimalElevenCol").unique())) )); + + assertThat(table.ignoredIndexes(), + containsInAnyOrder(ImmutableList.of( + indexMatcher(index("WithTypes_PRF1").columns("decimalNineFiveCol", "bigIntegerCol")) + ))); } } diff --git a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleMetaDataProvider.java b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleMetaDataProvider.java index 6a437f116..e1a7c4f2b 100755 --- a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleMetaDataProvider.java +++ b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleMetaDataProvider.java @@ -86,7 +86,6 @@ public class OracleMetaDataProvider implements AdditionalMetadata { private final String schemaName; private Map primaryKeyIndexNames; - /** * Construct a new meta data provider. * @@ -343,11 +342,6 @@ public void handle(ResultSet resultSet) throws SQLException { continue; } - if (DatabaseMetaDataProviderUtils.shouldIgnoreIndex(indexName)) { - log.info("Ignoring index: [" + indexName + "]"); - continue; - } - final boolean unique = "UNIQUE".equals(uniqueness); boolean isValid = isValid(status, indexName, indexPartitions); @@ -376,38 +370,48 @@ public void handle(ResultSet resultSet) throws SQLException { final String indexNameFinal = indexName; - currentTable.indexes().add(new Index() { - private final List columnNames = new ArrayList<>(); + if (DatabaseMetaDataProviderUtils.shouldIgnoreIndex(indexName)) { + Index ignoredIndex = getAssembledIndex(unique, indexNameFinal); + currentTable.ignoredIndexes().add(ignoredIndex); + continue; + } - @Override - public boolean isUnique() { - return unique; - } + currentTable.indexes().add(getAssembledIndex(unique, indexNameFinal)); + indexCount++; + } + if (log.isDebugEnabled()) { + log.debug(String.format("Loaded %d indexes", indexCount)); + } + } - @Override - public String getName() { - return indexNameFinal; - } + private Index getAssembledIndex(boolean unique, String indexNameFinal) { + return new Index() { + private final List columnNames = new ArrayList<>(); + @Override + public boolean isUnique() { + return unique; + } - @Override - public List columnNames() { - return columnNames; - } + @Override + public String getName() { + return indexNameFinal; + } - @Override - public String toString() { - return this.toStringHelper(); - } - }); - indexCount++; - } - if (log.isDebugEnabled()) { - log.debug(String.format("Loaded %d indexes", indexCount)); - } + @Override + public List columnNames() { + return columnNames; + } + + + @Override + public String toString() { + return this.toStringHelper(); + } + }; } @@ -447,6 +451,24 @@ public void handle(ResultSet resultSet) throws SQLException { } if (DatabaseMetaDataProviderUtils.shouldIgnoreIndex(indexName)) { + Index lastIndex = null; + for (Index currentIndex : currentTable.ignoredIndexes()) { + if (currentIndex.getName().equalsIgnoreCase(indexName)) { + lastIndex = currentIndex; + break; + } + } + + if (lastIndex == null) { + log.warn(String.format("Ignoring index details for index [%s] on table [%s] as no index definition exists", indexName, tableName)); + continue; + } + + // Correct the case on the column name + columnName = getColumnCorrectCase(currentTable, columnName); + + lastIndex.columnNames().add(columnName); + continue; } @@ -464,16 +486,21 @@ public void handle(ResultSet resultSet) throws SQLException { } // Correct the case on the column name - for (Column currentColumn : currentTable.columns()) { - if (currentColumn.getName().equalsIgnoreCase(columnName)) { - columnName = currentColumn.getName(); - break; - } - } + columnName = getColumnCorrectCase(currentTable, columnName); lastIndex.columnNames().add(columnName); } } + + private String getColumnCorrectCase(Table currentTable, String columnName) { + for (Column currentColumn : currentTable.columns()) { + if (currentColumn.getName().equalsIgnoreCase(columnName)) { + columnName = currentColumn.getName(); + break; + } + } + return columnName; + } }); long end = System.currentTimeMillis(); diff --git a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleMetaDataProvider.java b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleMetaDataProvider.java index 44006da5f..af9aba9f8 100755 --- a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleMetaDataProvider.java +++ b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleMetaDataProvider.java @@ -153,6 +153,80 @@ public void testPrimaryKeyIndexNames() throws SQLException { assertEquals("Primary key index names.", expectedPrimaryKeyIndexNames, oracleMetaDataProvider.primaryKeyIndexNames()); } + /** + * Checks the building of the collection of primary key index names. + * @throws SQLException + */ + @Test + public void testIgnoredIndexes() throws SQLException { + // Given + final PreparedStatement statement = mock(PreparedStatement.class, RETURNS_SMART_NULLS); + when(connection.prepareStatement(anyString())).thenReturn(statement); + + mockGetTableKeysQuery(0, false, false); + // This is the table that's returned. + when(statement.executeQuery()).thenAnswer(invocation -> { + final ResultSet resultSet = mock(ResultSet.class, RETURNS_SMART_NULLS); + when(resultSet.next()).thenReturn(true, false); + + // + when(resultSet.getString(1)).thenReturn("AREALTABLE"); + when(resultSet.getString(2)).thenReturn("TableComment"); + when(resultSet.getString(3)).thenReturn("ID"); + when(resultSet.getString(4)).thenReturn("IDComment"); + when(resultSet.getString(5)).thenReturn("VARCHAR2"); + when(resultSet.getString(6)).thenReturn("10"); + return resultSet; + }).thenAnswer(new ReturnTablesMockResultSet(4)); + + PreparedStatement statement1 = mock(PreparedStatement.class, RETURNS_SMART_NULLS); + when(connection.prepareStatement("select table_name, index_name, uniqueness, status from ALL_INDEXES where owner=? order by table_name, index_name")).thenReturn(statement1); + + // two indexes, one of which is an ignored index + when(statement1.executeQuery()).thenAnswer(answer -> { + ResultSet resultSet = mock(ResultSet.class, RETURNS_SMART_NULLS); + when(resultSet.next()).thenReturn(true, true, true, false); + when(resultSet.getString(1)).thenReturn("AREALTABLE", "AREALTABLE", "AREALTABLE"); + when(resultSet.getString(2)).thenReturn("AREALTABLE_1", "AREALTABLE_PRF1", "AREALTABLE_PRF2"); + when(resultSet.getString(3)).thenReturn("", "", "UNIQUE"); + when(resultSet.getString(4)).thenReturn("VALID", "VALID"); + return resultSet; + }); + + // "select table_name, INDEX_NAME, COLUMN_NAME from ALL_IND_COLUMNS where INDEX_OWNER=? order by table_name, index_name, column_position" + PreparedStatement statement2 = mock(PreparedStatement.class, RETURNS_SMART_NULLS); + when(connection.prepareStatement("select table_name, INDEX_NAME, COLUMN_NAME from ALL_IND_COLUMNS where INDEX_OWNER=? order by table_name, index_name, column_position")).thenReturn(statement2); + + // two indexes, one of which is an ignored index + when(statement2.executeQuery()).thenAnswer(answer -> { + ResultSet resultSet = mock(ResultSet.class, RETURNS_SMART_NULLS); + when(resultSet.next()).thenReturn(true, true, true, false); + when(resultSet.getString(1)).thenReturn("AREALTABLE", "AREALTABLE", "AREALTABLE"); + when(resultSet.getString(2)).thenReturn("AREALTABLE_1", "AREALTABLE_PRF1", "AREALTABLE_PRF2"); + when(resultSet.getString(3)).thenReturn("column1", "column2", "column3"); + return resultSet; + }); + + + // When + final AdditionalMetadata oracleMetaDataProvider = (AdditionalMetadata) oracle.openSchema(connection, "TESTDATABASE", "TESTSCHEMA"); + + List actualIgnoredIndexes = oracleMetaDataProvider.getTable("AREALTABLE").ignoredIndexes(); + assertEquals("Ignored indexes size.", 2, actualIgnoredIndexes.size()); + assertEquals("Ignored AREALTABLE table indexes size.", 2, actualIgnoredIndexes.size()); + Index index = actualIgnoredIndexes.get(0); + assertEquals("Ignored index name.", "AREALTABLE_PRF1", index.getName()); + assertEquals("Ignored index column.", "column2", index.columnNames().get(0)); + assertFalse("Ignored index uniqueness.", index.isUnique()); + assertEquals("Index-AREALTABLE_PRF1--column2", index.toStringHelper()); + + Index index2 = actualIgnoredIndexes.get(1); + assertEquals("Ignored index name.", "AREALTABLE_PRF2", index2.getName()); + assertEquals("Ignored index column.", "column3", index2.columnNames().get(0)); + assertTrue("Ignored index uniqueness.", index2.isUnique()); + assertEquals("Index-AREALTABLE_PRF2-unique-column3", index2.toStringHelper()); + } + @Test public void testIfCatchesWronglyNamedPrimaryKeyIndex() throws SQLException { mockGetTableKeysQuery(1, true, false); diff --git a/morf-testsupport/src/main/java/org/alfasoftware/morf/testing/UpgradeTestHelper.java b/morf-testsupport/src/main/java/org/alfasoftware/morf/testing/UpgradeTestHelper.java index 712aab55d..3f08d5827 100755 --- a/morf-testsupport/src/main/java/org/alfasoftware/morf/testing/UpgradeTestHelper.java +++ b/morf-testsupport/src/main/java/org/alfasoftware/morf/testing/UpgradeTestHelper.java @@ -117,7 +117,8 @@ public void testUpgrades(Schema finalSchema, Iterable sqlScript = Lists.newLinkedList(); // Upgrader, which captures the SQL as a script - InlineTableUpgrader inlineTableUpgrader = new InlineTableUpgrader(fromSchema, connectionResources.sqlDialect(), new SqlStatementWriter() { + InlineTableUpgrader inlineTableUpgrader = new InlineTableUpgrader(fromSchema, + connectionResources.openSchemaResource(), connectionResources.sqlDialect(), new SqlStatementWriter() { @Override public void writeSql(Collection sql) { sqlScript.addAll(sql);