diff options
Diffstat (limited to 'org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java')
-rw-r--r-- | org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java new file mode 100644 index 0000000000..0f8c57ea80 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java @@ -0,0 +1,590 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +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.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Iterator; + +/** + * This is a featuremap-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta + * support. + * + * @author Eike Stepper + * @since 3.0 + */ +public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport +{ + private FieldInfo[] keyFields; + + private static final int TEMP_INDEX = -1; + + private static final int UNBOUNDED_MOVE = -1; + + private String sqlClear; + + private String sqlUpdateIndex; + + private String sqlUpdateValue; + + private String sqlDeleteItem; + + private String sqlMoveDownWithLimit; + + private String sqlMoveDown; + + private String sqlMoveUpWithLimit; + + private String sqlMoveUp; + + public NonAuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // TODO: add key fields length support + + StringBuilder builder = new StringBuilder(); + + // ----------- clear list ------------------------- + + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + + sqlDeleteItem = builder.toString(); + + // ----------- update one item index -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------- update one item value -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append("=?,"); //$NON-NLS-1$ + + Iterator<String> iter = getColumnNames().iterator(); + while (iter.hasNext()) + { + String column = iter.next(); + builder.append(column); + builder.append("=?"); //$NON-NLS-1$ + + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateValue = builder.toString(); + + // ----------- move down -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("-1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">? "); //$NON-NLS-1$ + sqlMoveDown = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<=?"); //$NON-NLS-1$ + sqlMoveDownWithLimit = builder.toString(); + + // ----------- move up -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("+1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">=? "); //$NON-NLS-1$ + sqlMoveUp = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<?"); //$NON-NLS-1$ + sqlMoveUpWithLimit = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType(); + keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, dbType) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Insert a list item at a specified position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to insert the value + * @param index + * the index where to insert the element + * @param value + * the value to insert. + */ + public void insertListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + move1up(accessor, id, index, UNBOUNDED_MOVE); + insertValue(accessor, id, index, value, timestamp); + } + + private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + String sql = sqlInsert; + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + int column = getKeyFields().length + 1; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move a list item from one position to another. Indices between both positions are updated so that the list remains + * consistent. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision in which to move the item + * @param oldPosition + * the old position of the item. + * @param newPosition + * the new position of the item. + */ + public void moveListItem(IDBStoreAccessor accessor, CDOID id, int oldPosition, int newPosition) + { + if (oldPosition == newPosition) + { + return; + } + + // move element away temporarily + updateOneIndex(accessor, id, oldPosition, TEMP_INDEX); + + // move elements in between + if (oldPosition < newPosition) + { + move1down(accessor, id, oldPosition, newPosition); + } + else + { + // oldPosition > newPosition -- equal case is handled above + move1up(accessor, id, newPosition, oldPosition); + } + + // move temporary element to new position + updateOneIndex(accessor, id, TEMP_INDEX, newPosition); + } + + private void updateOneIndex(IDBStoreAccessor accessor, CDOID id, int oldIndex, int newIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + stmt.setInt(1, newIndex); + idHandler.setCDOID(stmt, 2, id); + stmt.setInt(3, oldIndex); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Remove a list item from a specified a position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove the item + * @param index + * the index of the item to remove + */ + public void removeListItem(IDBStoreAccessor accessor, CDOID id, int index) + { + deleteItem(accessor, id, index); + move1down(accessor, id, index, UNBOUNDED_MOVE); + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit, + ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit, + ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Set a value at a specified position to the given value. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to set the value + * @param index + * the index of the item to set + * @param value + * the value to be set. + */ + public void setListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + ITypeMapping mapping = getTypeMapping(tag); + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, tag); + int column = 2; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + mapping.setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, final long created, CDOListFeatureDelta listDelta) + { + CDOFeatureDeltaVisitor visitor = new CDOFeatureDeltaVisitor() + { + public void visit(CDOMoveFeatureDelta delta) + { + moveListItem(accessor, id, delta.getOldPosition(), delta.getNewPosition()); + } + + public void visit(CDOAddFeatureDelta delta) + { + insertListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDORemoveFeatureDelta delta) + { + removeListItem(accessor, id, delta.getIndex()); + } + + public void visit(CDOSetFeatureDelta delta) + { + setListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + clearList(accessor, id); + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + clearList(accessor, id); + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + }; + + for (CDOFeatureDelta delta : listDelta.getListChanges()) + { + delta.accept(visitor); + } + } +} |