[417741] [DB] Add support for database index creation with DBAnnotation
https://bugs.eclipse.org/bugs/show_bug.cgi?id=417741
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java
new file mode 100644
index 0000000..41ec7d2
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016 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.server.internal.db;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * @author Kai Schlamp
+ */
+public final class DBIndexAnnotation
+{
+ public static final String SOURCE_URI = "http://www.eclipse.org/CDO/DBIndex";
+
+ public static final String FEATURES = "features";
+
+ private DBIndexAnnotation()
+ {
+ }
+
+ public static Set<List<EStructuralFeature>> getIndices(EClass eClass, EStructuralFeature[] allPersistentFeatures)
+ {
+ Set<List<EStructuralFeature>> indices = new HashSet<List<EStructuralFeature>>();
+
+ for (EAnnotation annotation : getAnnotations(eClass))
+ {
+ List<EStructuralFeature> features = new ArrayList<EStructuralFeature>();
+
+ String featureNames = annotation.getDetails().get(FEATURES);
+ if (featureNames != null && featureNames.length() != 0)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(featureNames, ",");
+ while (tokenizer.hasMoreTokens())
+ {
+ String featureName = tokenizer.nextToken().trim();
+ if (featureName.length() != 0)
+ {
+ EStructuralFeature feature = getPersistentFeature(featureName, allPersistentFeatures);
+ if (feature == null)
+ {
+ OM.LOG.warn("Feature '" + featureName + "' not found in class '" + eClass.getName() + "' in package '" + eClass.getEPackage().getNsURI() + "'");
+ continue;
+ }
+
+ features.add(feature);
+ }
+ }
+ }
+ else
+ {
+ for (EObject reference : annotation.getReferences())
+ {
+ if (reference instanceof EStructuralFeature)
+ {
+ EStructuralFeature feature = (EStructuralFeature)reference;
+ if (!isPersistentFeature(feature, allPersistentFeatures))
+ {
+ OM.LOG.warn("Feature '" + feature.getName() + "' is not a persistent feature of class '" + eClass.getName() + "' in package '"
+ + eClass.getEPackage().getNsURI() + "'");
+ continue;
+ }
+
+ features.add(feature);
+ }
+ else
+ {
+ OM.LOG.warn("Reference '" + reference + "' is not a feature");
+ }
+ }
+ }
+
+ int size = features.size();
+ if (size > 0)
+ {
+ if (size > 1)
+ {
+ for (EStructuralFeature feature : features)
+ {
+ if (feature.isMany())
+ {
+ OM.LOG.warn("Many-valued feature '" + feature.getName() + "' not allowed in composed index on class '" + eClass.getName() + "' in package '"
+ + eClass.getEPackage().getNsURI() + "'");
+ continue;
+ }
+ }
+ }
+
+ indices.add(features);
+ }
+ }
+
+ for (EStructuralFeature feature : allPersistentFeatures)
+ {
+ if (feature.getEAnnotation(SOURCE_URI) != null)
+ {
+ indices.add(Collections.singletonList(feature));
+ }
+ }
+
+ return indices;
+ }
+
+ private static EList<EAnnotation> getAnnotations(EClass eClass)
+ {
+ EList<EAnnotation> annotations = new BasicEList<EAnnotation>();
+ getAnnotations(eClass, annotations, new HashSet<EClass>());
+ return annotations;
+ }
+
+ private static void getAnnotations(EClass eClass, EList<EAnnotation> annotations, Set<EClass> visited)
+ {
+ if (visited.add(eClass))
+ {
+ for (EAnnotation annotation : eClass.getEAnnotations())
+ {
+ if (SOURCE_URI.equals(annotation.getSource()))
+ {
+ annotations.add(annotation);
+ }
+ }
+
+ for (EClass superType : eClass.getESuperTypes())
+ {
+ getAnnotations(superType, annotations, visited);
+ }
+ }
+ }
+
+ private static EStructuralFeature getPersistentFeature(String featureName, EStructuralFeature[] allPersistentFeatures)
+ {
+ for (int i = 0; i < allPersistentFeatures.length; i++)
+ {
+ EStructuralFeature feature = allPersistentFeatures[i];
+ if (feature.getName().equals(featureName))
+ {
+ return feature;
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isPersistentFeature(EStructuralFeature feature, EStructuralFeature[] allPersistentFeatures)
+ {
+ for (int i = 0; i < allPersistentFeatures.length; i++)
+ {
+ if (allPersistentFeatures[i] == feature)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
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 8cce38f..4cd9795 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
@@ -34,6 +34,7 @@
import org.eclipse.emf.cdo.server.db.mapping.IListMapping3;
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.DBIndexAnnotation;
import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy;
import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
@@ -255,6 +256,68 @@
}
}
}
+
+ initIndices(allPersistentFeatures);
+ }
+ }
+
+ private void initIndices(EStructuralFeature[] allPersistentFeatures)
+ {
+ for (List<EStructuralFeature> features : DBIndexAnnotation.getIndices(eClass, allPersistentFeatures))
+ {
+ int size = features.size();
+ if (size > 1)
+ {
+ IDBField[] fields = new IDBField[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ EStructuralFeature feature = features.get(i);
+
+ ITypeMapping typeMapping = getValueMapping(feature);
+ IDBField field = typeMapping.getField();
+ fields[i] = field;
+ }
+
+ if (!table.hasIndexFor(fields))
+ {
+ InternalDBIndex index = (InternalDBIndex)table.addIndex(IDBIndex.Type.NON_UNIQUE, fields);
+ index.setOptional(true); // Creation might fail for unsupported column type!
+ }
+ }
+ else
+ {
+ EStructuralFeature feature = features.get(0);
+ if (feature.isMany())
+ {
+ IListMapping listMapping = getListMapping(feature);
+ if (listMapping instanceof AbstractListTableMapping)
+ {
+ AbstractListTableMapping mapping = (AbstractListTableMapping)listMapping;
+
+ IDBTable table = mapping.getDBTables().iterator().next();
+ ITypeMapping typeMapping = mapping.getTypeMapping();
+ IDBField field = typeMapping.getField();
+
+ if (!table.hasIndexFor(field))
+ {
+ InternalDBIndex index = (InternalDBIndex)table.addIndex(IDBIndex.Type.NON_UNIQUE, field);
+ index.setOptional(true); // Creation might fail for unsupported column type!
+ }
+ }
+ }
+ else
+ {
+ ITypeMapping typeMapping = getValueMapping(feature);
+ IDBField field = typeMapping.getField();
+
+ if (!table.hasIndexFor(field))
+ {
+ InternalDBIndex index = (InternalDBIndex)table.addIndex(IDBIndex.Type.NON_UNIQUE, field);
+ index.setOptional(true); // Creation might fail for unsupported column type!
+ }
+ }
+ }
}
}