diff options
13 files changed, 596 insertions, 165 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractFeatureMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractFeatureMapping.java new file mode 100644 index 0000000000..8aff30db13 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractFeatureMapping.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004-2018 Eike Stepper (Loehne, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.net4j.db.ddl.IDBField; + +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 4.7 + */ +public abstract class AbstractFeatureMapping implements IFeatureMapping2 +{ + private IMappingStrategy mappingStrategy; + + private EStructuralFeature feature; + + private IDBField unsettableField; + + public AbstractFeatureMapping() + { + } + + public final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final void setMappingStrategy(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public final EStructuralFeature getFeature() + { + return feature; + } + + public final void setFeature(EStructuralFeature feature) + { + this.feature = feature; + } + + public final IDBField getUnsettableField() + { + return unsettableField; + } + + public final void setUnsettableField(IDBField unsettableField) + { + this.unsettableField = unsettableField; + } + + public static IDBField getField(IClassMapping2 classMapping, IFeatureMapping featureMapping) + { + if (featureMapping instanceof IFeatureMapping2) + { + return ((IFeatureMapping2)featureMapping).getField(); + } + + if (featureMapping instanceof ITypeMapping) + { + return ((ITypeMapping)featureMapping).getField(); + } + + EStructuralFeature feature = featureMapping.getFeature(); + return classMapping.getListSizeFields().get(feature); + } + + public static IDBField getUnsettableField(IClassMapping2 classMapping, IFeatureMapping featureMapping) + { + if (featureMapping instanceof IFeatureMapping2) + { + return ((IFeatureMapping2)featureMapping).getUnsettableField(); + } + + EStructuralFeature feature = featureMapping.getFeature(); + return classMapping.getUnsettableFields().get(feature); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java index 3e08245b49..58b5e38cee 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java @@ -39,7 +39,7 @@ import java.sql.SQLException; import java.text.MessageFormat; /** - * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common + * This is a default implementation for the {@link ITypeMapping} interface which provides default behavior for all common * types. Implementors should provide a constructor which the factory (see below) can use and implement * {@link #getResultSetValue(ResultSet)}. If needed, {@link #doSetValue(PreparedStatement, int, Object)} can also be * overridden as a counterpart to {@link #getResultSetValue(ResultSet)}. Finally, an implementor should also implement a @@ -51,14 +51,10 @@ import java.text.MessageFormat; * @author Stefan Winkler * @since 4.0 */ -public abstract class AbstractTypeMapping implements ITypeMapping +public abstract class AbstractTypeMapping extends AbstractFeatureMapping implements ITypeMapping { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractTypeMapping.class); - private IMappingStrategy mappingStrategy; - - private EStructuralFeature feature; - private DBType dbType; private IDBField field; @@ -70,24 +66,9 @@ public abstract class AbstractTypeMapping implements ITypeMapping { } - public final IMappingStrategy getMappingStrategy() - { - return mappingStrategy; - } - - public final void setMappingStrategy(IMappingStrategy mappingStrategy) - { - this.mappingStrategy = mappingStrategy; - } - - public final EStructuralFeature getFeature() - { - return feature; - } - - public final void setFeature(EStructuralFeature feature) + public DBType getDBType() { - this.feature = feature; + return dbType; } public final void setDBType(DBType dbType) @@ -95,11 +76,6 @@ public abstract class AbstractTypeMapping implements ITypeMapping this.dbType = dbType; } - public DBType getDBType() - { - return dbType; - } - public final void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision revision) throws SQLException { setValue(stmt, index, getRevisionValue(revision)); @@ -116,13 +92,15 @@ public abstract class AbstractTypeMapping implements ITypeMapping { if (TRACER.isEnabled()) { - TRACER.format("TypeMapping for {0}: converting Revision.NIL to DB-null", feature.getName()); //$NON-NLS-1$ + TRACER.format("TypeMapping for {0}: converting Revision.NIL to DB-null", getFeature().getName()); //$NON-NLS-1$ } stmt.setNull(index, getSqlType()); } else if (value == null) { + EStructuralFeature feature = getFeature(); + if (feature.isMany() || getDefaultValue() == null) { if (TRACER.isEnabled()) @@ -151,7 +129,7 @@ public abstract class AbstractTypeMapping implements ITypeMapping @Deprecated public final void createDBField(IDBTable table) { - createDBField(table, mappingStrategy.getFieldName(feature)); + createDBField(table, getMappingStrategy().getFieldName(getFeature())); } public final void createDBField(IDBTable table, String fieldName) @@ -166,6 +144,14 @@ public abstract class AbstractTypeMapping implements ITypeMapping return field; } + /** + * @since 4.7 + */ + public final void setField(IDBField field) + { + this.field = field; + } + public final void setDBField(IDBTable table, String fieldName) { field = table.getFieldSafe(fieldName); @@ -182,11 +168,11 @@ public abstract class AbstractTypeMapping implements ITypeMapping Object value = getResultSetValue(resultSet); if (resultSet.wasNull()) { - if (feature.isMany()) + if (getFeature().isMany()) { if (TRACER.isEnabled()) { - TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null", feature.getName()); //$NON-NLS-1$ + TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null", getFeature().getName()); //$NON-NLS-1$ } value = null; @@ -198,7 +184,7 @@ public abstract class AbstractTypeMapping implements ITypeMapping if (TRACER.isEnabled()) { TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null, because of default", //$NON-NLS-1$ - feature.getName()); + getFeature().getName()); } value = null; @@ -207,7 +193,7 @@ public abstract class AbstractTypeMapping implements ITypeMapping { if (TRACER.isEnabled()) { - TRACER.format("TypeMapping for {0}: read db.null - setting Revision.NIL", feature.getName()); //$NON-NLS-1$ + TRACER.format("TypeMapping for {0}: read db.null - setting Revision.NIL", getFeature().getName()); //$NON-NLS-1$ } value = CDORevisionData.NIL; @@ -223,12 +209,12 @@ public abstract class AbstractTypeMapping implements ITypeMapping { Object mappedElement = field != null ? field : dbType; return MessageFormat.format("{0}[{1}.{2} --> {3}]", getClass().getSimpleName(), //$NON-NLS-1$ - feature.getEContainingClass().getName(), feature.getName(), mappedElement); + getFeature().getEContainingClass().getName(), getFeature().getName(), mappedElement); } protected Object getDefaultValue() { - return feature.getDefaultValue(); + return getFeature().getDefaultValue(); } protected final Object getRevisionValue(InternalCDORevision revision) @@ -256,7 +242,7 @@ public abstract class AbstractTypeMapping implements ITypeMapping * Returns the SQL type of this TypeMapping. The default implementation considers the type map held by the * {@link MetaDataManager meta-data manager}. Subclasses may override. * - * @return The sql type of this TypeMapping. + * @return The SQL type of this TypeMapping. */ protected int getSqlType() { @@ -265,6 +251,8 @@ public abstract class AbstractTypeMapping implements ITypeMapping protected int getDBLength(DBType type) { + EStructuralFeature feature = getFeature(); + String value = DBAnnotation.COLUMN_LENGTH.getValue(feature); if (value != null) { @@ -278,7 +266,7 @@ public abstract class AbstractTypeMapping implements ITypeMapping } } - IDBAdapter adapter = mappingStrategy.getStore().getDBAdapter(); + IDBAdapter adapter = getMappingStrategy().getStore().getDBAdapter(); return adapter.getFieldLength(type); } diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping2.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping2.java index bd71a2613b..2faa064860 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping2.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping2.java @@ -28,6 +28,8 @@ import java.util.Map; */ public interface IClassMapping2 extends IClassMapping { + public IDBTable getTable(); + public void setTable(IDBTable table); public void initTable(IDBStoreAccessor accessor); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IFeatureMapping2.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IFeatureMapping2.java new file mode 100644 index 0000000000..49eeeb22c0 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IFeatureMapping2.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Eike Stepper (Loehne, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.net4j.db.ddl.IDBField; + +/** + * An extension interface for {@link IFeatureMapping feature mappings}. + * + * @author Eike Stepper + * @since 4.7 + */ +public interface IFeatureMapping2 extends IFeatureMapping +{ + /** + * @return The value field if this feature mapping is an {@link ITypeMapping} or the size field if this feature mapping is an {@link IListMapping}. + */ + public IDBField getField(); + + public void setField(IDBField field); + + public IDBField getUnsettableField(); + + public void setUnsettableField(IDBField unsettableField); +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreMigrator.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreMigrator.java index 48940cd907..fba568afb4 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreMigrator.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreMigrator.java @@ -20,11 +20,13 @@ import org.eclipse.emf.cdo.internal.server.Repository; import org.eclipse.emf.cdo.server.db.IDBStore; import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.mapping.AbstractFeatureMapping; import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; import org.eclipse.emf.cdo.server.db.mapping.IClassMapping2; import org.eclipse.emf.cdo.server.db.mapping.IFeatureMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMapping4; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy.Props; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy3; import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; import org.eclipse.emf.cdo.server.evolution.Renamer; @@ -44,7 +46,6 @@ import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.db.ddl.IDBSchema; import org.eclipse.net4j.db.ddl.IDBTable; import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; -import org.eclipse.net4j.spi.db.ddl.InternalDBNamedElement; import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.collection.Pair; @@ -85,7 +86,9 @@ public class DBStoreMigrator private final IDBStore store; - private final IDBAdapter dbAdapter; + private final IDBAdapter adapter; + + private final IDBConnection connection; private final IMappingStrategy3 mappingStrategy; @@ -99,6 +102,8 @@ public class DBStoreMigrator private final Map<EModelElement, EModelElement> repositoryToOldElements = new HashMap<EModelElement, EModelElement>(); + private final Map<IDBTable, ColumnRenamer> columnRenamers = new HashMap<IDBTable, ColumnRenamer>(); + public DBStoreMigrator(IDBStoreAccessor accessor, MigrationContext context, Release release) { this.accessor = accessor; @@ -106,82 +111,27 @@ public class DBStoreMigrator this.release = release; store = accessor.getStore(); - dbAdapter = store.getDBAdapter(); + adapter = store.getDBAdapter(); + connection = accessor.getDBConnection(); mappingStrategy = (IMappingStrategy3)store.getMappingStrategy(); metaDataManager = store.getMetaDataManager(); repository = (InternalRepository)store.getRepository(); repositoryPackageRegistry = repository.getPackageRegistry(); - - //////////////////////////////////////////////////////////////////////////////////////// - // Build bidirectional mappings between repository and previous release classifiers. - //////////////////////////////////////////////////////////////////////////////////////// - oldRelease = release.getPreviousRelease(); - if (oldRelease != null) - { - for (EPackage oldPackage : oldRelease.getAllPackages()) - { - EPackage repositoryPackage = repositoryPackageRegistry.getEPackage(oldPackage.getNsURI()); - if (repositoryPackage != null) - { - repositoryToOldElements.put(repositoryPackage, oldPackage); - oldToRepositoryElements.put(oldPackage, repositoryPackage); - for (EClassifier oldClassifier : oldPackage.getEClassifiers()) - { - EClassifier repositoryClassifier = repositoryPackage.getEClassifier(oldClassifier.getName()); - if (repositoryClassifier != null) - { - repositoryToOldElements.put(repositoryClassifier, oldClassifier); - oldToRepositoryElements.put(oldClassifier, repositoryClassifier); - - if (oldClassifier instanceof EClass) - { - EClass oldClass = (EClass)oldClassifier; - EClass repositoryClass = (EClass)repositoryClassifier; - - for (EStructuralFeature oldFeature : oldClass.getEStructuralFeatures()) - { - EStructuralFeature repositoryFeature = repositoryClass.getEStructuralFeature(oldFeature.getName()); - if (repositoryFeature != null) - { - repositoryToOldElements.put(repositoryFeature, oldFeature); - oldToRepositoryElements.put(oldFeature, repositoryFeature); - } - } - } - else if (oldClassifier instanceof EEnum) - { - EEnum oldEnum = (EEnum)oldClassifier; - EEnum repositoryEnum = (EEnum)repositoryClassifier; - - for (EEnumLiteral oldLiteral : oldEnum.getELiterals()) - { - EEnumLiteral repositoryLiteral = repositoryEnum.getEEnumLiteral(oldLiteral.getName()); - if (repositoryLiteral != null) - { - repositoryToOldElements.put(repositoryLiteral, oldLiteral); - oldToRepositoryElements.put(oldLiteral, repositoryLiteral); - } - } - } - } - } - } - } - } + mapRepositoryAndOldElements(); + createAllRepositoryClassMappings(); } public void migrate(OMMonitor monitor) { - IDBConnection connection = accessor.getDBConnection(); Statement statement = null; try { statement = connection.createStatement(); - migrate(connection, statement, monitor); + migrate(statement, monitor); } catch (SQLException ex) { @@ -193,7 +143,7 @@ public class DBStoreMigrator } } - public void migrate(IDBConnection connection, Statement statement, OMMonitor monitor) throws SQLException + private void migrate(Statement statement, OMMonitor monitor) throws SQLException { InternalCDOPackageRegistry newPackageRegistry = (InternalCDOPackageRegistry)release.createPackageRegistry(); InternalCDOPackageUnit[] newPackageUnits = newPackageRegistry.getPackageUnits(); @@ -241,7 +191,6 @@ public class DBStoreMigrator Renamer tableRenamer = new TableRenamer(connection); Set<IDBTable> tablesToRemove = new HashSet<IDBTable>(); - int counter = 0; for (ElementChange elementChange : release.getElementChanges(EcorePackage.Literals.ECLASS, ChangeKind.REMOVED)) { EClass oldClass = (EClass)elementChange.getOldElement(); @@ -252,9 +201,7 @@ public class DBStoreMigrator { if (table != null) { - String tempName = "CDO_OLD_" + (++counter); - tableRenamer.addNames(table.getName(), tempName); - ((InternalDBNamedElement)table).setName(tempName); // TODO Remap in schema? + tableRenamer.addName(table.getName()); tablesToRemove.add(table); } @@ -266,10 +213,12 @@ public class DBStoreMigrator //////////////////////////////////////////////////////////////////////////////////////// // Create renaming rules for the mapped tables of renamed classes and features. + // Create renaming rules for the columns that become obsolete after the migration. //////////////////////////////////////////////////////////////////////////////////////// Set<IClassMapping2> addedClassMappings = new HashSet<IClassMapping2>(); Map<CDOID, Pair<CDOID, ? extends EModelElement>> oldToNewMetaIDs = new HashMap<CDOID, Pair<CDOID, ? extends EModelElement>>(); + List<IDBField> fieldsToRemove = new ArrayList<IDBField>(); for (IClassMapping2 newClassMapping : newClassMappings.values()) { @@ -277,7 +226,7 @@ public class DBStoreMigrator EClass oldClass = (EClass)newToOldElements.get(newClass); IClassMapping2 oldClassMapping = oldClass == null ? null : (IClassMapping2)mappingStrategy.getClassMapping(oldClass); - IDBTable table = oldClassMapping == null ? null : oldClassMapping.getDBTables().get(0); + IDBTable table = oldClassMapping == null ? null : oldClassMapping.getTable(); String oldTableName = table == null ? null : mappingStrategy.getTableName(oldClass); String newTableName = mappingStrategy.getTableName(newClass); @@ -291,8 +240,6 @@ public class DBStoreMigrator { if (newClass != null) { - ColumnRenamer columnRenamer = null; - if (table != null) { for (ITypeMapping newValueMapping : newClassMapping.getValueMappings()) @@ -305,13 +252,13 @@ public class DBStoreMigrator if (!newFieldName.equals(oldFieldName)) { - if (columnRenamer == null) - { - columnRenamer = new ColumnRenamer(connection, table); - } - + ColumnRenamer columnRenamer = getColumnRenamer(table); columnRenamer.addNames(oldFieldName, newFieldName); } + else + { + renameChangedColumn(oldClass, oldFeature, newClassMapping, newValueMapping, table, fieldsToRemove); + } monitor.checkCanceled(); } @@ -340,22 +287,19 @@ public class DBStoreMigrator if (!newListSizeFieldName.equals(oldListSizeFieldName)) { - if (columnRenamer == null) - { - columnRenamer = new ColumnRenamer(connection, table); - } - + ColumnRenamer columnRenamer = getColumnRenamer(table); columnRenamer.addNames(oldListSizeFieldName, newListSizeFieldName); } + else + { + renameChangedColumn(oldClass, oldFeature, newClassMapping, newListMapping, table, fieldsToRemove); + } } monitor.checkCanceled(); } - if (columnRenamer != null) - { - columnRenamer.run(); - } + runColumnRenamer(table); CDOID oldMetaID = metaDataManager.getMetaID(oldClass, CDOBranchPoint.UNSPECIFIED_DATE); CDOID newMetaID = metaDataManager.getMetaID(newClass, CDOBranchPoint.UNSPECIFIED_DATE); @@ -391,7 +335,12 @@ public class DBStoreMigrator tableRenamer.run(); //////////////////////////////////////////////////////////////////////////////////////// - // Create table columns for features added to existing classes. + // Create table columns for features added to existing classes, i.e.: + // 1. A value column for a new single-valued feature + // 2. A list size column for a new many-valued feature + // 3. A boolean column for a new unsettable feature + // + // Create table columns for existing features that map to a different column type. //////////////////////////////////////////////////////////////////////////////////////// IDBSchemaTransaction schemaTransaction = null; @@ -409,7 +358,7 @@ public class DBStoreMigrator IClassMapping2 repositoryClassMapping = (IClassMapping2)mappingStrategy.getClassMapping(repositoryClass); if (repositoryClassMapping != null) { - IDBTable table = repositoryClassMapping.getDBTables().get(0); + IDBTable table = repositoryClassMapping.getTable(); if (table != null) { for (IFeatureMapping newFeatureMapping : newClassMapping.getFeatureMappings()) @@ -418,16 +367,40 @@ public class DBStoreMigrator EStructuralFeature oldFeature = (EStructuralFeature)newToOldElements.get(newFeature); if (oldFeature == null) { - context.log("New feature " + newClass.getName() + "." + newFeature.getName()); - + // Create table column for the added feature. if (schemaTransaction == null) { schemaTransaction = store.getDatabase().openSchemaTransaction(connection); } - IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); - IDBTable workingTable = workingCopy.getTable(table.getName()); - repositoryClassMapping.createFeatureMappings(workingTable, false, newFeature); + IDBTable workingTable = schemaTransaction.getWorkingCopy().getTable(table.getName()); + IFeatureMapping featureMapping = repositoryClassMapping.createFeatureMappings(workingTable, false, newFeature)[0]; + + IDBField field = AbstractFeatureMapping.getField(repositoryClassMapping, featureMapping); + context.log("Creating column for new feature " + newClass.getName() + "." + newFeature.getName() + " --> " + field.getFullName()); + } + else + { + EStructuralFeature repositoryFeature = (EStructuralFeature)oldToRepositoryElements.get(oldFeature); + IFeatureMapping repositoryFeatureMapping = repositoryClassMapping.getFeatureMapping(repositoryFeature); + + IDBField repositoryField = AbstractFeatureMapping.getField(repositoryClassMapping, repositoryFeatureMapping); + IDBField newField = AbstractFeatureMapping.getField(newClassMapping, newFeatureMapping); + + if (!newField.isAssignableFrom(repositoryField)) + { + // Create table column for the changed feature. + if (schemaTransaction == null) + { + schemaTransaction = store.getDatabase().openSchemaTransaction(connection); + } + + IDBTable workingTable = schemaTransaction.getWorkingCopy().getTable(table.getName()); + IFeatureMapping featureMapping = repositoryClassMapping.createFeatureMappings(workingTable, false, newFeature)[0]; + + IDBField field = AbstractFeatureMapping.getField(repositoryClassMapping, featureMapping); + context.log("Creating column for changed feature " + newClass.getName() + "." + newFeature.getName() + " --> " + field.getFullName()); + } } } } @@ -446,11 +419,19 @@ public class DBStoreMigrator finally { IOUtil.close(schemaTransaction); + schemaTransaction = null; } //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// + context.log("######################"); + context.log("Migrating instances..."); + context.log("######################"); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + if (!tablesToRemove.isEmpty()) { context.log("Dropping obsolete tables:"); @@ -458,7 +439,7 @@ public class DBStoreMigrator for (IDBTable table : tablesToRemove) { context.log(" " + table); - dbAdapter.dropTable(table, statement); + adapter.dropTable(table, statement); monitor.checkCanceled(); } } @@ -466,6 +447,39 @@ public class DBStoreMigrator //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// + if (!fieldsToRemove.isEmpty()) + { + context.log("Dropping obsolete columns:"); + schemaTransaction = store.getDatabase().openSchemaTransaction(connection); + IDBSchema workingSchema = schemaTransaction.getWorkingCopy(); + + try + { + for (IDBField field : fieldsToRemove) + { + context.log(" " + field.getFullName()); + + IDBTable workingTable = workingSchema.getTableSafe(field.getTable().getName()); + IDBField workingField = workingTable.getFieldSafe(field.getName()); + workingField.remove(); + monitor.checkCanceled(); + } + + IDBSchemaDelta schemaDelta = schemaTransaction.getSchemaDelta(); + context.log(DBUtil.dumpToString(schemaDelta)); + + schemaTransaction.commit(); + } + finally + { + IOUtil.close(schemaTransaction); + schemaTransaction = null; + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + if (oldRelease != null) { context.log("Removing old package units from system tables:"); @@ -487,9 +501,7 @@ public class DBStoreMigrator //////////////////////////////////////////////////////////////////////////////////////// context.log("Adding new package units to system tables:"); - for ( - - InternalCDOPackageUnit newPackageUnit : newPackageUnits) + for (InternalCDOPackageUnit newPackageUnit : newPackageUnits) { context.log(" " + newPackageUnit.getID()); } @@ -517,6 +529,125 @@ public class DBStoreMigrator } /** + * Build bidirectional mappings between repository and previous release classifiers. + */ + private void mapRepositoryAndOldElements() + { + if (oldRelease != null) + { + for (EPackage oldPackage : oldRelease.getAllPackages()) + { + EPackage repositoryPackage = repositoryPackageRegistry.getEPackage(oldPackage.getNsURI()); + if (repositoryPackage != null) + { + repositoryToOldElements.put(repositoryPackage, oldPackage); + oldToRepositoryElements.put(oldPackage, repositoryPackage); + + for (EClassifier oldClassifier : oldPackage.getEClassifiers()) + { + EClassifier repositoryClassifier = repositoryPackage.getEClassifier(oldClassifier.getName()); + if (repositoryClassifier != null) + { + repositoryToOldElements.put(repositoryClassifier, oldClassifier); + oldToRepositoryElements.put(oldClassifier, repositoryClassifier); + + if (oldClassifier instanceof EClass) + { + EClass oldClass = (EClass)oldClassifier; + EClass repositoryClass = (EClass)repositoryClassifier; + + for (EStructuralFeature oldFeature : oldClass.getEStructuralFeatures()) + { + EStructuralFeature repositoryFeature = repositoryClass.getEStructuralFeature(oldFeature.getName()); + if (repositoryFeature != null) + { + repositoryToOldElements.put(repositoryFeature, oldFeature); + oldToRepositoryElements.put(oldFeature, repositoryFeature); + } + } + } + else if (oldClassifier instanceof EEnum) + { + EEnum oldEnum = (EEnum)oldClassifier; + EEnum repositoryEnum = (EEnum)repositoryClassifier; + + for (EEnumLiteral oldLiteral : oldEnum.getELiterals()) + { + EEnumLiteral repositoryLiteral = repositoryEnum.getEEnumLiteral(oldLiteral.getName()); + if (repositoryLiteral != null) + { + repositoryToOldElements.put(repositoryLiteral, oldLiteral); + oldToRepositoryElements.put(oldLiteral, repositoryLiteral); + } + } + } + } + } + } + } + } + } + + private void createAllRepositoryClassMappings() + { + Map<String, String> mappingStrategyProperties = mappingStrategy.getProperties(); + String oldEagerTableCreation = mappingStrategyProperties.put(Props.EAGER_TABLE_CREATION, String.valueOf(Boolean.FALSE)); + + try + { + mappingStrategy.getClassMappings(true); + } + finally + { + mappingStrategyProperties.put(Props.EAGER_TABLE_CREATION, oldEagerTableCreation); + } + } + + private ColumnRenamer getColumnRenamer(IDBTable table) + { + ColumnRenamer columnRenamer = columnRenamers.get(table); + if (columnRenamer == null) + { + columnRenamer = new ColumnRenamer(connection, table); + columnRenamers.put(table, columnRenamer); + } + + return columnRenamer; + } + + private ColumnRenamer runColumnRenamer(IDBTable table) + { + ColumnRenamer columnRenamer = columnRenamers.get(table); + if (columnRenamer != null) + { + columnRenamer.run(); + } + + return columnRenamer; + } + + private void renameChangedColumn(EClass oldClass, EStructuralFeature oldFeature, IClassMapping2 newClassMapping, IFeatureMapping newFeatureMapping, + IDBTable table, List<IDBField> columnsToDelete) + { + EClass repositoryClass = (EClass)oldToRepositoryElements.get(oldClass); + IClassMapping2 repositoryClassMapping = (IClassMapping2)mappingStrategy.getClassMapping(repositoryClass); + + EStructuralFeature repositoryFeature = (EStructuralFeature)oldToRepositoryElements.get(oldFeature); + IFeatureMapping repositoryFeatureMapping = repositoryClassMapping.getFeatureMapping(repositoryFeature); + + IDBField repositoryField = AbstractFeatureMapping.getField(repositoryClassMapping, repositoryFeatureMapping); + IDBField newField = AbstractFeatureMapping.getField(newClassMapping, newFeatureMapping); + + if (!newField.isAssignableFrom(repositoryField)) + { + ColumnRenamer columnRenamer = getColumnRenamer(table); + columnRenamer.addName(repositoryField.getName()); + + columnsToDelete.add(repositoryField); + } + } + + /** * @author Eike Stepper */ public abstract class LoggingRenamer extends Renamer @@ -645,7 +776,11 @@ public class DBStoreMigrator public TableRenamer(IDBConnection connection) { super(connection, "Renaming tables:"); + } + @Override + protected void initNames() + { // Initialize the renamer with all existing table names. for (IDBTable table : accessor.getStore().getDBSchema().getTables()) { @@ -656,7 +791,7 @@ public class DBStoreMigrator @Override protected String getSQL(String oldName, String newName) { - return dbAdapter.sqlRenameTable(newName, oldName); + return adapter.sqlRenameTable(newName, oldName); } @Override @@ -690,7 +825,11 @@ public class DBStoreMigrator { super(connection, "Renaming columns of table " + table + ":"); this.table = table; + } + @Override + protected void initNames() + { // Initialize the renamer with all existing column names. for (IDBField field : table.getFields()) { @@ -708,7 +847,7 @@ public class DBStoreMigrator try { - return dbAdapter.sqlRenameField(field, oldName); + return adapter.sqlRenameField(field, oldName); } finally { diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java index 7cf19c5a30..f303e8ca1e 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java @@ -26,6 +26,7 @@ import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.AbstractFeatureMapping; import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMapping4; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; @@ -36,6 +37,7 @@ import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy; import org.eclipse.net4j.db.DBException; import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.db.ddl.IDBTable; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -56,24 +58,17 @@ import java.util.Set; /** * @author Stefan Winkler */ -public abstract class AbstractBasicListTableMapping implements IListMapping4, IMappingConstants +public abstract class AbstractBasicListTableMapping extends AbstractFeatureMapping implements IListMapping4, IMappingConstants { - private IMappingStrategy mappingStrategy; - private EClass containingClass; - private EStructuralFeature feature; + private IDBField listSizeField; public AbstractBasicListTableMapping(IMappingStrategy mappingStrategy, EClass containingClass, EStructuralFeature feature) { - this.mappingStrategy = mappingStrategy; + setMappingStrategy(mappingStrategy); + setFeature(feature); this.containingClass = containingClass; - this.feature = feature; - } - - public final IMappingStrategy getMappingStrategy() - { - return mappingStrategy; } public final EClass getContainingClass() @@ -81,11 +76,6 @@ public abstract class AbstractBasicListTableMapping implements IListMapping4, IM return containingClass; } - public final EStructuralFeature getFeature() - { - return feature; - } - public IDBTable getTable() { Iterator<IDBTable> iterator = getDBTables().iterator(); @@ -97,6 +87,16 @@ public abstract class AbstractBasicListTableMapping implements IListMapping4, IM return null; } + public IDBField getField() + { + return listSizeField; + } + + public void setField(IDBField field) + { + listSizeField = field; + } + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index) { builder.append(LIST_IDX); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java index 0f01db5c7f..e75f3b174f 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java @@ -38,6 +38,7 @@ import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; import org.eclipse.emf.cdo.server.db.mapping.IClassMapping2; import org.eclipse.emf.cdo.server.db.mapping.IFeatureMapping; +import org.eclipse.emf.cdo.server.db.mapping.IFeatureMapping2; import org.eclipse.emf.cdo.server.db.mapping.IListMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMapping3; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; @@ -162,7 +163,12 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, addListMapping(listMapping); IDBField listSizeField = table.getField(fieldName); - addListSizeFiled(feature, listSizeField); + addListSizeField(feature, listSizeField); + + if (listMapping instanceof IFeatureMapping2) + { + ((IFeatureMapping2)listMapping).setField(listSizeField); + } } } else @@ -187,6 +193,12 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, String fieldName = mappingStrategy.getUnsettableFieldName(feature); IDBField field = table.getField(fieldName); unsettableFields.put(feature, field); + + IFeatureMapping featureMapping = getFeatureMapping(feature); + if (featureMapping instanceof IFeatureMapping2) + { + ((IFeatureMapping2)featureMapping).setUnsettableField(field); + } } } @@ -281,10 +293,14 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, // Add field for list sizes. IDBField listSizeField = table.addField(fieldName, DBType.INTEGER); - if (updateClassMapping) { - addListSizeFiled(feature, listSizeField); + addListSizeField(feature, listSizeField); + } + + if (listMapping instanceof IFeatureMapping2) + { + ((IFeatureMapping2)listMapping).setField(listSizeField); } } } @@ -324,7 +340,13 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, for (EStructuralFeature feature : unsettableFields.keySet()) { String fieldName = mappingStrategy.getUnsettableFieldName(feature); - table.addField(fieldName, DBType.BOOLEAN); + IDBField field = table.addField(fieldName, DBType.BOOLEAN); + + IFeatureMapping featureMapping = getFeatureMapping(feature); + if (featureMapping instanceof IFeatureMapping2) + { + ((IFeatureMapping2)featureMapping).setUnsettableField(field); + } } if (updateClassMapping) @@ -420,7 +442,7 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, listMappings.add(mapping); } - private void addListSizeFiled(EStructuralFeature feature, IDBField field) + private void addListSizeField(EStructuralFeature feature, IDBField field) { if (listSizeFields == null) { @@ -687,7 +709,7 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping2, return null; } - protected final IDBTable getTable() + public final IDBTable getTable() { return table; } diff --git a/plugins/org.eclipse.emf.cdo.server.evolution/src/org/eclipse/emf/cdo/server/evolution/Renamer.java b/plugins/org.eclipse.emf.cdo.server.evolution/src/org/eclipse/emf/cdo/server/evolution/Renamer.java index d4a5862c9e..c0137baa28 100644 --- a/plugins/org.eclipse.emf.cdo.server.evolution/src/org/eclipse/emf/cdo/server/evolution/Renamer.java +++ b/plugins/org.eclipse.emf.cdo.server.evolution/src/org/eclipse/emf/cdo/server/evolution/Renamer.java @@ -27,6 +27,10 @@ public abstract class Renamer implements Runnable private final Map<String, String> newNames = new HashMap<String, String>(); + private boolean initialized; + + private int counter; + public Renamer() { } @@ -43,6 +47,12 @@ public abstract class Renamer implements Runnable public void addNames(String oldName, String newName) { + if (!initialized) + { + initialized = true; + initNames(); + } + if (oldName != null) { oldNames.put(oldName, newName); @@ -54,10 +64,17 @@ public abstract class Renamer implements Runnable } } + public String addName(String oldName) + { + String newName = createUniqueName(++counter); + addNames(oldName, newName); + return newName; + } + public void run() { List<String> colliding = new ArrayList<String>(); - Set<String> newNames = new HashSet<String>(); + Set<String> names = new HashSet<String>(); for (Map.Entry<String, String> entry : oldNames.entrySet()) { @@ -80,21 +97,21 @@ public abstract class Renamer implements Runnable doRename(oldName, newName); } - newNames.add(newName); + names.add(newName); } } Map<String, String> tempNames = new LinkedHashMap<String, String>(); - int tempCounter = 0; + int tmpCounter = 0; while (!colliding.isEmpty()) { String oldName = colliding.remove(0); String newName = oldNames.get(oldName); - if (newNames.contains(newName) || colliding.contains(newName)) + if (names.contains(newName) || colliding.contains(newName)) { - String tempName = createTempName(++tempCounter); + String tempName = createTmpName(++tmpCounter); tempNames.put(tempName, newName); doRename(oldName, tempName); @@ -111,20 +128,38 @@ public abstract class Renamer implements Runnable String newName = entry.getValue(); doRename(tempName, newName); } + + oldNames.clear(); + newNames.clear(); + initialized = false; + } + + protected void initNames() + { } protected abstract void doRename(String oldName, String newName); - protected String createTempName(int id) + protected String createTmpName(int id) { - return getTempPrefix() + id; + return getTmpPrefix() + id; } - protected String getTempPrefix() + protected String getTmpPrefix() { return "CDO_TMP_"; } + protected String createUniqueName(int id) + { + return getUniquePrefix() + id; + } + + protected String getUniquePrefix() + { + return "CDO_OLD_"; + } + // public static void main(String[] args) // { // Renamer renamer = new Renamer() diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/EvolutionTest.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/EvolutionTest.java index 339e1389d2..9d0e46278e 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/EvolutionTest.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/EvolutionTest.java @@ -596,6 +596,56 @@ public class EvolutionTest extends AbstractCDOTest assertEquals(3, eList(customer2, "sites").size()); } + public void testChangeAttributeType_SingleValued() throws Exception + { + // InternalRepository repository = getRepository(); + // IDBStore store = (IDBStore)repository.getStore(); + // IDBSchema schema = store.getDatabase().getSchema(); + // IMappingStrategy mappingStrategy = store.getMappingStrategy(); + + Model model = createEvolution("evolution/model1.ecore"); + String nsURI = model.getRootPackage().getNsURI(); + Evolution evolution = model.getEvolution(); + + Release v1 = evolution.createRelease(); + migrate(v1); + + CDOSession session0 = openSession(); + CDOTransaction transaction0 = session0.openTransaction(); + CDOResource resource0 = transaction0.getOrCreateResource(getResourcePath("res")); + EObject customer0 = new SessionPackage(nsURI).create("Customer"); + resource0.getContents().add(customer0); + transaction0.commit(); + session0.close(); + + EPackage ePackage = model.getRootPackage(); + EClass addressClass = (EClass)ePackage.getEClassifier("Address"); + addressClass.getEStructuralFeature("city").setEType(EcorePackage.Literals.EINT); + + Release v2 = evolution.createRelease(); + migrate(v2); + + CDOSession session = openSession(); + + CDOPackageUnit packageUnit = session.getPackageRegistry().getPackageUnit(nsURI); + assertNotNull(packageUnit); + + EObject customer = new SessionPackage(nsURI).create("Customer"); + eSet(customer, "city", 32584); + + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getOrCreateResource(getResourcePath("res")); + resource.getContents().add(customer); + transaction.commit(); + CDOID customerID = CDOUtil.getCDOObject(customer).cdoID(); + session.close(); + + CDOSession session2 = openSession(); + CDOTransaction transaction2 = session2.openTransaction(); + EObject customer2 = transaction2.getObject(customerID); + assertEquals(32584, eGet(customer2, "city")); + } + public void _testNewPackageVersionWithSameNSURI() throws Exception { throw new UnsupportedOperationException(); @@ -615,4 +665,34 @@ public class EvolutionTest extends AbstractCDOTest { throw new UnsupportedOperationException(); } + + public void _testChangeAttributeToSingleValued() throws Exception + { + throw new UnsupportedOperationException(); + } + + public void _testChangeAttributeToManyValued() throws Exception + { + throw new UnsupportedOperationException(); + } + + public void _testChangeReferenceToSingleValued() throws Exception + { + throw new UnsupportedOperationException(); + } + + public void _testChangeReferenceToManyValued() throws Exception + { + throw new UnsupportedOperationException(); + } + + public void _testChangeAttributeToReference() throws Exception + { + throw new UnsupportedOperationException(); + } + + public void _testChangeReferenceToAttribute() throws Exception + { + throw new UnsupportedOperationException(); + } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java index 9cc76a2eac..da382a8506 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java @@ -790,6 +790,14 @@ public enum DBType } /** + * @since 4.8 + */ + public boolean isAssignableFrom(DBType other) + { + return equals(other); + } + + /** * @since 3.0 */ public void writeValue(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java index a2add8a0a5..25914bbaa0 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java @@ -56,5 +56,10 @@ public interface IDBField extends IDBSchemaElement, PositionProvider /** * @since 4.8 */ + public boolean isAssignableFrom(IDBField other); + + /** + * @since 4.8 + */ public boolean rename(String newName); } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java index ea0b2d927f..21c44b100d 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java @@ -243,6 +243,31 @@ public class DBField extends DBSchemaElement implements InternalDBField ((InternalDBTable)table).removeField(this); } + public boolean isAssignableFrom(IDBField other) + { + if (!getType().isAssignableFrom(other.getType())) + { + return false; + } + + if (getPrecision() < other.getPrecision()) + { + return false; + } + + if (getScale() < other.getScale()) + { + return false; + } + + if (isNotNull() && !other.isNotNull()) + { + return false; + } + + return true; + } + public String formatPrecision() { int precision = getPrecision(); diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java index c4e808594c..bb216cb79e 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java @@ -107,6 +107,11 @@ public final class DelegatingDBField extends DelegatingDBSchemaElement implement getDelegate().setNotNull(notNull); } + public boolean isAssignableFrom(IDBField other) + { + return getDelegate().isAssignableFrom(unwrap(other)); + } + public String formatPrecision() { return getDelegate().formatPrecision(); |