Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Winkler2009-07-27 13:35:59 +0000
committerStefan Winkler2009-07-27 13:35:59 +0000
commitfb197e566f20eed70274be006d985711c77bf7b7 (patch)
tree0c462e9ac98f437a691fdb0913521119c1730779
parente063f13b8575e584c2f9efd698af34d0bd6f7360 (diff)
downloadcdo-fb197e566f20eed70274be006d985711c77bf7b7.tar.gz
cdo-fb197e566f20eed70274be006d985711c77bf7b7.tar.xz
cdo-fb197e566f20eed70274be006d985711c77bf7b7.zip
[254455] [DB] Support FeatureMaps
https://bugs.eclipse.org/bugs/show_bug.cgi?id=254455
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java19
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java13
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java10
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMapping.java7
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingFactory.java13
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java716
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java10
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java63
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java569
-rw-r--r--plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 non-audit).launch20
-rw-r--r--plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2NonAudit.java222
-rw-r--r--plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/DBConfigs.java4
14 files changed, 1670 insertions, 8 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
index 1930e2db23..736d5d1761 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
@@ -8,11 +8,13 @@
* Contributors:
* Eike Stepper - initial API and implementation
* Stefan Winkler - major refactoring
+ * Christopher Albert - 254455: [DB] Support FeatureMaps https://bugs.eclipse.org/bugs/show_bug.cgi?id=254455
*/
package org.eclipse.emf.cdo.server.db.mapping;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.net4j.db.DBType;
import org.eclipse.net4j.db.ddl.IDBField;
import org.eclipse.net4j.db.ddl.IDBTable;
@@ -42,6 +44,12 @@ public interface ITypeMapping
public IDBField getField();
/**
+ * @return The db type which is associated with this mapping.
+ * @since 3.0
+ */
+ public DBType getDBType();
+
+ /**
* Creates the DBField and adds it to the given table. The name of the DBField is derived from the feature.
*
* @param table
@@ -61,6 +69,17 @@ public interface ITypeMapping
public void createDBField(IDBTable table, String fieldName);
/**
+ * Sets the DBField. The name of the DBField is explicitly determined by the corresponding parameter.
+ *
+ * @param table
+ * the table to add this field to.
+ * @param fieldName
+ * the name for the DBField.
+ * @since 3.0
+ */
+ public void setDBField(IDBTable table, String fieldName);
+
+ /**
* Set the given value to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code> method.
*
* @param stmt
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
index 07b8b3e9aa..129e1e4f48 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
@@ -152,6 +152,19 @@ public class CDODBSchema extends DBSchema
public static final String LIST_VALUE = "cdo_value"; //$NON-NLS-1$
+ /**
+ * Field names of featuremap tables
+ */
+ public static final String FEATUREMAP_REVISION_ID = "cdo_id"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_IDX = "cdo_idx"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_TAG = "cdo_tag"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VALUE = "cdo_value"; //$NON-NLS-1$
+
private CDODBSchema()
{
super("CDO"); //$NON-NLS-1$
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
index 460bfff42e..7db4fe3363 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
@@ -44,6 +44,7 @@ import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -395,5 +396,14 @@ public abstract class AbstractMappingStrategy extends Lifecycle implements IMapp
return mapping;
}
+ public final IListMapping createFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ checkArg(FeatureMapUtil.isFeatureMap(feature), "Only FeatureMaps allowed."); //$NON-NLS-1$
+ IListMapping mapping = doCreateFeatureMapMapping(containingClass, feature);
+ return mapping;
+ }
+
public abstract IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature);
+
+ public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature);
}
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMapping.java
index afd8e4bea0..fba14c74fe 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMapping.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMapping.java
@@ -112,6 +112,11 @@ public abstract class TypeMapping implements ITypeMapping
field = table.addField(fieldName, fieldType, fieldLength);
}
+ public final void setDBField(IDBTable table, String fieldName)
+ {
+ field = table.getField(fieldName);
+ }
+
public final IDBField getField()
{
return field;
@@ -156,7 +161,7 @@ public abstract class TypeMapping implements ITypeMapping
return getDBType().getCode();
}
- protected DBType getDBType()
+ public DBType getDBType()
{
return dbType;
}
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingFactory.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingFactory.java
index 4f47e9e6c3..5f0bdd7608 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingFactory.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingFactory.java
@@ -26,8 +26,11 @@ import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
/**
* @author Stefan Winkler
@@ -173,6 +176,8 @@ public enum TypeMappingFactory
private static Map<Pair<CDOType, DBType>, TypeMappingFactory> mappingTable = new HashMap<Pair<CDOType, DBType>, TypeMappingFactory>();
+ private static Set<DBType> defaultFeatureMapDBTypes;
+
static
{
/* --- initialize default types --- */
@@ -237,8 +242,7 @@ public enum TypeMappingFactory
mappingTable.put(new Pair<CDOType, DBType>(CDOType.STRING, DBType.VARCHAR), STRING_MAPPING);
mappingTable.put(new Pair<CDOType, DBType>(CDOType.STRING, DBType.CLOB), STRING_MAPPING);
- // preliminary
- mappingTable.put(new Pair<CDOType, DBType>(CDOType.FEATURE_MAP_ENTRY, DBType.VARCHAR), STRING_MAPPING);
+ defaultFeatureMapDBTypes = new HashSet<DBType>(defaultTypeMap.values());
}
protected abstract ITypeMapping doCreateTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature,
@@ -298,4 +302,9 @@ public enum TypeMappingFactory
return DBType.VARCHAR;
}
+
+ public static Collection<DBType> getDefaultFeatureMapDBTypes()
+ {
+ return defaultFeatureMapDBTypes;
+ }
}
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
new file mode 100644
index 0000000000..a9edb800c8
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
@@ -0,0 +1,716 @@
+/**
+ * Copyright (c) 2004 - 2009 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 https://bugs.eclipse.org/bugs/show_bug.cgi?id=271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps https://bugs.eclipse.org/bugs/show_bug.cgi?id=254455
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+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.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingFactory;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+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.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public abstract class AbstractFeatureMapTableMapping implements IListMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractFeatureMapTableMapping.class);
+
+ /**
+ * The feature for this mapping.
+ */
+ private EStructuralFeature feature;
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The tags mapped to column names
+ */
+ private HashMap<Long, String> tagMap;
+
+ /**
+ * Column name Set
+ */
+ private List<String> columnNames;
+
+ /**
+ * The type mappings for the value fields.
+ */
+ private Map<Long, ITypeMapping> typeMappings;
+
+ /**
+ * The associated mapping strategy.
+ */
+ private IMappingStrategy mappingStrategy;
+
+ // --------- SQL strings - see initSqlStrings() -----------------
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ protected String sqlInsert;
+
+ private EClass containingClass;
+
+ private String sqlGetListLastIndex;
+
+ private List<DBType> dbTypes;
+
+ public AbstractFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ this.mappingStrategy = mappingStrategy;
+ this.feature = feature;
+ containingClass = eClass;
+
+ initDBTypes();
+ initTable();
+ initSqlStrings();
+ }
+
+ private void initDBTypes()
+ {
+ // TODO add annotation processing here ...
+
+ dbTypes = new ArrayList<DBType>(TypeMappingFactory.getDefaultFeatureMapDBTypes());
+ }
+
+ private void initTable()
+ {
+ String tableName = mappingStrategy.getTableName(containingClass, feature);
+ table = mappingStrategy.getStore().getDBSchema().addTable(tableName);
+
+ // add fields for keys (cdo_id, version, feature_id)
+ FieldInfo[] fields = getKeyFields();
+ IDBField[] dbFields = new IDBField[fields.length];
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType());
+ }
+
+ // add field for list index
+ IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER);
+
+ // add field for FeatureMap tag (MetaID for Feature in CDO registry)
+ IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, DBType.INTEGER);
+
+ tagMap = new HashMap<Long, String>();
+ typeMappings = new HashMap<Long, ITypeMapping>();
+ columnNames = new ArrayList<String>();
+
+ // create columns for all DBTypes
+ for (DBType type : getDBTypes())
+ {
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name();
+ table.addField(column, type);
+ columnNames.add(column);
+ }
+
+ table.addIndex(Type.NON_UNIQUE, dbFields);
+ table.addIndex(Type.NON_UNIQUE, idxField);
+ table.addIndex(Type.NON_UNIQUE, tagField);
+ }
+
+ protected abstract FieldInfo[] getKeyFields();
+
+ protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException;
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ private void initSqlStrings()
+ {
+ String tableName = getTable().getName();
+ FieldInfo[] fields = getKeyFields();
+
+ // ---------------- SELECT to read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT ");
+
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(", ");
+
+ Iterator<String> iter = columnNames.iterator();
+ while (iter.hasNext())
+ {
+ builder.append(iter.next());
+ if (iter.hasNext())
+ {
+ builder.append(", ");
+ }
+ }
+
+ builder.append(" FROM ");
+ builder.append(tableName);
+ builder.append(" WHERE ");
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ if (i + 1 < fields.length)
+ {
+ // more to come
+ builder.append("= ? AND ");
+ }
+ else
+ {
+ // last one
+ builder.append("= ? ");
+ }
+ }
+
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$
+
+ // ----------------- count list size --------------------------
+
+ builder = new StringBuilder("SELECT max(");
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(") FROM ");
+ builder.append(tableName);
+ builder.append(" WHERE ");
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ if (i + 1 < fields.length)
+ {
+ // more to come
+ builder.append("= ? AND ");
+ }
+ else
+ {
+ // last one
+ builder.append("= ? ");
+ }
+ }
+
+ sqlGetListLastIndex = builder.toString();
+
+ // INSERT with dynamic field name
+ // TODO: Better: universal INSERT-Statement, because of stmt caching!
+
+ // ----------------- INSERT - prefix -----------------
+ builder = new StringBuilder("INSERT INTO ");
+ builder.append(tableName);
+ builder.append(" ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ builder.append(", "); //$NON-NLS-1$
+ }
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ builder.append(columnNames.get(i));
+ builder.append(", "); //$NON-NLS-1$
+ }
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(") VALUES ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length + columnNames.size(); i++)
+ {
+ builder.append("?, ");
+ }
+ builder.append("?, ?)");
+
+ sqlInsert = builder.toString();
+ }
+
+ public final EStructuralFeature getFeature()
+ {
+ return feature;
+ }
+
+ protected List<DBType> getDBTypes()
+ {
+ return dbTypes;
+ }
+
+ public final EClass getContainingClass()
+ {
+ return containingClass;
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final List<String> getColumnNames()
+ {
+ return columnNames;
+ }
+
+ protected final Map<Long, ITypeMapping> getTypeMappings()
+ {
+ return typeMappings;
+ }
+
+ protected final Map<Long, String> getTagMap()
+ {
+ return tagMap;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ MoveableList<Object> list = revision.getList(getFeature());
+ int listSize = -1;
+
+ if (listChunk != CDORevision.UNCHUNKED)
+ {
+ listSize = getListLastIndex(accessor, revision);
+ if (listSize == -1)
+ {
+ // list is empty - take shortcut
+ return;
+ }
+ else
+ {
+ // subtract amount of items we are going to read now
+ listSize -= listChunk;
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", containingClass.getName(), feature.getName(),
+ revision.getID(), revision.getVersion());
+ }
+
+ PreparedStatement pstmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+
+ pstmt = accessor.getStatementCache().getPreparedStatement(sql, ReuseProbability.HIGH);
+
+ setKeyFields(pstmt, revision);
+
+ // if (TRACER.isEnabled())
+ // {
+ // TRACER.trace(pstmt.toString());
+ // }
+
+ if (listChunk != CDORevision.UNCHUNKED)
+ {
+ pstmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
+ }
+
+ resultSet = pstmt.executeQuery();
+
+ while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
+ {
+ Long tag = resultSet.getLong(1);
+ String column = getColumnName(tag);
+ int fieldIndex = resultSet.findColumn(column);
+ Object value = getTypeMapping(tag).readValue(resultSet, fieldIndex);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", list.size(), value);
+ }
+
+ list.add(CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ }
+
+ while (listSize-- >= 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Adding UNINITIALIZED for index {0} ", list.size());
+ }
+
+ list.add(InternalCDOList.UNINITIALIZED);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ accessor.getStatementCache().releasePreparedStatement(pstmt);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", containingClass.getName(), feature //$NON-NLS-1$
+ .getName(), revision.getID(), revision.getVersion());
+ }
+ }
+
+ private void addFeature(Long tag)
+ {
+ EStructuralFeature modelFeature = getFeatureByTag(tag);
+
+ TypeMapping typeMapping = (TypeMapping)mappingStrategy.createValueMapping(modelFeature);
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType();
+
+ tagMap.put(tag, column);
+ typeMapping.setDBField(table, column);
+ typeMappings.put(tag, typeMapping);
+ }
+
+ /**
+ * Return the last (maximum) list index. (euals to size-1)
+ *
+ * @param accessor
+ * the accessor to use
+ * @param revision
+ * the revision to which the feature list belongs
+ * @return the last index or <code>-1</code> if the list is empty.
+ */
+ private int getListLastIndex(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ PreparedStatement pstmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ pstmt = accessor.getStatementCache().getPreparedStatement(sqlGetListLastIndex, ReuseProbability.HIGH);
+
+ setKeyFields(pstmt, revision);
+
+ // if (TRACER.isEnabled())
+ // {
+ // TRACER.trace(pstmt.toString());
+ // }
+
+ resultSet = pstmt.executeQuery();
+
+ if (!resultSet.next())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("No last index found -> list is empty. ");
+ }
+
+ return -1;
+ }
+ else
+ {
+ int result = resultSet.getInt(1);
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Read list last index = " + result);
+ }
+
+ return result;
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ accessor.getStatementCache().releasePreparedStatement(pstmt);
+ }
+ }
+
+ public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", containingClass.getName(), feature //$NON-NLS-1$
+ .getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+
+ PreparedStatement pstmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
+ if (where != null)
+ {
+ builder.append(where);
+ }
+
+ builder.append(sqlOrderByIndex);
+
+ String sql = builder.toString();
+ pstmt = chunkReader.getAccessor().getStatementCache().getPreparedStatement(sql, ReuseProbability.LOW);
+ setKeyFields(pstmt, chunkReader.getRevision());
+ // if (TRACER.isEnabled())
+ // {
+ // TRACER.trace(pstmt.toString());
+ // }
+
+ resultSet = pstmt.executeQuery();
+
+ Chunk chunk = null;
+ int chunkSize = 0;
+ int chunkIndex = 0;
+ int indexInChunk = 0;
+
+ while (resultSet.next())
+ {
+ Long tag = resultSet.getLong(1);
+ String column = getColumnName(tag);
+ int fieldIndex = resultSet.findColumn(column);
+ Object value = getTypeMapping(tag).readValue(resultSet, fieldIndex);
+
+ if (chunk == null)
+ {
+ chunk = chunks.get(chunkIndex++);
+ chunkSize = chunk.size();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(),
+ chunkSize);
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value);
+ }
+
+ chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ if (indexInChunk == chunkSize)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Chunk finished.");
+ }
+
+ chunk = null;
+ indexInChunk = 0;
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}v{3}", containingClass.getName(),
+ getTagByFeature(feature), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ chunkReader.getAccessor().getStatementCache().releasePreparedStatement(pstmt);
+ }
+ }
+
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ CDOList values = revision.getList(getFeature());
+
+ int idx = 0;
+ for (Object element : values)
+ {
+ writeValue(accessor, revision, idx++, element);
+ }
+ }
+
+ protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
+ {
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER
+ .format(
+ "Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", containingClass.getName(), getTagByFeature(feature), idx, revision.getID(), revision.getVersion(), value); //$NON-NLS-1$
+ }
+
+ try
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ Long tag = getTagByFeature(entryFeature);
+ String column = getColumnName(tag);
+
+ String sql = sqlInsert;
+
+ stmt = accessor.getStatementCache().getPreparedStatement(sql, ReuseProbability.HIGH);
+
+ setKeyFields(stmt, revision);
+ int stmtIndex = getKeyFields().length + 1;
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ if (columnNames.get(i).equals(column))
+ {
+ getTypeMapping(tag).setValue(stmt, stmtIndex++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(stmtIndex++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ stmt.setInt(stmtIndex++, idx);
+ stmt.setLong(stmtIndex++, tag);
+
+ CDODBUtil.sqlUpdate(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Get column name (lazy)
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the column name where the values are stored
+ */
+
+ protected String getColumnName(Long tag)
+ {
+ String column = tagMap.get(tag);
+ if (column == null)
+ {
+ addFeature(tag);
+ column = tagMap.get(tag);
+ }
+ return column;
+ }
+
+ /**
+ * Get type mapping (lazy)
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the corresponding type mapping
+ */
+
+ protected ITypeMapping getTypeMapping(Long tag)
+ {
+
+ ITypeMapping typeMapping = typeMappings.get(tag);
+ if (typeMapping == null)
+ {
+ addFeature(tag);
+ typeMapping = typeMappings.get(tag);
+ }
+ return typeMapping;
+ }
+
+ /**
+ * @param metaID
+ * @return the column name where the values are stored
+ */
+
+ private EStructuralFeature getFeatureByTag(Long tag)
+ {
+ return (EStructuralFeature)mappingStrategy.getStore().getMetaDataManager().getMetaInstance(tag);
+ }
+
+ /**
+ * @param feature
+ * The EStructuralFeature
+ * @return The feature's MetaID in CDO
+ */
+
+ protected Long getTagByFeature(EStructuralFeature feature)
+ {
+ return mappingStrategy.getStore().getMetaDataManager().getMetaID(feature);
+ }
+
+ /**
+ * @param metaID
+ * The feature's MetaID in CDO
+ * @return the column name where the values are stored
+ */
+
+ /**
+ * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ...
+ *
+ * @author Stefan Winkler
+ */
+ protected static class FieldInfo
+ {
+ private String name;
+
+ private DBType dbType;
+
+ public FieldInfo(String name, DBType dbType)
+ {
+ this.name = name;
+ this.dbType = dbType;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public DBType getDbType()
+ {
+ return dbType;
+ }
+ }
+}
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 39b3f55e5a..f1b849b810 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
@@ -40,6 +40,7 @@ import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -132,7 +133,14 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping
{
if (feature.isMany())
{
- listMappings.add(mappingStrategy.createListMapping(eClass, feature));
+ if (FeatureMapUtil.isFeatureMap(feature))
+ {
+ listMappings.add(mappingStrategy.createFeatureMapMapping(eClass, feature));
+ }
+ else
+ {
+ listMappings.add(mappingStrategy.createListMapping(eClass, feature));
+ }
}
}
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java
new file mode 100644
index 0000000000..5deaffc375
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2004 - 2009 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 https://bugs.eclipse.org/bugs/show_bug.cgi?id=271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps https://bugs.eclipse.org/bugs/show_bug.cgi?id=254455
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping
+{
+ private static final FieldInfo[] KEY_FIELDS = { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, DBType.BIGINT),
+ new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) };
+
+ public AuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ return KEY_FIELDS;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ stmt.setLong(1, CDOIDUtil.getLong(revision.getID()));
+ stmt.setInt(2, revision.getVersion());
+ }
+
+ public void objectRevised(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ // the audit list mapping does not care about revised references -> NOP
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java
index d062dd0c82..77c90a2999 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java
@@ -36,6 +36,12 @@ public class HorizontalAuditMappingStrategy extends AbstractHorizontalMappingStr
}
@Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new AuditFeatureMapTableMapping(this, containingClass, feature);
+ }
+
+ @Override
public boolean hasAuditSupport()
{
return true;
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java
index 4dbd5bf4b7..93b465671d 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java
@@ -36,6 +36,12 @@ public class HorizontalNonAuditMappingStrategy extends AbstractHorizontalMapping
}
@Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new NonAuditFeatureMapTableMapping(this, containingClass, feature);
+ }
+
+ @Override
public boolean hasAuditSupport()
{
return false;
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java
new file mode 100644
index 0000000000..e571d64d0a
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java
@@ -0,0 +1,569 @@
+/**
+ * Copyright (c) 2004 - 2009 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 https://bugs.eclipse.org/bugs/show_bug.cgi?id=271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps https://bugs.eclipse.org/bugs/show_bug.cgi?id=254455
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+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.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+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.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 IListMapping,
+ IListMappingDeltaSupport
+{
+ private static final FieldInfo[] KEY_FIELDS = { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, DBType.BIGINT) };
+
+ 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().getName());
+ 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().getName());
+ 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().getName());
+ 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().getName());
+ 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().getName());
+ 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()
+ {
+ return KEY_FIELDS;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ stmt.setLong(1, CDOIDUtil.getLong(revision.getID()));
+ }
+
+ public void objectRevised(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)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlClear, ReuseProbability.HIGH);
+ stmt.setLong(1, CDOIDUtil.getLong(id));
+ CDODBUtil.sqlUpdate(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().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)
+ {
+ move1up(accessor, id, index, UNBOUNDED_MOVE);
+ insertValue(accessor, id, index, value);
+ }
+
+ private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ Long tag = getTagByFeature(entryFeature);
+ String column = getColumnName(tag);
+
+ String sql = sqlInsert;
+
+ stmt = accessor.getStatementCache().getPreparedStatement(sql, ReuseProbability.HIGH);
+
+ stmt.setLong(1, CDOIDUtil.getLong(id));
+ int stmtIndex = getKeyFields().length + 1;
+
+ for (int i = 0; i < getColumnNames().size(); i++)
+ {
+ if (getColumnNames().get(i).equals(column))
+ {
+ getTypeMapping(tag).setValue(stmt, stmtIndex++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(stmtIndex++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ stmt.setInt(stmtIndex++, index);
+ stmt.setLong(stmtIndex++, tag);
+
+ CDODBUtil.sqlUpdate(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().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)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ stmt.setInt(1, newIndex);
+ stmt.setLong(2, CDOIDUtil.getLong(id));
+ stmt.setInt(3, oldIndex);
+ CDODBUtil.sqlUpdate(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().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)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(
+ upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit, ReuseProbability.HIGH);
+
+ stmt.setLong(1, CDOIDUtil.getLong(id));
+ stmt.setInt(2, index);
+ if (upperIndex != UNBOUNDED_MOVE)
+ {
+ stmt.setInt(3, upperIndex);
+ }
+
+ CDODBUtil.sqlUpdate(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().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)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(
+ upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit, ReuseProbability.HIGH);
+ stmt.setLong(1, CDOIDUtil.getLong(id));
+ stmt.setInt(2, index);
+ if (upperIndex != UNBOUNDED_MOVE)
+ {
+ stmt.setInt(3, upperIndex);
+ }
+
+ CDODBUtil.sqlUpdate(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
+ stmt.setLong(1, CDOIDUtil.getLong(id));
+ stmt.setInt(2, index);
+ CDODBUtil.sqlUpdate(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().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)
+ {
+ PreparedStatement stmt = null;
+
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ Long tag = getTagByFeature(entryFeature);
+ String column = getColumnName(tag);
+ ITypeMapping mapping = getTypeMapping(tag);
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
+ stmt.setLong(1, tag);
+ int colIndex = 2;
+
+ for (int i = 0; i < getColumnNames().size(); i++)
+ {
+ String columnName = getColumnNames().get(i);
+
+ if (columnName.equals(column))
+ {
+ mapping.setValue(stmt, colIndex++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(colIndex++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ stmt.setLong(colIndex++, CDOIDUtil.getLong(id));
+ stmt.setInt(colIndex++, index);
+ CDODBUtil.sqlUpdate(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int oldVersion, final int newVersion,
+ 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());
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ removeListItem(accessor, id, delta.getIndex());
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ setListItem(accessor, id, delta.getIndex(), delta.getValue());
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 non-audit).launch b/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 non-audit).launch
new file mode 100644
index 0000000000..4a8a8cb614
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 non-audit).launch
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2NonAudit.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.emf.cdo.tests.db.AllTestsDBH2NonAudit"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.emf.cdo.tests.db"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m&#13;&#10;-Xmx512m"/>
+</launchConfiguration>
diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2NonAudit.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2NonAudit.java
new file mode 100644
index 0000000000..9654371519
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2NonAudit.java
@@ -0,0 +1,222 @@
+/**
+ * Copyright (c) 2004 - 2009 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
+ */
+package org.eclipse.emf.cdo.tests.db;
+
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.tests.AuditTest;
+import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_252214_Test;
+import org.eclipse.emf.cdo.tests.config.impl.ConfigTest;
+
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.h2.H2Adapter;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.io.TMPUtil;
+
+import org.h2.jdbcx.JdbcDataSource;
+
+import javax.sql.DataSource;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @author Eike Stepper
+ */
+public class AllTestsDBH2NonAudit extends DBConfigs
+{
+ public static Test suite()
+ {
+ return new AllTestsDBH2NonAudit()
+ .getTestSuite("CDO Tests (DBStoreRepositoryConfig H2 Horizontal - non-audit mode)");
+ }
+
+ @Override
+ protected void initConfigSuites(TestSuite parent)
+ {
+ addScenario(parent, COMBINED, AllTestsDBH2NonAudit.H2NonAudit.ReusableFolder.INSTANCE, JVM, NATIVE);
+ }
+
+ @Override
+ protected void initTestClasses(List<Class<? extends ConfigTest>> testClasses)
+ {
+ super.initTestClasses(testClasses);
+
+ // non-audit mode - remove audit tests
+ testClasses.remove(AuditTest.class);
+ testClasses.remove(AuditTest.LocalAuditTest.class);
+ testClasses.remove(Bugzilla_252214_Test.class);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class H2NonAudit extends DBStoreRepositoryConfig
+ {
+ private static final long serialVersionUID = 1L;
+
+ public static final AllTestsDBH2NonAudit.H2NonAudit INSTANCE = new H2NonAudit("DBStore: H2 (non-audit)");
+
+ protected transient File dbFolder;
+
+ public H2NonAudit(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void initRepositoryProperties(Map<String, String> props)
+ {
+ super.initRepositoryProperties(props);
+ props.put(IRepository.Props.SUPPORTING_AUDITS, "false");
+ }
+
+ @Override
+ protected IMappingStrategy createMappingStrategy()
+ {
+ return CDODBUtil.createHorizontalMappingStrategy(false);
+ }
+
+ @Override
+ protected IDBAdapter createDBAdapter()
+ {
+ return new H2Adapter();
+ }
+
+ @Override
+ protected DataSource createDataSource(String repoName)
+ {
+ if (dbFolder == null)
+ {
+ dbFolder = createDBFolder();
+ tearDownClean();
+ }
+
+ JdbcDataSource dataSource = new JdbcDataSource();
+ dataSource.setURL("jdbc:h2:" + dbFolder.getAbsolutePath() + "/h2test;SCHEMA=" + repoName);
+ return dataSource;
+ }
+
+ protected void tearDownClean()
+ {
+ IOUtil.delete(dbFolder);
+ }
+
+ protected File createDBFolder()
+ {
+ return TMPUtil.createTempFolder("h2_", "_test", new File("/temp"));
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class ReusableFolder extends H2NonAudit
+ {
+ private static final long serialVersionUID = 1L;
+
+ public static final ReusableFolder INSTANCE = new ReusableFolder("DBStore: H2 (Reusable Folder)");
+
+ private static File reusableFolder;
+
+ private static JdbcDataSource defaultDataSource;
+
+ private transient ArrayList<String> repoNames = new ArrayList<String>();
+
+ public ReusableFolder(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected DataSource createDataSource(String repoName)
+ {
+ if (reusableFolder == null)
+ {
+ reusableFolder = createDBFolder();
+ IOUtil.delete(reusableFolder);
+ }
+ dbFolder = reusableFolder;
+
+ if (defaultDataSource == null)
+ {
+ defaultDataSource = new JdbcDataSource();
+ defaultDataSource.setURL("jdbc:h2:" + dbFolder.getAbsolutePath() + "/h2test");
+ }
+
+ Connection conn = null;
+ Statement stmt = null;
+ try
+ {
+ conn = defaultDataSource.getConnection();
+ stmt = conn.createStatement();
+ stmt.execute("DROP SCHEMA IF EXISTS " + repoName);
+ stmt.execute("CREATE SCHEMA " + repoName);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ DBUtil.close(conn);
+ DBUtil.close(stmt);
+ }
+
+ JdbcDataSource dataSource = new JdbcDataSource();
+ dataSource.setURL("jdbc:h2:" + dbFolder.getAbsolutePath() + "/h2test;SCHEMA=" + repoName);
+
+ return dataSource;
+ }
+
+ @Override
+ protected void tearDownClean()
+ {
+ for (String repoName : repoNames)
+ {
+ tearDownClean(repoName);
+ }
+ }
+
+ protected void tearDownClean(String repoName)
+ {
+ reusableFolder.deleteOnExit();
+ Connection connection = null;
+ Statement stmt = null;
+
+ try
+ {
+ connection = defaultDataSource.getConnection();
+ stmt = connection.createStatement();
+ stmt.execute("DROP SCHEMA " + repoName);
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ finally
+ {
+ DBUtil.close(stmt);
+ DBUtil.close(connection);
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/DBConfigs.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/DBConfigs.java
index 80a349550a..33a877fce0 100644
--- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/DBConfigs.java
+++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/DBConfigs.java
@@ -11,7 +11,6 @@
package org.eclipse.emf.cdo.tests.db;
import org.eclipse.emf.cdo.tests.AllTestsAllConfigs;
-import org.eclipse.emf.cdo.tests.FeatureMapTest;
import org.eclipse.emf.cdo.tests.XATransactionTest;
import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_258933_Test;
import org.eclipse.emf.cdo.tests.config.impl.ConfigTest;
@@ -38,8 +37,5 @@ public abstract class DBConfigs extends AllTestsAllConfigs
// fails because of Bug 284110
testClasses.remove(Bugzilla_258933_Test.class);
testClasses.add(DISABLE_Bugzilla_258933_Test.class);
-
- // fails because of Bug 254455
- testClasses.remove(FeatureMapTest.class);
}
}

Back to the top