From e7bc45a0d64f152d50bdf001ebe032e6517ee1a7 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Mon, 14 Aug 2023 23:24:53 +0200
Subject: [PATCH 1/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../caosdb/server/database/DatabaseUtils.java | 124 ++++++++++++++++--
 .../MySQL/MySQLRetrieveProperties.java        |  86 ++++++------
 .../interfaces/RetrievePropertiesImpl.java    |   4 +-
 .../transaction/RetrieveProperties.java       |  46 ++++---
 .../server/database/proto/ProtoProperty.java  |  19 ++-
 .../server/entity/wrapper/Property.java       |  48 ++++---
 .../entity/xml/EntityToElementStrategy.java   |  10 +-
 .../entity/xml/PropertyToElementStrategy.java |   5 +
 .../caosdb/server/database/InsertTest.java    |   8 +-
 9 files changed, 251 insertions(+), 99 deletions(-)

diff --git a/src/main/java/org/caosdb/server/database/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
index 5280de17..8f4a59bb 100644
--- a/src/main/java/org/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
@@ -25,16 +25,21 @@ package org.caosdb.server.database;
 import com.google.common.base.Objects;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import org.caosdb.server.database.proto.FlatProperty;
 import org.caosdb.server.database.proto.ProtoProperty;
 import org.caosdb.server.database.proto.SparseEntity;
 import org.caosdb.server.database.proto.VerySparseEntity;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.CollectionValue;
+import org.caosdb.server.datatype.GenericValue;
 import org.caosdb.server.datatype.IndexedSingleValue;
 import org.caosdb.server.datatype.ReferenceValue;
 import org.caosdb.server.datatype.SingleValue;
@@ -175,9 +180,9 @@ public class DatabaseUtils {
   }
 
   public static List<FlatProperty> parsePropertyResultset(final ResultSet rs) throws SQLException {
-    final ArrayList<FlatProperty> ret = new ArrayList<FlatProperty>();
+    final List<FlatProperty> ret = new LinkedList<>();
     while (rs.next()) {
-      final FlatProperty fp = new FlatProperty();
+      FlatProperty fp = new FlatProperty();
       fp.id = rs.getInt("PropertyID");
       fp.value = bytes2UTF8(rs.getBytes("PropertyValue"));
       fp.status = bytes2UTF8(rs.getBytes("PropertyStatus"));
@@ -354,27 +359,120 @@ public class DatabaseUtils {
     }
   }
 
-  public static ArrayList<Property> parseFromProtoProperties(final List<ProtoProperty> protos) {
+  public static ArrayList<Property> parseFromProtoProperties(
+      EntityInterface entity, List<ProtoProperty> protos) {
     final ArrayList<Property> ret = new ArrayList<Property>();
     for (final ProtoProperty pp : protos) {
-      final Property property = parseFlatProperty(pp.property);
-      parseFromFlatProperties(property.getProperties(), pp.subProperties);
-      ret.add(property);
+      if (pp.id.equals(entity.getId().toInteger())) {
+        if (pp.value != null) {
+          entity.setValue(new GenericValue(pp.value));
+        }
+        if (pp.collValues != null) {
+          CollectionValue value = new CollectionValue();
+          for (Object next : pp.collValues) {
+            if (next == null) {
+              value.add(null);
+            } else {
+              value.add(new GenericValue(next.toString()));
+            }
+          }
+          entity.setValue(value);
+        }
+
+      } else {
+        final Property property = parseFlatProperty(pp);
+        // parseFromFlatProperties(property.getProperties(), pp.subProperties);
+        ret.add(property);
+      }
     }
     return ret;
   }
 
   private static void parseFromFlatProperties(
-      final List<Property> properties, final List<FlatProperty> props) {
-    for (final FlatProperty fp : props) {
-      final Property property = parseFlatProperty(fp);
-      properties.add(property);
-    }
+      final List<Property> properties, final List<ProtoProperty> props) {
+    if (props != null)
+      for (final ProtoProperty fp : props) {
+        final Property property = parseFlatProperty(fp);
+        parseFromFlatProperties(properties, fp.subProperties);
+        properties.add(property);
+      }
   }
 
-  private static Property parseFlatProperty(final FlatProperty fp) {
+  private static Property parseFlatProperty(final ProtoProperty fp) {
     final Property property = new Property(new RetrieveEntity());
-    property.parseFlatProperty(fp);
+    property.parseProtoProperty(fp);
     return property;
   }
+
+  @SuppressWarnings("unchecked")
+  public static LinkedList<ProtoProperty> transformToDeepPropertyTree(
+      List<ProtoProperty> properties) {
+    LinkedList<ProtoProperty> result = new LinkedList<>();
+    Map<String, ProtoProperty> replacements = new HashMap<>();
+    for (ProtoProperty pp : properties) {
+      if (pp.status.equals(StatementStatus.REPLACEMENT.toString())) {
+        replacements.put(pp.value, null);
+      }
+    }
+    Iterator<ProtoProperty> iterator = properties.iterator();
+    while (iterator.hasNext()) {
+      ProtoProperty pp = iterator.next();
+      if (replacements.containsKey(pp.id.toString())) {
+        ProtoProperty other = replacements.get(pp.id.toString());
+        // handle collection values
+        if (other != null) {
+          if (other.collValues == null) {
+            other.collValues = new LinkedList<>();
+            Entry<Integer, String> obj = new SimpleEntry<>(other.idx, other.value);
+            other.collValues.add(obj);
+            other.value = null;
+          }
+          other.collValues.add(new SimpleEntry<>(pp.idx, pp.value));
+        } else {
+          replacements.put(pp.id.toString(), pp);
+        }
+        iterator.remove();
+      }
+    }
+    for (ProtoProperty pp : properties) {
+      if (pp.status.equals(StatementStatus.REPLACEMENT.toString())) {
+        replace(pp, replacements.get(pp.value));
+      }
+      if (pp.collValues != null) {
+        // sort
+        pp.collValues.sort(
+            (x, y) -> {
+              return ((Entry<Integer, String>) x).getKey() - ((Entry<Integer, String>) y).getKey();
+            });
+        pp.collValues =
+            pp.collValues
+                .stream()
+                .map((x) -> (Object) ((Entry<Integer, String>) x).getValue())
+                .toList();
+      }
+      result.add(pp);
+    }
+    return result;
+  }
+
+  private static void replace(ProtoProperty pp, ProtoProperty replacement) {
+    if (replacement == null) {
+      throw new NullPointerException("Replacement was null");
+      //		    // entity has been deleted (we are processing properties of an old entity version)
+      //		    p.setValue(null);
+      //		    p.addWarning(
+      //		        new Message(
+      //		            "The referenced entity has been deleted in the mean time and is not longer
+      // available."));
+      //		    return;
+    }
+    pp.desc = replacement.desc;
+    pp.name = replacement.name;
+    pp.type = replacement.type;
+    pp.collection = replacement.collection;
+    pp.value = replacement.value;
+    pp.collValues = replacement.collValues;
+    pp.status = replacement.status;
+    pp.subProperties = replacement.subProperties;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
index 6bcfeac0..b0c5d5b7 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
@@ -26,7 +26,7 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
-import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
@@ -44,29 +44,23 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
 
   private static final String stmtStr = "call retrieveEntityProperties(?,?,?)";
   private static final String stmtStr2 = "call retrieveOverrides(?,?,?)";
+  private static final String isCollectionStmt = "call isCollection(?,?)";
 
   @Override
-  public ArrayList<ProtoProperty> execute(final EntityID entity, final String version)
+  public LinkedList<ProtoProperty> execute(final EntityID entity, final String version)
       throws TransactionException {
     try {
       final PreparedStatement prepareStatement = prepareStatement(stmtStr);
 
-      final List<FlatProperty> props =
+      final List<ProtoProperty> propsStage1 =
           retrieveFlatPropertiesStage1(0, entity.toInteger(), version, prepareStatement);
 
-      final ArrayList<ProtoProperty> protos = new ArrayList<ProtoProperty>();
-      for (final FlatProperty p : props) {
-        final ProtoProperty proto = new ProtoProperty();
-        proto.property = p;
+      for (final ProtoProperty p : propsStage1) {
 
-        final List<FlatProperty> subProps =
+        p.subProperties =
             retrieveFlatPropertiesStage1(entity.toInteger(), p.id, version, prepareStatement);
-
-        proto.subProperties = subProps;
-
-        protos.add(proto);
       }
-      return protos;
+      return DatabaseUtils.transformToDeepPropertyTree(propsStage1);
     } catch (final SQLException e) {
       throw new TransactionException(e);
     } catch (final ConnectionException e) {
@@ -74,68 +68,76 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
     }
   }
 
-  private List<FlatProperty> retrieveFlatPropertiesStage1(
+  private List<ProtoProperty> retrieveFlatPropertiesStage1(
       final Integer domain,
       final Integer entity,
       final String version,
       final PreparedStatement stmt)
       throws SQLException, ConnectionException {
-    ResultSet rs = null;
-    try {
-      if (domain != null && domain >= 0) {
-        stmt.setInt(1, domain);
-      } else {
-        stmt.setInt(1, 0);
-      }
+    if (domain != null && domain >= 0) {
+      stmt.setInt(1, domain);
+    } else {
+      stmt.setInt(1, 0);
+    }
 
-      stmt.setInt(2, entity);
-      if (version == null) {
-        stmt.setNull(3, Types.VARBINARY);
-      } else {
-        stmt.setString(3, version);
-      }
+    stmt.setInt(2, entity);
+    if (version == null) {
+      stmt.setNull(3, Types.VARBINARY);
+    } else {
+      stmt.setString(3, version);
+    }
 
-      final long t1 = System.currentTimeMillis();
-      rs = stmt.executeQuery();
+    final long t1 = System.currentTimeMillis();
+
+    try (ResultSet rs = stmt.executeQuery()) {
       final long t2 = System.currentTimeMillis();
       addMeasurement(this.getClass().getSimpleName() + ".retrieveFlatPropertiesStage1", t2 - t1);
 
       final List<FlatProperty> props = DatabaseUtils.parsePropertyResultset(rs);
 
-      final PreparedStatement stmt2 = prepareStatement(stmtStr2);
+      final PreparedStatement retrieveOverrides = prepareStatement(stmtStr2);
 
-      retrieveOverrides(domain, entity, version, stmt2, props);
+      retrieveOverrides(domain, entity, version, retrieveOverrides, props);
 
-      return props;
-    } finally {
-      if (rs != null && !rs.isClosed()) {
-        rs.close();
-      }
+      PreparedStatement isCollectionStatement = prepareStatement(isCollectionStmt);
+
+      return handleCollectionValues(props, isCollectionStatement);
     }
   }
 
+  public List<ProtoProperty> handleCollectionValues(
+      List<FlatProperty> props, PreparedStatement isCollectionStatement) throws SQLException {
+
+    List<ProtoProperty> result = new LinkedList<>();
+    for (FlatProperty fp : props) {
+      result.add(new ProtoProperty(fp));
+    }
+    return result;
+  }
+
   private void retrieveOverrides(
       final Integer domain,
       final Integer entity,
       final String version,
-      final PreparedStatement stmt2,
+      final PreparedStatement retrieveOverrides,
       final List<FlatProperty> props)
       throws SQLException {
 
     ResultSet rs = null;
     try {
-      stmt2.setInt(1, domain);
-      stmt2.setInt(2, entity);
+      retrieveOverrides.setInt(1, domain);
+      retrieveOverrides.setInt(2, entity);
       if (version == null) {
-        stmt2.setNull(3, Types.VARBINARY);
+        retrieveOverrides.setNull(3, Types.VARBINARY);
       } else {
-        stmt2.setString(3, version);
+        retrieveOverrides.setString(3, version);
       }
       final long t1 = System.currentTimeMillis();
-      rs = stmt2.executeQuery();
+      rs = retrieveOverrides.executeQuery();
       final long t2 = System.currentTimeMillis();
       addMeasurement(this.getClass().getSimpleName() + ".retrieveOverrides", t2 - t1);
       DatabaseUtils.parseOverrides(props, rs);
+
     } finally {
       if (rs != null && !rs.isClosed()) {
         rs.close();
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
index dc1d5aa0..69fc9e70 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
@@ -22,12 +22,12 @@
  */
 package org.caosdb.server.database.backend.interfaces;
 
-import java.util.ArrayList;
+import java.util.LinkedList;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.ProtoProperty;
 import org.caosdb.server.entity.EntityID;
 
 public interface RetrievePropertiesImpl extends BackendTransactionImpl {
 
-  public ArrayList<ProtoProperty> execute(EntityID id, String version) throws TransactionException;
+  public LinkedList<ProtoProperty> execute(EntityID id, String version) throws TransactionException;
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
index 726382f2..5cd7ace1 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
@@ -24,7 +24,8 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.caosdb.server.caching.Cache;
 import org.caosdb.server.database.CacheableBackendTransaction;
@@ -37,11 +38,11 @@ import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.wrapper.Property;
 
 public class RetrieveProperties
-    extends CacheableBackendTransaction<String, ArrayList<ProtoProperty>> {
+    extends CacheableBackendTransaction<String, LinkedList<ProtoProperty>> {
 
   private final EntityInterface entity;
   public static final String CACHE_REGION = "BACKEND_EntityProperties";
-  private static final ICacheAccess<String, ArrayList<ProtoProperty>> cache =
+  private static final ICacheAccess<String, LinkedList<ProtoProperty>> cache =
       Cache.getCache(CACHE_REGION);
 
   /**
@@ -61,40 +62,45 @@ public class RetrieveProperties
   }
 
   @Override
-  public ArrayList<ProtoProperty> executeNoCache() throws TransactionException {
+  public LinkedList<ProtoProperty> executeNoCache() throws TransactionException {
     final RetrievePropertiesImpl t = getImplementation(RetrievePropertiesImpl.class);
     return t.execute(this.entity.getId(), this.entity.getVersion().getId());
   }
 
   @Override
-  protected void process(final ArrayList<ProtoProperty> t) throws TransactionException {
+  protected void process(final LinkedList<ProtoProperty> t) throws TransactionException {
     this.entity.getProperties().clear();
 
-    final ArrayList<Property> props = DatabaseUtils.parseFromProtoProperties(t);
+    final List<Property> props = DatabaseUtils.parseFromProtoProperties(this.entity, t);
+    this.entity.getProperties().addAll(props);
 
-    for (final Property p : props) {
+    retrieveSparseSubproperties(props);
+  }
+
+  private void retrieveSparseSubproperties(List<Property> properties) {
+    for (final Property p : properties) {
       // retrieve sparse properties stage 1
+
+      // handle corner case where a record type is used as a property with an overridden name.
+      boolean isNameOverride = p.isNameOverride();
+      String overrideName = p.getName();
+      p.setNameOverride(false);
+
       final RetrieveSparseEntity t1 = new RetrieveSparseEntity(p);
       execute(t1);
+      String originalName = p.getName();
+      if (isNameOverride) {
+        p.setNameOverride(isNameOverride);
+        p.setName(overrideName);
+      }
 
-      // add default data type for record types
       if (!p.hasDatatype() && p.getRole() == Role.RecordType) {
-        p.setDatatype(p.getName());
+        p.setDatatype(originalName);
       }
 
       // retrieve sparse properties stage 2
-      for (final EntityInterface subP : p.getProperties()) {
-        final RetrieveSparseEntity t2 = new RetrieveSparseEntity(subP);
-        execute(t2);
-
-        // add default data type for record types
-        if (!subP.hasDatatype() && subP.getRole() == Role.RecordType) {
-          subP.setDatatype(subP.getName());
-        }
-      }
+      retrieveSparseSubproperties(p.getProperties());
     }
-
-    DatabaseUtils.transformToDeepPropertyTree(this.entity, props);
   }
 
   @Override
diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
index 44acaa02..898565ac 100644
--- a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
+++ b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
@@ -25,9 +25,22 @@ package org.caosdb.server.database.proto;
 import java.io.Serializable;
 import java.util.List;
 
-public class ProtoProperty implements Serializable {
+public class ProtoProperty extends FlatProperty implements Serializable {
+
+  public ProtoProperty(FlatProperty fp) {
+    id = fp.id;
+    value = fp.value;
+    status = fp.status;
+    idx = fp.idx;
+    name = fp.name;
+    desc = fp.desc;
+    type = fp.type;
+    collection = fp.collection;
+  }
+
+  public ProtoProperty() {}
 
   private static final long serialVersionUID = 7731301985162924975L;
-  public List<FlatProperty> subProperties = null;
-  public FlatProperty property = null;
+  public List<ProtoProperty> subProperties = null;
+  public List<Object> collValues = null;
 }
diff --git a/src/main/java/org/caosdb/server/entity/wrapper/Property.java b/src/main/java/org/caosdb/server/entity/wrapper/Property.java
index efa1a685..9268d7f3 100644
--- a/src/main/java/org/caosdb/server/entity/wrapper/Property.java
+++ b/src/main/java/org/caosdb/server/entity/wrapper/Property.java
@@ -25,11 +25,13 @@ package org.caosdb.server.entity.wrapper;
 import org.apache.shiro.authz.AuthorizationException;
 import org.apache.shiro.authz.Permission;
 import org.apache.shiro.subject.Subject;
-import org.caosdb.server.database.proto.FlatProperty;
+import org.caosdb.server.database.proto.ProtoProperty;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
+import org.caosdb.server.datatype.CollectionValue;
 import org.caosdb.server.datatype.GenericValue;
 import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.xml.PropertyToElementStrategy;
 
 public class Property extends EntityWrapper {
@@ -72,29 +74,45 @@ public class Property extends EntityWrapper {
     return this;
   }
 
-  public Property parseFlatProperty(final FlatProperty fp) {
-    setId(new EntityID(fp.id));
-    setStatementStatus(fp.status);
-    setPIdx(fp.idx);
-    if (fp.name != null) {
-      setName(fp.name);
+  public Property parseProtoProperty(final ProtoProperty pp) {
+    setId(new EntityID(pp.id));
+    setStatementStatus(pp.status);
+    setPIdx(pp.idx);
+    if (pp.name != null) {
+      setName(pp.name);
       setNameOverride(true);
     }
-    if (fp.desc != null) {
-      setDescription(fp.desc);
+    if (pp.desc != null) {
+      setDescription(pp.desc);
       setDescOverride(true);
     }
-    if (fp.type != null) {
-      if (fp.collection != null) {
+    if (pp.type != null) {
+      if (pp.collection != null) {
         this.setDatatype(
-            AbstractCollectionDatatype.collectionDatatypeFactory(fp.collection, fp.type));
+            AbstractCollectionDatatype.collectionDatatypeFactory(pp.collection, pp.type));
       } else {
-        this.setDatatype(fp.type);
+        this.setDatatype(pp.type);
       }
       setDatatypeOverride(true);
     }
-    if (fp.value != null) {
-      setValue(new GenericValue(fp.value));
+    if (pp.value != null) {
+      setValue(new GenericValue(pp.value));
+    }
+    if (pp.collValues != null) {
+      CollectionValue value = new CollectionValue();
+      for (Object next : pp.collValues) {
+        if (next == null) {
+          value.add(null);
+        } else {
+          value.add(new GenericValue(next.toString()));
+        }
+      }
+      setValue(value);
+    }
+    if (pp.subProperties != null) {
+      for (ProtoProperty subP : pp.subProperties) {
+        this.addProperty(new Property(new RetrieveEntity()).parseProtoProperty(subP));
+      }
     }
     return this;
   }
diff --git a/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java b/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
index 7f6e6dca..35d94aa7 100644
--- a/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
+++ b/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
@@ -85,9 +85,7 @@ public class EntityToElementStrategy implements ToElementStrategy {
 
     // @review Florian Spreckelsen 2022-03-22
 
-    if (entity.getEntityACL() != null) {
-      element.addContent(entity.getEntityACL().getPermissionsFor(SecurityUtils.getSubject()));
-    }
+    addPermissions(entity, element);
     if (serializeFieldStrategy.isToBeSet("id") && entity.hasId()) {
       element.setAttribute("id", entity.getId().toString());
     }
@@ -135,6 +133,12 @@ public class EntityToElementStrategy implements ToElementStrategy {
     }
   }
 
+  protected void addPermissions(EntityInterface entity, Element element) {
+    if (entity.getEntityACL() != null) {
+      element.addContent(entity.getEntityACL().getPermissionsFor(SecurityUtils.getSubject()));
+    }
+  }
+
   /**
    * Set the value of the entity.
    *
diff --git a/src/main/java/org/caosdb/server/entity/xml/PropertyToElementStrategy.java b/src/main/java/org/caosdb/server/entity/xml/PropertyToElementStrategy.java
index 23fd8a55..7109a0d8 100644
--- a/src/main/java/org/caosdb/server/entity/xml/PropertyToElementStrategy.java
+++ b/src/main/java/org/caosdb/server/entity/xml/PropertyToElementStrategy.java
@@ -35,6 +35,11 @@ public class PropertyToElementStrategy extends EntityToElementStrategy {
     super("Property");
   }
 
+  @Override
+  protected void addPermissions(EntityInterface entity, Element element) {
+    // don't
+  }
+
   @Override
   public Element addToElement(
       final EntityInterface entity,
diff --git a/src/test/java/org/caosdb/server/database/InsertTest.java b/src/test/java/org/caosdb/server/database/InsertTest.java
index 524e7add..59aa6f55 100644
--- a/src/test/java/org/caosdb/server/database/InsertTest.java
+++ b/src/test/java/org/caosdb/server/database/InsertTest.java
@@ -124,7 +124,7 @@ public class InsertTest {
 
     final LinkedList<EntityInterface> stage1Inserts = new LinkedList<EntityInterface>();
     final LinkedList<EntityInterface> stage2Inserts = new LinkedList<EntityInterface>();
-    new LinkedList<EntityInterface>();
+
     deriveStage1Inserts(stage1Inserts, r);
 
     assertEquals(7, stage1Inserts.size());
@@ -166,34 +166,40 @@ public class InsertTest {
     assertEquals(Role.Property, stage2Inserts.get(0).getRole());
     assertEquals(new EntityID(3), stage2Inserts.get(0).getId());
     assertEquals("V3", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString());
+    assertEquals(new EntityID(2), stage2Inserts.get(0).getDomain());
     assertFalse(stage2Inserts.get(0).hasReplacement());
 
     assertEquals(Role.Property, stage2Inserts.get(1).getRole());
     assertEquals(new EntityID(5), stage2Inserts.get(1).getId());
     assertEquals("V5", ((SingleValue) stage2Inserts.get(1).getValue()).toDatabaseString());
     assertTrue(stage2Inserts.get(1).hasReplacement());
+    assertEquals(new EntityID(4), stage2Inserts.get(1).getDomain());
     assertEquals(stage1Inserts.get(3), stage2Inserts.get(1).getReplacement());
 
     assertEquals(Role.Property, stage2Inserts.get(2).getRole());
     assertEquals(new EntityID(6), stage2Inserts.get(2).getId());
     assertEquals("V6", ((SingleValue) stage2Inserts.get(2).getValue()).toDatabaseString());
+    assertEquals(new EntityID(5), stage2Inserts.get(2).getDomain());
     assertFalse(stage2Inserts.get(2).hasReplacement());
 
     assertEquals(Role.Property, stage2Inserts.get(3).getRole());
     assertEquals(new EntityID(8), stage2Inserts.get(3).getId());
     assertEquals("V8", ((SingleValue) stage2Inserts.get(3).getValue()).toDatabaseString());
     assertTrue(stage2Inserts.get(3).hasReplacement());
+    assertEquals(new EntityID(7), stage2Inserts.get(3).getDomain());
     assertEquals(stage1Inserts.get(5), stage2Inserts.get(3).getReplacement());
 
     assertEquals(Role.Property, stage2Inserts.get(4).getRole());
     assertEquals(new EntityID(9), stage2Inserts.get(4).getId());
     assertEquals("V9", ((SingleValue) stage2Inserts.get(4).getValue()).toDatabaseString());
     assertTrue(stage2Inserts.get(4).hasReplacement());
+    assertEquals(new EntityID(8), stage2Inserts.get(4).getDomain());
     assertEquals(stage1Inserts.get(6), stage2Inserts.get(4).getReplacement());
 
     assertEquals(Role.Property, stage2Inserts.get(5).getRole());
     assertEquals(new EntityID(10), stage2Inserts.get(5).getId());
     assertEquals("V10", ((SingleValue) stage2Inserts.get(5).getValue()).toDatabaseString());
+    assertEquals(new EntityID(9), stage2Inserts.get(5).getDomain());
     assertFalse(stage2Inserts.get(5).hasReplacement());
   }
 
-- 
GitLab


From 236a8d7bde090676b52a757be2e6cde4cc2f2026 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Tue, 15 Aug 2023 10:43:16 +0200
Subject: [PATCH 2/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../caosdb/server/database/DatabaseUtils.java | 142 +++---------------
 .../MySQL/MySQLRetrieveProperties.java        |  15 +-
 .../interfaces/RetrievePropertiesImpl.java    |   3 +-
 .../transaction/RetrieveProperties.java       |   3 +-
 4 files changed, 33 insertions(+), 130 deletions(-)

diff --git a/src/main/java/org/caosdb/server/database/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
index 8f4a59bb..5799d23f 100644
--- a/src/main/java/org/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
@@ -40,9 +40,6 @@ import org.caosdb.server.database.proto.VerySparseEntity;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.CollectionValue;
 import org.caosdb.server.datatype.GenericValue;
-import org.caosdb.server.datatype.IndexedSingleValue;
-import org.caosdb.server.datatype.ReferenceValue;
-import org.caosdb.server.datatype.SingleValue;
 import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
@@ -55,6 +52,9 @@ import org.caosdb.server.entity.wrapper.Property;
 
 public class DatabaseUtils {
 
+  private static final String DELETED_REFERENCE_IN_PREVIOUS_VERSION =
+      "DELETED_REFERENCE_IN_PREVIOUS_VERSION";
+
   public static final String bytes2UTF8(final byte[] bytes) {
     if (bytes == null) {
       return null;
@@ -265,100 +265,6 @@ public class DatabaseUtils {
     }
   }
 
-  private static void replace(
-      final Property p, final Map<EntityID, Property> domainMap, final boolean isHead) {
-    // ... find the corresponding domain and replace it
-    ReferenceValue ref;
-    try {
-      ref = ReferenceValue.parseReference(p.getValue());
-    } catch (final Message e) {
-      throw new RuntimeException("This should never happen.");
-    }
-    final EntityInterface replacement = domainMap.get(ref.getId());
-    if (replacement == null) {
-      if (isHead) {
-        throw new NullPointerException("Replacement was null");
-      }
-      // entity has been deleted (we are processing properties of an old entity version)
-      p.setValue(null);
-      p.addWarning(
-          new Message(
-              "The referenced entity has been deleted in the mean time and is not longer available."));
-      return;
-    }
-    if (replacement.isDescOverride()) {
-      p.setDescOverride(true);
-      p.setDescription(replacement.getDescription());
-    }
-    if (replacement.isNameOverride()) {
-      p.setNameOverride(true);
-      p.setName(replacement.getName());
-    }
-    if (replacement.isDatatypeOverride()) {
-      p.setDatatypeOverride(true);
-      p.setDatatype(replacement.getDatatype());
-    }
-    p.setProperties(replacement.getProperties());
-    p.setStatementStatus(replacement.getStatementStatus());
-    p.setValue(replacement.getValue());
-  }
-
-  public static void transformToDeepPropertyTree(
-      final EntityInterface e, final List<Property> protoProperties) {
-    // here we will store every domain we can find and we will index it by
-    // its id.
-    final Map<EntityID, Property> domainMap = new HashMap<>();
-
-    // loop over all properties and collect the domains
-    for (final Property p : protoProperties) {
-      // if this is a domain it represents a deeper branch of the property
-      // tree.
-      if (p.getRole() == Role.Domain) {
-        if (domainMap.containsKey(p.getId())) {
-          // aggregate the multiple values.
-          final Property domain = domainMap.get(p.getId());
-          if (!(domain.getValue() instanceof CollectionValue)) {
-            final SingleValue singleValue = (SingleValue) domain.getValue();
-            final CollectionValue vals = new CollectionValue();
-            final IndexedSingleValue iSingleValue =
-                new IndexedSingleValue(domain.getPIdx(), singleValue);
-            vals.add(iSingleValue);
-            domain.setValue(vals);
-          }
-          ((CollectionValue) domain.getValue()).add(p.getPIdx(), p.getValue());
-        } else {
-          domainMap.put(p.getId(), p);
-        }
-      }
-    }
-
-    // loop over all properties
-    final boolean isHead =
-        e.getVersion().getSuccessors() == null || e.getVersion().getSuccessors().isEmpty();
-    for (final Property p : protoProperties) {
-
-      // if this is a replacement
-      if (p.getStatementStatus() == StatementStatus.REPLACEMENT) {
-        replace(p, domainMap, isHead);
-      }
-
-      for (final Property subP : p.getProperties()) {
-        if (subP.getStatementStatus() == StatementStatus.REPLACEMENT) {
-          replace(subP, domainMap, isHead);
-        }
-      }
-
-      if (p.getId().equals(e.getId())) {
-        // this is the value of an abstract property.
-        e.setValue(p.getValue());
-      } else if (p.getRole() != Role.Domain) {
-        // if this is not a domain it is to be added to the properties
-        // of e.
-        e.addProperty(p);
-      }
-    }
-  }
-
   public static ArrayList<Property> parseFromProtoProperties(
       EntityInterface entity, List<ProtoProperty> protos) {
     final ArrayList<Property> ret = new ArrayList<Property>();
@@ -380,24 +286,23 @@ public class DatabaseUtils {
         }
 
       } else {
+        Message warning = null;
+        if (pp.status == DELETED_REFERENCE_IN_PREVIOUS_VERSION) {
+          pp.status = StatementStatus.FIX.name();
+          warning =
+              new Message(
+                  "The referenced entity has been deleted in the mean time and is not longer available.");
+        }
         final Property property = parseFlatProperty(pp);
-        // parseFromFlatProperties(property.getProperties(), pp.subProperties);
+        if (warning != null) {
+          property.addWarning(warning);
+        }
         ret.add(property);
       }
     }
     return ret;
   }
 
-  private static void parseFromFlatProperties(
-      final List<Property> properties, final List<ProtoProperty> props) {
-    if (props != null)
-      for (final ProtoProperty fp : props) {
-        final Property property = parseFlatProperty(fp);
-        parseFromFlatProperties(properties, fp.subProperties);
-        properties.add(property);
-      }
-  }
-
   private static Property parseFlatProperty(final ProtoProperty fp) {
     final Property property = new Property(new RetrieveEntity());
     property.parseProtoProperty(fp);
@@ -406,7 +311,7 @@ public class DatabaseUtils {
 
   @SuppressWarnings("unchecked")
   public static LinkedList<ProtoProperty> transformToDeepPropertyTree(
-      List<ProtoProperty> properties) {
+      List<ProtoProperty> properties, boolean isHead) {
     LinkedList<ProtoProperty> result = new LinkedList<>();
     Map<String, ProtoProperty> replacements = new HashMap<>();
     for (ProtoProperty pp : properties) {
@@ -436,7 +341,7 @@ public class DatabaseUtils {
     }
     for (ProtoProperty pp : properties) {
       if (pp.status.equals(StatementStatus.REPLACEMENT.toString())) {
-        replace(pp, replacements.get(pp.value));
+        replace(pp, replacements.get(pp.value), isHead);
       }
       if (pp.collValues != null) {
         // sort
@@ -455,16 +360,15 @@ public class DatabaseUtils {
     return result;
   }
 
-  private static void replace(ProtoProperty pp, ProtoProperty replacement) {
+  private static void replace(ProtoProperty pp, ProtoProperty replacement, boolean isHead) {
     if (replacement == null) {
-      throw new NullPointerException("Replacement was null");
-      //		    // entity has been deleted (we are processing properties of an old entity version)
-      //		    p.setValue(null);
-      //		    p.addWarning(
-      //		        new Message(
-      //		            "The referenced entity has been deleted in the mean time and is not longer
-      // available."));
-      //		    return;
+      if (isHead) {
+        throw new NullPointerException("Replacement was null");
+      }
+      // entity has been deleted (we are processing properties of an old entity version)
+      pp.value = null;
+      pp.status = DELETED_REFERENCE_IN_PREVIOUS_VERSION;
+      return;
     }
     pp.desc = replacement.desc;
     pp.name = replacement.name;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
index b0c5d5b7..c8843f9c 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
@@ -44,11 +44,10 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
 
   private static final String stmtStr = "call retrieveEntityProperties(?,?,?)";
   private static final String stmtStr2 = "call retrieveOverrides(?,?,?)";
-  private static final String isCollectionStmt = "call isCollection(?,?)";
 
   @Override
-  public LinkedList<ProtoProperty> execute(final EntityID entity, final String version)
-      throws TransactionException {
+  public LinkedList<ProtoProperty> execute(
+      final EntityID entity, final String version, boolean isHead) throws TransactionException {
     try {
       final PreparedStatement prepareStatement = prepareStatement(stmtStr);
 
@@ -60,7 +59,7 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
         p.subProperties =
             retrieveFlatPropertiesStage1(entity.toInteger(), p.id, version, prepareStatement);
       }
-      return DatabaseUtils.transformToDeepPropertyTree(propsStage1);
+      return DatabaseUtils.transformToDeepPropertyTree(propsStage1, isHead);
     } catch (final SQLException e) {
       throw new TransactionException(e);
     } catch (final ConnectionException e) {
@@ -99,14 +98,12 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
 
       retrieveOverrides(domain, entity, version, retrieveOverrides, props);
 
-      PreparedStatement isCollectionStatement = prepareStatement(isCollectionStmt);
-
-      return handleCollectionValues(props, isCollectionStatement);
+      return handleCollectionValues(props);
     }
   }
 
-  public List<ProtoProperty> handleCollectionValues(
-      List<FlatProperty> props, PreparedStatement isCollectionStatement) throws SQLException {
+  @Deprecated
+  public List<ProtoProperty> handleCollectionValues(List<FlatProperty> props) throws SQLException {
 
     List<ProtoProperty> result = new LinkedList<>();
     for (FlatProperty fp : props) {
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
index 69fc9e70..6bf2051a 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
@@ -29,5 +29,6 @@ import org.caosdb.server.entity.EntityID;
 
 public interface RetrievePropertiesImpl extends BackendTransactionImpl {
 
-  public LinkedList<ProtoProperty> execute(EntityID id, String version) throws TransactionException;
+  public LinkedList<ProtoProperty> execute(EntityID id, String version, boolean isHead)
+      throws TransactionException;
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
index 5cd7ace1..caa4b6cf 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
@@ -64,7 +64,8 @@ public class RetrieveProperties
   @Override
   public LinkedList<ProtoProperty> executeNoCache() throws TransactionException {
     final RetrievePropertiesImpl t = getImplementation(RetrievePropertiesImpl.class);
-    return t.execute(this.entity.getId(), this.entity.getVersion().getId());
+    return t.execute(
+        this.entity.getId(), this.entity.getVersion().getId(), this.entity.getVersion().isHead());
   }
 
   @Override
-- 
GitLab


From 0326111fc575abeddfad65c546cf6f075464c8bf Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 00:00:44 +0200
Subject: [PATCH 3/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../server/database/BackendTransaction.java   |   3 -
 .../caosdb/server/database/DatabaseUtils.java |  80 +++++----
 .../caosdb/server/database/Replacement.java   |  41 +++++
 .../MySQL/MySQLInsertEntityProperties.java    | 147 ++++++++++++++++-
 .../MySQL/MySQLRegisterSubDomain.java         |  64 -------
 .../InsertEntityPropertiesImpl.java           |   9 +-
 .../interfaces/RegisterSubDomainImpl.java     |  32 ----
 .../transaction/InsertEntityProperties.java   | 137 +--------------
 .../transaction/InsertEntityTransaction.java  |   4 +-
 .../transaction/RegisterSubDomain.java        |  49 ------
 .../server/database/proto/FlatProperty.java   |  13 ++
 .../server/database/proto/ProtoProperty.java  |  11 +-
 .../caosdb/server/database/InsertTest.java    | 156 ++++++++----------
 .../caosdb/server/database/InsertTest2.java   | 148 +++++++++++++++++
 14 files changed, 466 insertions(+), 428 deletions(-)
 create mode 100644 src/main/java/org/caosdb/server/database/Replacement.java
 delete mode 100644 src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRegisterSubDomain.java
 delete mode 100644 src/main/java/org/caosdb/server/database/backend/interfaces/RegisterSubDomainImpl.java
 delete mode 100644 src/main/java/org/caosdb/server/database/backend/transaction/RegisterSubDomain.java
 create mode 100644 src/test/java/org/caosdb/server/database/InsertTest2.java

diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java
index 5addf3f3..05ea26c2 100644
--- a/src/main/java/org/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java
@@ -47,7 +47,6 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLIsSubType;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLListRoles;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLListUsers;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLLogUserVisit;
-import org.caosdb.server.database.backend.implementation.MySQL.MySQLRegisterSubDomain;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAll;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAllUncheckedFiles;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveDatatypes;
@@ -106,7 +105,6 @@ import org.caosdb.server.database.backend.interfaces.IsSubTypeImpl;
 import org.caosdb.server.database.backend.interfaces.ListRolesImpl;
 import org.caosdb.server.database.backend.interfaces.ListUsersImpl;
 import org.caosdb.server.database.backend.interfaces.LogUserVisitImpl;
-import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveDatatypesImpl;
@@ -177,7 +175,6 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(IsSubTypeImpl.class, MySQLIsSubType.class);
       setImpl(UpdateSparseEntityImpl.class, MySQLUpdateSparseEntity.class);
       setImpl(RetrieveAllImpl.class, MySQLRetrieveAll.class);
-      setImpl(RegisterSubDomainImpl.class, MySQLRegisterSubDomain.class);
       setImpl(RetrieveDatatypesImpl.class, MySQLRetrieveDatatypes.class);
       setImpl(RetrieveUserImpl.class, MySQLRetrieveUser.class);
       setImpl(RetrieveParentsImpl.class, MySQLRetrieveParents.class);
diff --git a/src/main/java/org/caosdb/server/database/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
index 5799d23f..6d72d965 100644
--- a/src/main/java/org/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
@@ -27,6 +27,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -44,9 +45,7 @@ import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.RetrieveEntity;
-import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.StatementStatus;
-import org.caosdb.server.entity.wrapper.Domain;
 import org.caosdb.server.entity.wrapper.Parent;
 import org.caosdb.server.entity.wrapper.Property;
 
@@ -63,39 +62,33 @@ public class DatabaseUtils {
     }
   }
 
-  private static Domain makeReplacement(final EntityInterface p) {
-    // add a new helper domain.
-    final Domain d =
-        new Domain(p.getProperties(), p.getDatatype(), p.getValue(), p.getStatementStatus());
-    d.setDescOverride(p.isDescOverride());
-    d.setNameOverride(p.isNameOverride());
-    d.setDatatypeOverride(p.isDatatypeOverride());
-    d.setName(p.getName());
-    d.setDescription(p.getDescription());
-    p.setReplacement(d);
-    return d;
-  }
-
-  public static void deriveStage1Inserts(
-      final List<EntityInterface> stage1Inserts, final EntityInterface e) {
+  public static int deriveStage1Inserts(
+      final List<Property> stage1Inserts, final EntityInterface e) {
     // entity value
     if (e.hasValue()) {
       final Property p = new Property(e);
       p.setStatementStatus(StatementStatus.FIX);
-      p.setPIdx(0);
       p.setDatatype(e.getDatatype());
       p.setValue(e.getValue());
+      p.setPIdx(0);
       processPropertiesStage1(stage1Inserts, p, e);
     }
 
-    for (final EntityInterface p : e.getProperties()) {
+    for (final Property p : e.getProperties()) {
       processPropertiesStage1(stage1Inserts, p, e);
     }
+    int replacementCount = 0;
+    for (EntityInterface pp : stage1Inserts) {
+      if (pp instanceof Replacement) {
+        replacementCount++;
+      }
+    }
+    return replacementCount;
   }
 
   private static void processPropertiesStage1(
-      final List<EntityInterface> stage1Inserts, final EntityInterface p, final EntityInterface e) {
-    stage1Inserts.add(p);
+      final List<Property> stage1Inserts, final Property p, final EntityInterface e) {
+
     if (!p.isDescOverride()
         && !p.isNameOverride()
         && !p.isDatatypeOverride()
@@ -105,25 +98,40 @@ public class DatabaseUtils {
       // setReplacement(null) because there is a corner case (related to the inheritance of
       // properties) where there is a replacement present which belongs to the parent entity, see
       // https://gitlab.com/caosdb/caosdb-server/-/issues/216.
-      p.setReplacement(null);
+      stage1Inserts.add(p);
     } else {
-      stage1Inserts.add(makeReplacement(p));
+      Replacement r = new Replacement(p);
+      stage1Inserts.add(r);
+      stage1Inserts.add(r.replacement);
     }
     processSubPropertiesStage1(stage1Inserts, p);
   }
 
-  public static int deriveStage2Inserts(
-      final List<EntityInterface> stage2Inserts, final List<EntityInterface> stage1Inserts) {
-    int domainCount = 0;
-    for (final EntityInterface p : stage1Inserts) {
-      if (p.hasRole() && p.getRole() == Role.Domain) {
-        domainCount++;
-      }
-      if (!p.hasReplacement() && p.hasProperties()) {
-        stage2Inserts.addAll(p.getProperties());
+  public static void deriveStage2Inserts(
+      final List<Property> stage2Inserts,
+      final List<Property> stage1Inserts,
+      Deque<EntityID> replacementIds,
+      EntityInterface entity) {
+    for (final Property p : stage1Inserts) {
+      if (p instanceof Replacement) {
+
+        EntityID replacementId = replacementIds.pop();
+        ((Replacement) p).setReplacementId(replacementId);
+
+        if (p.hasProperties()) {
+          for (Property subP : p.getProperties()) {
+            subP.setDomain(p);
+            stage2Inserts.add(subP);
+          }
+        }
+      } else {
+        if (p.hasProperties()) {
+          for (Property subP : p.getProperties()) {
+            stage2Inserts.add(subP);
+          }
+        }
       }
     }
-    return domainCount;
   }
 
   private static boolean hasUniquePropertyId(final EntityInterface p, final EntityInterface e) {
@@ -137,10 +145,10 @@ public class DatabaseUtils {
   }
 
   private static void processSubPropertiesStage1(
-      final List<EntityInterface> stage1Inserts, final EntityInterface p) {
-    for (final EntityInterface subP : p.getProperties()) {
+      final List<Property> stage1Inserts, final EntityInterface p) {
+    for (final Property subP : p.getProperties()) {
       if (subP.hasProperties()) {
-        stage1Inserts.add(makeReplacement(subP));
+        stage1Inserts.add(new Replacement(subP));
         processSubPropertiesStage1(stage1Inserts, subP);
       }
     }
diff --git a/src/main/java/org/caosdb/server/database/Replacement.java b/src/main/java/org/caosdb/server/database/Replacement.java
new file mode 100644
index 00000000..8606d51b
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/Replacement.java
@@ -0,0 +1,41 @@
+package org.caosdb.server.database;
+
+import org.caosdb.server.datatype.ReferenceValue;
+import org.caosdb.server.entity.EntityID;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.wrapper.Property;
+
+public class Replacement extends Property {
+
+  public Property replacement;
+  public EntityID replacementId;
+
+  @Override
+  public EntityID getId() {
+    return replacementId;
+  }
+
+  public void setReplacementId(EntityID id) {
+    replacementId.link(id);
+  }
+
+  public Replacement(Property p) {
+    super(p);
+    replacementId = new EntityID();
+    replacement = new Property(new RetrieveEntity());
+
+    replacement.setDomain(p.getDomainEntity());
+    //    replacement.setDatatypeOverride(p.isDatatypeOverride());
+    //    replacement.setDatatype(p.getDatatype());
+    //    replacement.setNameOverride(p.isNameOverride());
+    //    replacement.setName(p.getName());
+    //    replacement.setDescOverride(p.isDescOverride());
+    //    replacement.setDescription(p.getDescription());
+
+    replacement.setId(replacementId);
+    replacement.setStatementStatus(StatementStatus.REPLACEMENT);
+    replacement.setValue(new ReferenceValue(p.getId()));
+    replacement.setPIdx(0);
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
index 7c3c5a33..5c9ca6f9 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
@@ -25,15 +25,30 @@ package org.caosdb.server.database.backend.implementation.MySQL;
 import static java.sql.Types.BIGINT;
 
 import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLIntegrityConstraintViolationException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.InsertEntityPropertiesImpl;
 import org.caosdb.server.database.exceptions.IntegrityException;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.FlatProperty;
+import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.AbstractDatatype.Table;
+import org.caosdb.server.datatype.CollectionValue;
+import org.caosdb.server.datatype.IndexedSingleValue;
+import org.caosdb.server.datatype.ReferenceValue;
+import org.caosdb.server.datatype.SingleValue;
 import org.caosdb.server.entity.EntityID;
+import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.wrapper.Property;
 
 public class MySQLInsertEntityProperties extends MySQLTransaction
     implements InsertEntityPropertiesImpl {
@@ -46,7 +61,27 @@ public class MySQLInsertEntityProperties extends MySQLTransaction
   }
 
   @Override
-  public void execute(
+  public void execute(EntityInterface entity) {
+    final List<Property> stage1Inserts = new LinkedList<>();
+    final List<Property> stage2Inserts = new LinkedList<>();
+
+    final int domainCount = DatabaseUtils.deriveStage1Inserts(stage1Inserts, entity);
+
+    try {
+      final Deque<EntityID> replacementIds = registerReplacementId(domainCount);
+
+      DatabaseUtils.deriveStage2Inserts(stage2Inserts, stage1Inserts, replacementIds, entity);
+
+      insertStages(stage1Inserts, entity.getDomain(), entity.getId());
+      insertStages(stage2Inserts, entity.getId(), null);
+    } catch (final SQLIntegrityConstraintViolationException exc) {
+      throw new IntegrityException(exc);
+    } catch (SQLException | ConnectionException e) {
+      throw new TransactionException(e);
+    }
+  }
+
+  private void insertFlatProperty(
       final EntityID domain,
       final EntityID entity,
       final FlatProperty fp,
@@ -76,10 +111,114 @@ public class MySQLInsertEntityProperties extends MySQLTransaction
       stmt.execute();
     } catch (final SQLIntegrityConstraintViolationException exc) {
       throw new IntegrityException(exc);
-    } catch (final SQLException exc) {
-      throw new TransactionException(exc);
-    } catch (final ConnectionException exc) {
+    } catch (final SQLException | ConnectionException exc) {
       throw new TransactionException(exc);
     }
   }
+
+  private void insertStages(
+      final List<Property> stage1Inserts, final EntityID domain, final EntityID entity)
+      throws TransactionException {
+
+    for (final Property property : stage1Inserts) {
+
+      // prepare flat property
+      final FlatProperty fp = new FlatProperty();
+      Table table = Table.null_data;
+      Long unit_sig = null;
+      fp.id = property.getId().toInteger();
+      fp.idx = property.getPIdx();
+      fp.status = property.getStatementStatus().name();
+
+      if (property.getStatementStatus() == StatementStatus.REPLACEMENT) {
+        table = Table.reference_data;
+        fp.value = fp.id.toString();
+        fp.id = ((ReferenceValue) property.getValue()).getId().toInteger();
+      } else {
+
+        if (property.hasUnit()) {
+          unit_sig = property.getUnit().getSignature();
+        }
+        if (property.hasValue()) {
+          if (property.getValue() instanceof CollectionValue) {
+            // insert collection of values
+            final CollectionValue v = (CollectionValue) property.getValue();
+            final Iterator<IndexedSingleValue> iterator = v.iterator();
+            final SingleValue firstValue = iterator.next();
+
+            // insert 2nd to nth item
+            for (int i = 1; i < v.size(); i++) {
+              final SingleValue vi = iterator.next();
+              fp.idx = i;
+              if (vi == null) {
+                fp.value = null;
+                table = Table.null_data;
+              } else {
+                fp.value = vi.toDatabaseString();
+                table = vi.getTable();
+              }
+              insertFlatProperty(
+                  domain, entity != null ? entity : property.getDomain(), fp, table, unit_sig);
+            }
+
+            // insert first item
+            fp.idx = 0;
+            if (firstValue == null) {
+              fp.value = null;
+              table = Table.null_data;
+            } else {
+              fp.value = firstValue.toDatabaseString();
+              table = firstValue.getTable();
+            }
+
+          } else {
+            // insert single value
+            fp.value = ((SingleValue) property.getValue()).toDatabaseString();
+            if (property instanceof Property && ((Property) property).isName()) {
+              table = Table.name_data;
+            } else {
+              table = ((SingleValue) property.getValue()).getTable();
+            }
+          }
+        }
+        if (property.isNameOverride()) {
+          fp.name = property.getName();
+        }
+        if (property.isDescOverride()) {
+          fp.desc = property.getDescription();
+        }
+        if (property.isDatatypeOverride()) {
+          if (property.getDatatype() instanceof AbstractCollectionDatatype) {
+            fp.type =
+                ((AbstractCollectionDatatype) property.getDatatype())
+                    .getDatatype()
+                    .getId()
+                    .toString();
+            fp.collection =
+                ((AbstractCollectionDatatype) property.getDatatype()).getCollectionName();
+          } else {
+            fp.type = property.getDatatype().getId().toString();
+          }
+        }
+      }
+
+      insertFlatProperty(
+          domain, entity != null ? entity : property.getDomain(), fp, table, unit_sig);
+    }
+  }
+
+  public static final String STMT_REGISTER_SUBDOMAIN = "call registerSubdomain(?)";
+
+  public Deque<EntityID> registerReplacementId(final int domainCount)
+      throws SQLException, ConnectionException {
+    final PreparedStatement stmt = prepareStatement(STMT_REGISTER_SUBDOMAIN);
+    stmt.setInt(1, domainCount);
+    try (final ResultSet rs = stmt.executeQuery()) {
+      final Deque<EntityID> ret = new ArrayDeque<>();
+      while (rs.next()) {
+        ret.add(new EntityID(rs.getInt(1)));
+      }
+      return ret;
+    }
+  }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRegisterSubDomain.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRegisterSubDomain.java
deleted file mode 100644
index 1e14600e..00000000
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRegisterSubDomain.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.implementation.MySQL;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import org.caosdb.server.database.access.Access;
-import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.entity.EntityID;
-
-public class MySQLRegisterSubDomain extends MySQLTransaction implements RegisterSubDomainImpl {
-
-  public MySQLRegisterSubDomain(final Access access) {
-    super(access);
-  }
-
-  public static final String STMT_REGISTER_SUBDOMAIN = "call registerSubdomain(?)";
-
-  @Override
-  public Deque<EntityID> execute(final int domainCount) throws TransactionException {
-    try {
-      final PreparedStatement stmt = prepareStatement(STMT_REGISTER_SUBDOMAIN);
-      stmt.setInt(1, domainCount);
-      final ResultSet rs = stmt.executeQuery();
-      try {
-        final Deque<EntityID> ret = new ArrayDeque<>();
-        while (rs.next()) {
-          ret.add(new EntityID(rs.getInt(1)));
-        }
-        return ret;
-      } finally {
-        rs.close();
-      }
-    } catch (final SQLException e) {
-      throw new TransactionException(e);
-    } catch (final ConnectionException e) {
-      throw new TransactionException(e);
-    }
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/InsertEntityPropertiesImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/InsertEntityPropertiesImpl.java
index 2406d489..f17643fd 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/InsertEntityPropertiesImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/InsertEntityPropertiesImpl.java
@@ -22,15 +22,10 @@
  */
 package org.caosdb.server.database.backend.interfaces;
 
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.FlatProperty;
-import org.caosdb.server.datatype.AbstractDatatype.Table;
-import org.caosdb.server.entity.EntityID;
+import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.utils.Undoable;
 
 public interface InsertEntityPropertiesImpl extends BackendTransactionImpl, Undoable {
 
-  public abstract void execute(
-      EntityID domain, EntityID entity, FlatProperty fp, Table table, Long unit_sig)
-      throws TransactionException;
+  public abstract void execute(EntityInterface entity);
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RegisterSubDomainImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RegisterSubDomainImpl.java
deleted file mode 100644
index 87ea0c85..00000000
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RegisterSubDomainImpl.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.interfaces;
-
-import java.util.Deque;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.entity.EntityID;
-
-public interface RegisterSubDomainImpl extends BackendTransactionImpl {
-
-  public abstract Deque<EntityID> execute(int domainCount) throws TransactionException;
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java
index f9af6a48..8de8afc0 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java
@@ -22,25 +22,10 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.List;
 import org.caosdb.server.database.BackendTransaction;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.backend.interfaces.InsertEntityPropertiesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.FlatProperty;
-import org.caosdb.server.datatype.AbstractCollectionDatatype;
-import org.caosdb.server.datatype.AbstractDatatype.Table;
-import org.caosdb.server.datatype.CollectionValue;
-import org.caosdb.server.datatype.IndexedSingleValue;
-import org.caosdb.server.datatype.SingleValue;
-import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.entity.Role;
-import org.caosdb.server.entity.StatementStatus;
-import org.caosdb.server.entity.wrapper.Property;
 
 public class InsertEntityProperties extends BackendTransaction {
 
@@ -54,126 +39,6 @@ public class InsertEntityProperties extends BackendTransaction {
   public void execute() throws TransactionException {
 
     final InsertEntityPropertiesImpl t = getImplementation(InsertEntityPropertiesImpl.class);
-
-    final ArrayList<EntityInterface> stage1Inserts = new ArrayList<EntityInterface>();
-    final ArrayList<EntityInterface> stage2Inserts = new ArrayList<EntityInterface>();
-
-    DatabaseUtils.deriveStage1Inserts(stage1Inserts, this.entity);
-
-    final int domainCount = DatabaseUtils.deriveStage2Inserts(stage2Inserts, stage1Inserts);
-
-    final Deque<EntityID> domainIds = execute(new RegisterSubDomain(domainCount)).getDomains();
-
-    insertStages(t, domainIds, stage1Inserts, this.entity.getDomain(), this.entity.getId());
-    insertStages(t, domainIds, stage2Inserts, this.entity.getId(), null);
-  }
-
-  private void insertStages(
-      final InsertEntityPropertiesImpl t,
-      final Deque<EntityID> domainIds,
-      final List<EntityInterface> stage1Inserts,
-      final EntityID domain,
-      final EntityID entity)
-      throws TransactionException {
-
-    for (final EntityInterface property : stage1Inserts) {
-      if (property.hasRole() && property.getRole() == Role.Domain && !property.hasId()) {
-        property.setId(domainIds.removeFirst());
-      }
-      int pIdx;
-      if (property instanceof Property) {
-        // this is a normal property
-        pIdx = ((Property) property).getPIdx();
-      } else {
-        // this is a replacement
-        pIdx = 0;
-      }
-
-      // prepare flat property
-      final FlatProperty fp = new FlatProperty();
-      Table table = Table.null_data;
-      Long unit_sig = null;
-      fp.id = property.getId().toInteger();
-      fp.idx = pIdx;
-
-      if (property.hasReplacement()) {
-        if (!property.getReplacement().hasId()) {
-          property.getReplacement().setId(domainIds.removeFirst());
-        }
-
-        fp.value = property.getReplacement().getId().toString();
-        fp.status = StatementStatus.REPLACEMENT.toString();
-
-        table = Table.reference_data;
-      } else {
-        if (property.hasUnit()) {
-          unit_sig = property.getUnit().getSignature();
-        }
-        fp.status = property.getStatementStatus().toString();
-        if (property.hasValue()) {
-          if (property.getValue() instanceof CollectionValue) {
-            // insert collection of values
-            final CollectionValue v = (CollectionValue) property.getValue();
-            final Iterator<IndexedSingleValue> iterator = v.iterator();
-            final SingleValue firstValue = iterator.next();
-
-            // insert 2nd to nth item
-            for (int i = 1; i < v.size(); i++) {
-              final SingleValue vi = iterator.next();
-              fp.idx = i;
-              if (vi == null) {
-                fp.value = null;
-                table = Table.null_data;
-              } else {
-                fp.value = vi.toDatabaseString();
-                table = vi.getTable();
-              }
-              t.execute(
-                  domain, entity != null ? entity : property.getDomain(), fp, table, unit_sig);
-            }
-
-            // insert first item
-            fp.idx = 0;
-            if (firstValue == null) {
-              fp.value = null;
-              table = Table.null_data;
-            } else {
-              fp.value = firstValue.toDatabaseString();
-              table = firstValue.getTable();
-            }
-
-          } else {
-            // insert single value
-            fp.value = ((SingleValue) property.getValue()).toDatabaseString();
-            if (property instanceof Property && ((Property) property).isName()) {
-              table = Table.name_data;
-            } else {
-              table = ((SingleValue) property.getValue()).getTable();
-            }
-          }
-        }
-        if (property.isNameOverride()) {
-          fp.name = property.getName();
-        }
-        if (property.isDescOverride()) {
-          fp.desc = property.getDescription();
-        }
-        if (property.isDatatypeOverride()) {
-          if (property.getDatatype() instanceof AbstractCollectionDatatype) {
-            fp.type =
-                ((AbstractCollectionDatatype) property.getDatatype())
-                    .getDatatype()
-                    .getId()
-                    .toString();
-            fp.collection =
-                ((AbstractCollectionDatatype) property.getDatatype()).getCollectionName();
-          } else {
-            fp.type = property.getDatatype().getId().toString();
-          }
-        }
-      }
-
-      t.execute(domain, entity != null ? entity : property.getDomain(), fp, table, unit_sig);
-    }
+    t.execute(this.entity);
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
index 5871c97e..2846fa64 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
@@ -54,7 +54,9 @@ public class InsertEntityTransaction extends BackendTransaction {
         if (newEntity.hasFileProperties()) {
           execute(new InsertFile(newEntity));
         }
-        execute(new InsertEntityProperties(newEntity));
+        if (newEntity.hasProperties() || newEntity.hasValue()) {
+          execute(new InsertEntityProperties(newEntity));
+        }
         execute(new InsertParents(newEntity));
 
         if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED) {
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RegisterSubDomain.java b/src/main/java/org/caosdb/server/database/backend/transaction/RegisterSubDomain.java
deleted file mode 100644
index 0321a621..00000000
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RegisterSubDomain.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.transaction;
-
-import java.util.Deque;
-import org.caosdb.server.database.BackendTransaction;
-import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.entity.EntityID;
-
-public class RegisterSubDomain extends BackendTransaction {
-
-  private final int domainCount;
-  private Deque<EntityID> list;
-
-  public RegisterSubDomain(final int domainCount) {
-    this.domainCount = domainCount;
-  }
-
-  @Override
-  public void execute() throws TransactionException {
-    final RegisterSubDomainImpl t = getImplementation(RegisterSubDomainImpl.class);
-    this.list = t.execute(this.domainCount);
-  }
-
-  public Deque<EntityID> getDomains() {
-    return this.list;
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
index b92d3def..b4f0f62a 100644
--- a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
+++ b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
@@ -26,6 +26,19 @@ import java.io.Serializable;
 
 public class FlatProperty implements Serializable {
 
+  public FlatProperty() {}
+
+  public FlatProperty(FlatProperty fp) {
+    id = fp.id;
+    value = fp.value;
+    status = fp.status;
+    idx = fp.idx;
+    name = fp.name;
+    desc = fp.desc;
+    type = fp.type;
+    collection = fp.collection;
+  }
+
   private static final long serialVersionUID = 6039288034435124195L;
   public Integer id = null;
   public String value = null;
diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
index 898565ac..c6cdbd54 100644
--- a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
+++ b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
@@ -26,21 +26,14 @@ import java.io.Serializable;
 import java.util.List;
 
 public class ProtoProperty extends FlatProperty implements Serializable {
+  private static final long serialVersionUID = 7731301985162924975L;
 
   public ProtoProperty(FlatProperty fp) {
-    id = fp.id;
-    value = fp.value;
-    status = fp.status;
-    idx = fp.idx;
-    name = fp.name;
-    desc = fp.desc;
-    type = fp.type;
-    collection = fp.collection;
+    super(fp);
   }
 
   public ProtoProperty() {}
 
-  private static final long serialVersionUID = 7731301985162924975L;
   public List<ProtoProperty> subProperties = null;
   public List<Object> collValues = null;
 }
diff --git a/src/test/java/org/caosdb/server/database/InsertTest.java b/src/test/java/org/caosdb/server/database/InsertTest.java
index 59aa6f55..e385d6cb 100644
--- a/src/test/java/org/caosdb/server/database/InsertTest.java
+++ b/src/test/java/org/caosdb/server/database/InsertTest.java
@@ -28,13 +28,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.ArrayDeque;
+import java.util.Deque;
 import java.util.LinkedList;
 import org.caosdb.server.datatype.CollectionValue;
 import org.caosdb.server.datatype.GenericValue;
+import org.caosdb.server.datatype.ReferenceValue;
 import org.caosdb.server.datatype.SingleValue;
 import org.caosdb.server.entity.Entity;
 import org.caosdb.server.entity.EntityID;
-import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.InsertEntity;
 import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.Role;
@@ -44,6 +46,14 @@ import org.junit.jupiter.api.Test;
 
 public class InsertTest {
 
+  private Deque<EntityID> registerReplacementIds(int count) {
+    Deque<EntityID> replacementIds = new ArrayDeque<>();
+    for (int i = 1; i < count + 1; i++) {
+      replacementIds.add(new EntityID(-i));
+    }
+    return replacementIds;
+  }
+
   /**
    * Record with very deep property tree.
    *
@@ -122,85 +132,67 @@ public class InsertTest {
     p10.setStatementStatus(StatementStatus.OBLIGATORY);
     p9.addProperty(p10);
 
-    final LinkedList<EntityInterface> stage1Inserts = new LinkedList<EntityInterface>();
-    final LinkedList<EntityInterface> stage2Inserts = new LinkedList<EntityInterface>();
+    final LinkedList<Property> stage1Inserts = new LinkedList<>();
+    final LinkedList<Property> stage2Inserts = new LinkedList<>();
 
-    deriveStage1Inserts(stage1Inserts, r);
+    int replacementCount = deriveStage1Inserts(stage1Inserts, r);
+    assertEquals(3, replacementCount);
+
+    deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(replacementCount), r);
 
     assertEquals(7, stage1Inserts.size());
-    assertEquals(Role.Property, stage1Inserts.get(0).getRole());
-    assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
+    assertFalse(stage1Inserts.get(0) instanceof Replacement);
+    assertEquals(1, stage1Inserts.get(0).getId().toInteger());
     assertEquals("V1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(0).hasReplacement());
 
-    assertEquals(Role.Property, stage1Inserts.get(1).getRole());
-    assertEquals(new EntityID(2), stage1Inserts.get(1).getId());
+    assertFalse(stage1Inserts.get(1) instanceof Replacement);
+    assertEquals(2, stage1Inserts.get(1).getId().toInteger());
     assertEquals("V2", ((SingleValue) stage1Inserts.get(1).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(1).hasReplacement());
 
-    assertEquals(Role.Property, stage1Inserts.get(2).getRole());
-    assertEquals(new EntityID(4), stage1Inserts.get(2).getId());
+    assertFalse(stage1Inserts.get(2) instanceof Replacement);
+    assertEquals(4, stage1Inserts.get(2).getId().toInteger());
     assertEquals("V4", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(2).hasReplacement());
 
-    assertEquals(Role.Domain, stage1Inserts.get(3).getRole());
+    assertTrue(stage1Inserts.get(3) instanceof Replacement);
+    assertEquals(5, stage1Inserts.get(3).getId().toInteger());
     assertEquals("V5", ((SingleValue) stage1Inserts.get(3).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(3).hasReplacement());
 
-    assertEquals(Role.Property, stage1Inserts.get(4).getRole());
-    assertEquals(new EntityID(7), stage1Inserts.get(4).getId());
+    assertFalse(stage1Inserts.get(4) instanceof Replacement);
+    assertEquals(7, stage1Inserts.get(4).getId().toInteger());
     assertEquals("V7", ((SingleValue) stage1Inserts.get(4).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(4).hasReplacement());
 
-    assertEquals(Role.Domain, stage1Inserts.get(5).getRole());
+    assertTrue(stage1Inserts.get(5) instanceof Replacement);
+    assertEquals(8, stage1Inserts.get(5).getId().toInteger());
     assertEquals("V8", ((SingleValue) stage1Inserts.get(5).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(5).hasReplacement());
 
-    assertEquals(Role.Domain, stage1Inserts.get(6).getRole());
+    assertTrue(stage1Inserts.get(6) instanceof Replacement);
+    assertEquals(9, stage1Inserts.get(6).getId().toInteger());
     assertEquals("V9", ((SingleValue) stage1Inserts.get(6).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(6).hasReplacement());
-
-    deriveStage2Inserts(stage2Inserts, stage1Inserts);
 
     assertEquals(6, stage2Inserts.size());
-    assertEquals(Role.Property, stage2Inserts.get(0).getRole());
     assertEquals(new EntityID(3), stage2Inserts.get(0).getId());
     assertEquals("V3", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString());
     assertEquals(new EntityID(2), stage2Inserts.get(0).getDomain());
-    assertFalse(stage2Inserts.get(0).hasReplacement());
 
-    assertEquals(Role.Property, stage2Inserts.get(1).getRole());
     assertEquals(new EntityID(5), stage2Inserts.get(1).getId());
     assertEquals("V5", ((SingleValue) stage2Inserts.get(1).getValue()).toDatabaseString());
-    assertTrue(stage2Inserts.get(1).hasReplacement());
     assertEquals(new EntityID(4), stage2Inserts.get(1).getDomain());
-    assertEquals(stage1Inserts.get(3), stage2Inserts.get(1).getReplacement());
 
-    assertEquals(Role.Property, stage2Inserts.get(2).getRole());
     assertEquals(new EntityID(6), stage2Inserts.get(2).getId());
     assertEquals("V6", ((SingleValue) stage2Inserts.get(2).getValue()).toDatabaseString());
-    assertEquals(new EntityID(5), stage2Inserts.get(2).getDomain());
-    assertFalse(stage2Inserts.get(2).hasReplacement());
+    assertEquals(new EntityID(-1), stage2Inserts.get(2).getDomain());
 
-    assertEquals(Role.Property, stage2Inserts.get(3).getRole());
     assertEquals(new EntityID(8), stage2Inserts.get(3).getId());
     assertEquals("V8", ((SingleValue) stage2Inserts.get(3).getValue()).toDatabaseString());
-    assertTrue(stage2Inserts.get(3).hasReplacement());
     assertEquals(new EntityID(7), stage2Inserts.get(3).getDomain());
-    assertEquals(stage1Inserts.get(5), stage2Inserts.get(3).getReplacement());
 
-    assertEquals(Role.Property, stage2Inserts.get(4).getRole());
     assertEquals(new EntityID(9), stage2Inserts.get(4).getId());
     assertEquals("V9", ((SingleValue) stage2Inserts.get(4).getValue()).toDatabaseString());
-    assertTrue(stage2Inserts.get(4).hasReplacement());
-    assertEquals(new EntityID(8), stage2Inserts.get(4).getDomain());
-    assertEquals(stage1Inserts.get(6), stage2Inserts.get(4).getReplacement());
+    assertEquals(new EntityID(-2), stage2Inserts.get(4).getDomain());
 
-    assertEquals(Role.Property, stage2Inserts.get(5).getRole());
     assertEquals(new EntityID(10), stage2Inserts.get(5).getId());
     assertEquals("V10", ((SingleValue) stage2Inserts.get(5).getValue()).toDatabaseString());
-    assertEquals(new EntityID(9), stage2Inserts.get(5).getDomain());
-    assertFalse(stage2Inserts.get(5).hasReplacement());
+    assertEquals(new EntityID(-3), stage2Inserts.get(5).getDomain());
   }
 
   /**
@@ -233,34 +225,31 @@ public class InsertTest {
     subp.setStatementStatus(StatementStatus.FIX);
     p2.addProperty(subp);
 
-    final LinkedList<EntityInterface> stage1Inserts = new LinkedList<EntityInterface>();
-    final LinkedList<EntityInterface> stage2Inserts = new LinkedList<EntityInterface>();
+    final LinkedList<Property> stage1Inserts = new LinkedList<>();
+    final LinkedList<Property> stage2Inserts = new LinkedList<>();
 
-    deriveStage1Inserts(stage1Inserts, r);
+    int replacementCount = deriveStage1Inserts(stage1Inserts, r);
+    deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(replacementCount), r);
 
     assertEquals(3, stage1Inserts.size());
-    assertEquals(Role.Property, stage1Inserts.get(0).getRole());
+
+    assertFalse(stage1Inserts.get(0) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
     assertEquals("V1-1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(0).hasReplacement());
 
-    assertEquals(Role.Property, stage1Inserts.get(1).getRole());
+    assertTrue(stage1Inserts.get(1) instanceof Replacement);
     assertEquals("V1-2", ((SingleValue) stage1Inserts.get(1).getValue()).toDatabaseString());
     assertEquals(new EntityID(1), stage1Inserts.get(1).getId());
-    assertTrue(stage1Inserts.get(1).hasReplacement());
-    assertEquals(stage1Inserts.get(2), stage1Inserts.get(1).getReplacement());
-
-    assertEquals(Role.Domain, stage1Inserts.get(2).getRole());
-    assertEquals("V1-2", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString());
-    assertFalse(stage1Inserts.get(2).hasReplacement());
 
-    deriveStage2Inserts(stage2Inserts, stage1Inserts);
+    assertFalse(stage1Inserts.get(2) instanceof Replacement);
+    assertEquals(new EntityID(-1), stage1Inserts.get(2).getId());
+    assertEquals("1", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString());
 
     assertEquals(1, stage2Inserts.size());
-    assertEquals(Role.Property, stage2Inserts.get(0).getRole());
+
+    assertFalse(stage2Inserts.get(0) instanceof Replacement);
     assertEquals(new EntityID(2), stage2Inserts.get(0).getId());
     assertEquals("V2", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString());
-    assertFalse(stage2Inserts.get(0).hasReplacement());
   }
 
   /**
@@ -297,39 +286,33 @@ public class InsertTest {
     sub1.setStatementStatus(StatementStatus.FIX);
     p1.addProperty(sub1);
 
-    final LinkedList<EntityInterface> stage1Inserts = new LinkedList<EntityInterface>();
-    final LinkedList<EntityInterface> stage2Inserts = new LinkedList<EntityInterface>();
+    final LinkedList<Property> stage1Inserts = new LinkedList<>();
+    final LinkedList<Property> stage2Inserts = new LinkedList<>();
 
-    deriveStage1Inserts(stage1Inserts, r);
+    int count = deriveStage1Inserts(stage1Inserts, r);
+    deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(count), r);
 
     assertEquals(4, stage1Inserts.size());
-    assertEquals(Role.Property, stage1Inserts.get(0).getRole());
+    assertTrue(stage1Inserts.get(0) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
-    assertTrue(stage1Inserts.get(0).hasReplacement());
-    assertEquals(stage1Inserts.get(1), stage1Inserts.get(0).getReplacement());
     assertEquals(null, stage1Inserts.get(0).getValue());
 
-    assertEquals(Role.Domain, stage1Inserts.get(1).getRole());
-    assertFalse(stage1Inserts.get(1).hasReplacement());
-    assertEquals(null, stage1Inserts.get(1).getValue());
+    assertFalse(stage1Inserts.get(1) instanceof Replacement);
+    assertEquals(new EntityID(-1), stage1Inserts.get(1).getId());
+    assertEquals(1, ((ReferenceValue) stage1Inserts.get(1).getValue()).getId().toInteger());
 
-    assertEquals(Role.Property, stage1Inserts.get(2).getRole());
+    assertFalse(stage1Inserts.get(2) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(2).getId());
-    assertFalse(stage1Inserts.get(2).hasReplacement());
     assertEquals(null, stage1Inserts.get(2).getValue());
 
-    assertEquals(Role.Property, stage1Inserts.get(3).getRole());
+    assertFalse(stage1Inserts.get(3) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(3).getId());
-    assertFalse(stage1Inserts.get(3).hasReplacement());
     assertEquals(null, stage1Inserts.get(3).getValue());
 
-    deriveStage2Inserts(stage2Inserts, stage1Inserts);
-
     assertEquals(1, stage2Inserts.size());
-    assertEquals(Role.Property, stage2Inserts.get(0).getRole());
     assertEquals(new EntityID(2), stage2Inserts.get(0).getId());
     assertEquals("V1", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString());
-    assertFalse(stage2Inserts.get(0).hasReplacement());
+    assertEquals(new EntityID(-1), stage2Inserts.get(0).getDomain());
   }
 
   /**
@@ -366,32 +349,31 @@ public class InsertTest {
     p3.setStatementStatus(StatementStatus.FIX);
     r.addProperty(p3);
 
-    final LinkedList<EntityInterface> stage1Inserts = new LinkedList<EntityInterface>();
-    final LinkedList<EntityInterface> stage2Inserts = new LinkedList<EntityInterface>();
+    final LinkedList<Property> stage1Inserts = new LinkedList<>();
+    final LinkedList<Property> stage2Inserts = new LinkedList<>();
 
-    deriveStage1Inserts(stage1Inserts, r);
+    int count = deriveStage1Inserts(stage1Inserts, r);
+    deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(count), r);
 
     assertEquals(4, stage1Inserts.size());
-    assertEquals(Role.Property, stage1Inserts.get(0).getRole());
+
+    assertFalse(stage1Inserts.get(0) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
     assertFalse(stage1Inserts.get(0).hasReplacement());
     assertEquals("V1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString());
 
-    assertEquals(Role.Property, stage1Inserts.get(1).getRole());
+    assertTrue(stage1Inserts.get(1) instanceof Replacement);
     assertEquals(new EntityID(2), stage1Inserts.get(1).getId());
-    assertTrue(stage1Inserts.get(1).hasReplacement());
-    assertEquals(stage1Inserts.get(2), stage1Inserts.get(1).getReplacement());
     assertTrue(stage1Inserts.get(1).getValue() instanceof CollectionValue);
 
-    assertEquals(Role.Domain, stage1Inserts.get(2).getRole());
-    assertFalse(stage1Inserts.get(2).hasReplacement());
-    assertTrue(stage1Inserts.get(2).getValue() instanceof CollectionValue);
+    assertFalse(stage1Inserts.get(2) instanceof Replacement);
+    assertEquals(new EntityID(-1), stage1Inserts.get(2).getId());
+    assertEquals(2, ((ReferenceValue) stage1Inserts.get(2).getValue()).getId().toInteger());
 
-    assertEquals(Role.Property, stage1Inserts.get(3).getRole());
+    assertFalse(stage1Inserts.get(3) instanceof Replacement);
     assertEquals(new EntityID(3), stage1Inserts.get(3).getId());
-    assertFalse(stage1Inserts.get(3).hasReplacement());
     assertEquals("V3", ((SingleValue) stage1Inserts.get(3).getValue()).toDatabaseString());
 
-    deriveStage2Inserts(stage2Inserts, stage1Inserts);
+    assertEquals(0, stage2Inserts.size());
   }
 }
diff --git a/src/test/java/org/caosdb/server/database/InsertTest2.java b/src/test/java/org/caosdb/server/database/InsertTest2.java
new file mode 100644
index 00000000..58cc5850
--- /dev/null
+++ b/src/test/java/org/caosdb/server/database/InsertTest2.java
@@ -0,0 +1,148 @@
+package org.caosdb.server.database;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import org.caosdb.server.entity.Entity;
+import org.caosdb.server.entity.EntityID;
+import org.caosdb.server.entity.InsertEntity;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.Role;
+import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.wrapper.Property;
+import org.junit.jupiter.api.Test;
+
+public class InsertTest2 {
+
+  private Deque<EntityID> registerReplacementIds(int count) {
+    Deque<EntityID> replacementIds = new ArrayDeque<>();
+    for (int i = 1; i < count + 1; i++) {
+      replacementIds.add(new EntityID(-i));
+    }
+    return replacementIds;
+  }
+
+  @Test
+  public void transformation5() {
+    final Entity r = new InsertEntity("Test", Role.RecordType);
+    final Property p1 = new Property(new RetrieveEntity(new EntityID(1)));
+    p1.setRole("Property");
+    p1.setDatatype("TEXT");
+    p1.setDescription("desc1");
+    p1.setDescOverride(true);
+    p1.setName("P1");
+    p1.setNameOverride(true);
+    p1.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p1);
+
+    final Property p2 = new Property(new RetrieveEntity(new EntityID(2)));
+    p2.setRole("Property");
+    p2.setDatatype("TEXT");
+    p2.setDescription("desc2");
+    p2.setDescOverride(true);
+    p2.setName("P2");
+    p2.setNameOverride(true);
+    p2.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p2);
+
+    final Property p21 = new Property(new RetrieveEntity(new EntityID(1)));
+    p21.setRole("Property");
+    p21.setDatatype("TEXT");
+    p21.setDescription("desc21");
+    p21.setDescOverride(true);
+    p21.setName("P21");
+    p21.setNameOverride(true);
+    p21.setStatementStatus(StatementStatus.RECOMMENDED);
+    p2.addProperty(p21);
+
+    final Property p22 = new Property(new RetrieveEntity(new EntityID(2)));
+    p22.setRole("Property");
+    p22.setDatatype("TEXT");
+    p22.setDescription("desc22");
+    p22.setDescOverride(true);
+    p22.setName("P22");
+    p22.setNameOverride(true);
+    p22.setStatementStatus(StatementStatus.RECOMMENDED);
+    p2.addProperty(p22);
+
+    final Property p3 = new Property(new RetrieveEntity(new EntityID(3)));
+    p3.setRole("Property");
+    p3.setDatatype("TEXT");
+    p3.setDescription("desc3");
+    p3.setDescOverride(true);
+    p3.setName("P3");
+    p3.setNameOverride(true);
+    p3.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p3);
+
+    final Property p31 = new Property(new RetrieveEntity(new EntityID(1)));
+    p31.setRole("Property");
+    p31.setDatatype("TEXT");
+    p31.setDescription("desc31");
+    p31.setDescOverride(true);
+    p31.setName("P31");
+    p31.setNameOverride(true);
+    p31.setStatementStatus(StatementStatus.RECOMMENDED);
+    p3.addProperty(p31);
+
+    final Property p32 = new Property(new RetrieveEntity(new EntityID(2)));
+    p32.setRole("Property");
+    p32.setDatatype("TEXT");
+    p32.setDescription("desc32");
+    p32.setDescOverride(true);
+    p32.setName("P32");
+    p32.setNameOverride(true);
+    p32.setStatementStatus(StatementStatus.RECOMMENDED);
+    p3.addProperty(p32);
+
+    final Property p321 = new Property(new RetrieveEntity(new EntityID(1)));
+    p321.setRole("Property");
+    p321.setDatatype("TEXT");
+    p321.setDescription("desc321");
+    p321.setDescOverride(true);
+    p321.setName("P321");
+    p321.setNameOverride(true);
+    p321.setStatementStatus(StatementStatus.RECOMMENDED);
+    p32.addProperty(p321);
+
+    final Property p322 = new Property(new RetrieveEntity(new EntityID(2)));
+    p322.setRole("Property");
+    p322.setDatatype("TEXT");
+    p322.setDescription("desc322");
+    p322.setDescOverride(true);
+    p322.setName("P322");
+    p322.setNameOverride(true);
+    p322.setStatementStatus(StatementStatus.RECOMMENDED);
+    p32.addProperty(p322);
+
+    final Property p323 = new Property(new RetrieveEntity(new EntityID(3)));
+    p323.setRole("Property");
+    p323.setDatatype("TEXT");
+    p323.setDescription("desc323");
+    p323.setDescOverride(true);
+    p323.setName("P323");
+    p323.setNameOverride(true);
+    p323.setStatementStatus(StatementStatus.RECOMMENDED);
+    p32.addProperty(p323);
+
+    final Property p33 = new Property(new RetrieveEntity(new EntityID(3)));
+    p33.setRole("Property");
+    p33.setDatatype("TEXT");
+    p33.setDescription("desc33");
+    p33.setDescOverride(true);
+    p33.setName("P33");
+    p33.setNameOverride(true);
+    p33.setStatementStatus(StatementStatus.RECOMMENDED);
+    p3.addProperty(p33);
+
+    List<Property> stage1Inserts = new LinkedList<>();
+    List<Property> stage2Inserts = new LinkedList<>();
+    int c = DatabaseUtils.deriveStage1Inserts(stage1Inserts, r);
+    DatabaseUtils.deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(c), r);
+    assertEquals(7, stage1Inserts.size());
+    assertEquals(8, stage2Inserts.size());
+  }
+}
-- 
GitLab


From d186c1b94b24f52cd2919660401431e9fcfab18c Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 00:10:26 +0200
Subject: [PATCH 4/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../java/org/caosdb/server/entity/Entity.java |  18 ---
 .../caosdb/server/entity/EntityInterface.java |   7 -
 .../caosdb/server/entity/wrapper/Domain.java  |  59 -------
 .../server/entity/wrapper/EntityWrapper.java  |  15 --
 .../caosdb/server/database/InsertTest.java    | 138 +++++++++++++++-
 .../caosdb/server/database/InsertTest2.java   | 148 ------------------
 6 files changed, 131 insertions(+), 254 deletions(-)
 delete mode 100644 src/main/java/org/caosdb/server/entity/wrapper/Domain.java
 delete mode 100644 src/test/java/org/caosdb/server/database/InsertTest2.java

diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java
index e1acfbdc..843449ce 100644
--- a/src/main/java/org/caosdb/server/entity/Entity.java
+++ b/src/main/java/org/caosdb/server/entity/Entity.java
@@ -47,7 +47,6 @@ import org.caosdb.server.datatype.Value;
 import org.caosdb.server.entity.Message.MessageType;
 import org.caosdb.server.entity.container.ParentContainer;
 import org.caosdb.server.entity.container.PropertyContainer;
-import org.caosdb.server.entity.wrapper.Domain;
 import org.caosdb.server.entity.wrapper.Parent;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.entity.xml.EntityToElementStrategy;
@@ -849,23 +848,6 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa
     }
   }
 
-  @Override
-  public void setReplacement(final Domain d) {
-    this.replacement = d;
-  }
-
-  @Override
-  public boolean hasReplacement() {
-    return this.replacement != null;
-  }
-
-  @Override
-  public Domain getReplacement() {
-    return this.replacement;
-  }
-
-  private Domain replacement = null;
-
   private final HashMap<String, String> flags = new HashMap<String, String>();
 
   @Override
diff --git a/src/main/java/org/caosdb/server/entity/EntityInterface.java b/src/main/java/org/caosdb/server/entity/EntityInterface.java
index f967b307..d97970de 100644
--- a/src/main/java/org/caosdb/server/entity/EntityInterface.java
+++ b/src/main/java/org/caosdb/server/entity/EntityInterface.java
@@ -31,7 +31,6 @@ import org.caosdb.server.datatype.AbstractDatatype;
 import org.caosdb.server.datatype.Value;
 import org.caosdb.server.entity.container.ParentContainer;
 import org.caosdb.server.entity.container.PropertyContainer;
-import org.caosdb.server.entity.wrapper.Domain;
 import org.caosdb.server.entity.wrapper.Parent;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.entity.xml.SerializeFieldStrategy;
@@ -127,12 +126,6 @@ public interface EntityInterface
 
   public abstract void setProperties(PropertyContainer properties);
 
-  public abstract void setReplacement(Domain d);
-
-  public abstract boolean hasReplacement();
-
-  public abstract Domain getReplacement();
-
   public abstract EntityInterface setDescOverride(boolean b);
 
   public abstract boolean isDescOverride();
diff --git a/src/main/java/org/caosdb/server/entity/wrapper/Domain.java b/src/main/java/org/caosdb/server/entity/wrapper/Domain.java
deleted file mode 100644
index 146706ef..00000000
--- a/src/main/java/org/caosdb/server/entity/wrapper/Domain.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.entity.wrapper;
-
-import org.caosdb.server.datatype.AbstractDatatype;
-import org.caosdb.server.datatype.Value;
-import org.caosdb.server.entity.Entity;
-import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.entity.Role;
-import org.caosdb.server.entity.container.PropertyContainer;
-
-public class Domain extends Entity {
-
-  private boolean descO;
-
-  public Domain(
-      final PropertyContainer properties,
-      final AbstractDatatype datatype,
-      final Value value,
-      final org.caosdb.server.entity.StatementStatus statementStatus) {
-
-    setRole(Role.Domain);
-    setProperties(properties);
-    setDatatype(datatype);
-    setValue(value);
-    setStatementStatus(statementStatus);
-  }
-
-  @Override
-  public EntityInterface setDescOverride(final boolean b) {
-    this.descO = b;
-    return this;
-  }
-
-  @Override
-  public boolean isDescOverride() {
-    return this.descO;
-  }
-}
diff --git a/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java b/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
index 7da94573..f23408df 100644
--- a/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
+++ b/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
@@ -376,21 +376,6 @@ public abstract class EntityWrapper implements EntityInterface {
     this.entity.setProperties(properties);
   }
 
-  @Override
-  public void setReplacement(final Domain d) {
-    this.entity.setReplacement(d);
-  }
-
-  @Override
-  public boolean hasReplacement() {
-    return this.entity.hasReplacement();
-  }
-
-  @Override
-  public Domain getReplacement() {
-    return this.entity.getReplacement();
-  }
-
   @Override
   public Map<String, String> getFlags() {
     return this.entity.getFlags();
diff --git a/src/test/java/org/caosdb/server/database/InsertTest.java b/src/test/java/org/caosdb/server/database/InsertTest.java
index e385d6cb..0484e623 100644
--- a/src/test/java/org/caosdb/server/database/InsertTest.java
+++ b/src/test/java/org/caosdb/server/database/InsertTest.java
@@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.LinkedList;
+import java.util.List;
 import org.caosdb.server.datatype.CollectionValue;
 import org.caosdb.server.datatype.GenericValue;
 import org.caosdb.server.datatype.ReferenceValue;
@@ -154,7 +155,7 @@ public class InsertTest {
     assertEquals("V4", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString());
 
     assertTrue(stage1Inserts.get(3) instanceof Replacement);
-    assertEquals(5, stage1Inserts.get(3).getId().toInteger());
+    assertEquals(-1, stage1Inserts.get(3).getId().toInteger());
     assertEquals("V5", ((SingleValue) stage1Inserts.get(3).getValue()).toDatabaseString());
 
     assertFalse(stage1Inserts.get(4) instanceof Replacement);
@@ -162,11 +163,11 @@ public class InsertTest {
     assertEquals("V7", ((SingleValue) stage1Inserts.get(4).getValue()).toDatabaseString());
 
     assertTrue(stage1Inserts.get(5) instanceof Replacement);
-    assertEquals(8, stage1Inserts.get(5).getId().toInteger());
+    assertEquals(-2, stage1Inserts.get(5).getId().toInteger());
     assertEquals("V8", ((SingleValue) stage1Inserts.get(5).getValue()).toDatabaseString());
 
     assertTrue(stage1Inserts.get(6) instanceof Replacement);
-    assertEquals(9, stage1Inserts.get(6).getId().toInteger());
+    assertEquals(-3, stage1Inserts.get(6).getId().toInteger());
     assertEquals("V9", ((SingleValue) stage1Inserts.get(6).getValue()).toDatabaseString());
 
     assertEquals(6, stage2Inserts.size());
@@ -239,7 +240,7 @@ public class InsertTest {
 
     assertTrue(stage1Inserts.get(1) instanceof Replacement);
     assertEquals("V1-2", ((SingleValue) stage1Inserts.get(1).getValue()).toDatabaseString());
-    assertEquals(new EntityID(1), stage1Inserts.get(1).getId());
+    assertEquals(new EntityID(-1), stage1Inserts.get(1).getId());
 
     assertFalse(stage1Inserts.get(2) instanceof Replacement);
     assertEquals(new EntityID(-1), stage1Inserts.get(2).getId());
@@ -294,7 +295,7 @@ public class InsertTest {
 
     assertEquals(4, stage1Inserts.size());
     assertTrue(stage1Inserts.get(0) instanceof Replacement);
-    assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
+    assertEquals(new EntityID(-1), stage1Inserts.get(0).getId());
     assertEquals(null, stage1Inserts.get(0).getValue());
 
     assertFalse(stage1Inserts.get(1) instanceof Replacement);
@@ -359,11 +360,10 @@ public class InsertTest {
 
     assertFalse(stage1Inserts.get(0) instanceof Replacement);
     assertEquals(new EntityID(1), stage1Inserts.get(0).getId());
-    assertFalse(stage1Inserts.get(0).hasReplacement());
     assertEquals("V1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString());
 
     assertTrue(stage1Inserts.get(1) instanceof Replacement);
-    assertEquals(new EntityID(2), stage1Inserts.get(1).getId());
+    assertEquals(new EntityID(-1), stage1Inserts.get(1).getId());
     assertTrue(stage1Inserts.get(1).getValue() instanceof CollectionValue);
 
     assertFalse(stage1Inserts.get(2) instanceof Replacement);
@@ -376,4 +376,128 @@ public class InsertTest {
 
     assertEquals(0, stage2Inserts.size());
   }
+
+  /** Deeply nested properties without any values, with overridden name and description */
+  @Test
+  public void testTransformation5() {
+    final Entity r = new InsertEntity("Test", Role.RecordType);
+    final Property p1 = new Property(new RetrieveEntity(new EntityID(1)));
+    p1.setRole("Property");
+    p1.setDatatype("TEXT");
+    p1.setDescription("desc1");
+    p1.setDescOverride(true);
+    p1.setName("P1");
+    p1.setNameOverride(true);
+    p1.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p1);
+
+    final Property p2 = new Property(new RetrieveEntity(new EntityID(2)));
+    p2.setRole("Property");
+    p2.setDatatype("TEXT");
+    p2.setDescription("desc2");
+    p2.setDescOverride(true);
+    p2.setName("P2");
+    p2.setNameOverride(true);
+    p2.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p2);
+
+    final Property p21 = new Property(new RetrieveEntity(new EntityID(1)));
+    p21.setRole("Property");
+    p21.setDatatype("TEXT");
+    p21.setDescription("desc21");
+    p21.setDescOverride(true);
+    p21.setName("P21");
+    p21.setNameOverride(true);
+    p21.setStatementStatus(StatementStatus.FIX);
+    p2.addProperty(p21);
+
+    final Property p22 = new Property(new RetrieveEntity(new EntityID(2)));
+    p22.setRole("Property");
+    p22.setDatatype("TEXT");
+    p22.setDescription("desc22");
+    p22.setDescOverride(true);
+    p22.setName("P22");
+    p22.setNameOverride(true);
+    p22.setStatementStatus(StatementStatus.FIX);
+    p2.addProperty(p22);
+
+    final Property p3 = new Property(new RetrieveEntity(new EntityID(3)));
+    p3.setRole("Property");
+    p3.setDatatype("TEXT");
+    p3.setDescription("desc3");
+    p3.setDescOverride(true);
+    p3.setName("P3");
+    p3.setNameOverride(true);
+    p3.setStatementStatus(StatementStatus.RECOMMENDED);
+    r.addProperty(p3);
+
+    final Property p31 = new Property(new RetrieveEntity(new EntityID(1)));
+    p31.setRole("Property");
+    p31.setDatatype("TEXT");
+    p31.setDescription("desc31");
+    p31.setDescOverride(true);
+    p31.setName("P31");
+    p31.setNameOverride(true);
+    p31.setStatementStatus(StatementStatus.FIX);
+    p3.addProperty(p31);
+
+    final Property p32 = new Property(new RetrieveEntity(new EntityID(2)));
+    p32.setRole("Property");
+    p32.setDatatype("TEXT");
+    p32.setDescription("desc32");
+    p32.setDescOverride(true);
+    p32.setName("P32");
+    p32.setNameOverride(true);
+    p32.setStatementStatus(StatementStatus.FIX);
+    p3.addProperty(p32);
+
+    final Property p321 = new Property(new RetrieveEntity(new EntityID(1)));
+    p321.setRole("Property");
+    p321.setDatatype("TEXT");
+    p321.setDescription("desc321");
+    p321.setDescOverride(true);
+    p321.setName("P321");
+    p321.setNameOverride(true);
+    p321.setStatementStatus(StatementStatus.FIX);
+    p32.addProperty(p321);
+
+    final Property p322 = new Property(new RetrieveEntity(new EntityID(2)));
+    p322.setRole("Property");
+    p322.setDatatype("TEXT");
+    p322.setDescription("desc322");
+    p322.setDescOverride(true);
+    p322.setName("P322");
+    p322.setNameOverride(true);
+    p322.setStatementStatus(StatementStatus.FIX);
+    p32.addProperty(p322);
+
+    final Property p323 = new Property(new RetrieveEntity(new EntityID(3)));
+    p323.setRole("Property");
+    p323.setDatatype("TEXT");
+    p323.setDescription("desc323");
+    p323.setDescOverride(true);
+    p323.setName("P323");
+    p323.setNameOverride(true);
+    p323.setStatementStatus(StatementStatus.FIX);
+    p32.addProperty(p323);
+
+    final Property p33 = new Property(new RetrieveEntity(new EntityID(3)));
+    p33.setRole("Property");
+    p33.setDatatype("TEXT");
+    p33.setDescription("desc33");
+    p33.setDescOverride(true);
+    p33.setName("P33");
+    p33.setNameOverride(true);
+    p33.setStatementStatus(StatementStatus.FIX);
+    p3.addProperty(p33);
+
+    List<Property> stage1Inserts = new LinkedList<>();
+    List<Property> stage2Inserts = new LinkedList<>();
+    int c = DatabaseUtils.deriveStage1Inserts(stage1Inserts, r);
+    DatabaseUtils.deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(c), r);
+
+    assertEquals(4, c);
+    assertEquals(7, stage1Inserts.size());
+    assertEquals(8, stage2Inserts.size());
+  }
 }
diff --git a/src/test/java/org/caosdb/server/database/InsertTest2.java b/src/test/java/org/caosdb/server/database/InsertTest2.java
deleted file mode 100644
index 58cc5850..00000000
--- a/src/test/java/org/caosdb/server/database/InsertTest2.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package org.caosdb.server.database;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.List;
-import org.caosdb.server.entity.Entity;
-import org.caosdb.server.entity.EntityID;
-import org.caosdb.server.entity.InsertEntity;
-import org.caosdb.server.entity.RetrieveEntity;
-import org.caosdb.server.entity.Role;
-import org.caosdb.server.entity.StatementStatus;
-import org.caosdb.server.entity.wrapper.Property;
-import org.junit.jupiter.api.Test;
-
-public class InsertTest2 {
-
-  private Deque<EntityID> registerReplacementIds(int count) {
-    Deque<EntityID> replacementIds = new ArrayDeque<>();
-    for (int i = 1; i < count + 1; i++) {
-      replacementIds.add(new EntityID(-i));
-    }
-    return replacementIds;
-  }
-
-  @Test
-  public void transformation5() {
-    final Entity r = new InsertEntity("Test", Role.RecordType);
-    final Property p1 = new Property(new RetrieveEntity(new EntityID(1)));
-    p1.setRole("Property");
-    p1.setDatatype("TEXT");
-    p1.setDescription("desc1");
-    p1.setDescOverride(true);
-    p1.setName("P1");
-    p1.setNameOverride(true);
-    p1.setStatementStatus(StatementStatus.RECOMMENDED);
-    r.addProperty(p1);
-
-    final Property p2 = new Property(new RetrieveEntity(new EntityID(2)));
-    p2.setRole("Property");
-    p2.setDatatype("TEXT");
-    p2.setDescription("desc2");
-    p2.setDescOverride(true);
-    p2.setName("P2");
-    p2.setNameOverride(true);
-    p2.setStatementStatus(StatementStatus.RECOMMENDED);
-    r.addProperty(p2);
-
-    final Property p21 = new Property(new RetrieveEntity(new EntityID(1)));
-    p21.setRole("Property");
-    p21.setDatatype("TEXT");
-    p21.setDescription("desc21");
-    p21.setDescOverride(true);
-    p21.setName("P21");
-    p21.setNameOverride(true);
-    p21.setStatementStatus(StatementStatus.RECOMMENDED);
-    p2.addProperty(p21);
-
-    final Property p22 = new Property(new RetrieveEntity(new EntityID(2)));
-    p22.setRole("Property");
-    p22.setDatatype("TEXT");
-    p22.setDescription("desc22");
-    p22.setDescOverride(true);
-    p22.setName("P22");
-    p22.setNameOverride(true);
-    p22.setStatementStatus(StatementStatus.RECOMMENDED);
-    p2.addProperty(p22);
-
-    final Property p3 = new Property(new RetrieveEntity(new EntityID(3)));
-    p3.setRole("Property");
-    p3.setDatatype("TEXT");
-    p3.setDescription("desc3");
-    p3.setDescOverride(true);
-    p3.setName("P3");
-    p3.setNameOverride(true);
-    p3.setStatementStatus(StatementStatus.RECOMMENDED);
-    r.addProperty(p3);
-
-    final Property p31 = new Property(new RetrieveEntity(new EntityID(1)));
-    p31.setRole("Property");
-    p31.setDatatype("TEXT");
-    p31.setDescription("desc31");
-    p31.setDescOverride(true);
-    p31.setName("P31");
-    p31.setNameOverride(true);
-    p31.setStatementStatus(StatementStatus.RECOMMENDED);
-    p3.addProperty(p31);
-
-    final Property p32 = new Property(new RetrieveEntity(new EntityID(2)));
-    p32.setRole("Property");
-    p32.setDatatype("TEXT");
-    p32.setDescription("desc32");
-    p32.setDescOverride(true);
-    p32.setName("P32");
-    p32.setNameOverride(true);
-    p32.setStatementStatus(StatementStatus.RECOMMENDED);
-    p3.addProperty(p32);
-
-    final Property p321 = new Property(new RetrieveEntity(new EntityID(1)));
-    p321.setRole("Property");
-    p321.setDatatype("TEXT");
-    p321.setDescription("desc321");
-    p321.setDescOverride(true);
-    p321.setName("P321");
-    p321.setNameOverride(true);
-    p321.setStatementStatus(StatementStatus.RECOMMENDED);
-    p32.addProperty(p321);
-
-    final Property p322 = new Property(new RetrieveEntity(new EntityID(2)));
-    p322.setRole("Property");
-    p322.setDatatype("TEXT");
-    p322.setDescription("desc322");
-    p322.setDescOverride(true);
-    p322.setName("P322");
-    p322.setNameOverride(true);
-    p322.setStatementStatus(StatementStatus.RECOMMENDED);
-    p32.addProperty(p322);
-
-    final Property p323 = new Property(new RetrieveEntity(new EntityID(3)));
-    p323.setRole("Property");
-    p323.setDatatype("TEXT");
-    p323.setDescription("desc323");
-    p323.setDescOverride(true);
-    p323.setName("P323");
-    p323.setNameOverride(true);
-    p323.setStatementStatus(StatementStatus.RECOMMENDED);
-    p32.addProperty(p323);
-
-    final Property p33 = new Property(new RetrieveEntity(new EntityID(3)));
-    p33.setRole("Property");
-    p33.setDatatype("TEXT");
-    p33.setDescription("desc33");
-    p33.setDescOverride(true);
-    p33.setName("P33");
-    p33.setNameOverride(true);
-    p33.setStatementStatus(StatementStatus.RECOMMENDED);
-    p3.addProperty(p33);
-
-    List<Property> stage1Inserts = new LinkedList<>();
-    List<Property> stage2Inserts = new LinkedList<>();
-    int c = DatabaseUtils.deriveStage1Inserts(stage1Inserts, r);
-    DatabaseUtils.deriveStage2Inserts(stage2Inserts, stage1Inserts, registerReplacementIds(c), r);
-    assertEquals(7, stage1Inserts.size());
-    assertEquals(8, stage2Inserts.size());
-  }
-}
-- 
GitLab


From ce345f40b927ac1bcd1d159ec4383d768a678bbe Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 00:59:18 +0200
Subject: [PATCH 5/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../java/org/caosdb/server/CaosDBServer.java  |  1 -
 .../server/accessControl/ACMPermissions.java  |  3 -
 .../caosdb/server/database/Replacement.java   | 41 --------------
 .../implementation/MySQL}/DatabaseUtils.java  | 55 +++++--------------
 .../MySQL/MySQLGetAllNames.java               |  1 -
 .../MySQL/MySQLInsertEntityProperties.java    |  1 -
 .../MySQL/MySQLInsertSparseEntity.java        |  1 -
 .../MySQL/MySQLRetrieveAll.java               |  1 -
 .../MySQL/MySQLRetrieveDatatypes.java         |  5 +-
 .../MySQL/MySQLRetrieveParents.java           |  5 +-
 .../MySQL/MySQLRetrievePasswordValidator.java |  2 +-
 .../MySQL/MySQLRetrieveProperties.java        | 20 ++-----
 .../MySQL/MySQLRetrieveSparseEntity.java      |  1 -
 .../MySQL/MySQLRetrieveUser.java              |  2 +-
 .../MySQL/MySQLRetrieveVersionHistory.java    |  1 -
 .../MySQL/MySQLUpdateSparseEntity.java        |  1 -
 .../implementation/MySQL/Replacement.java     | 53 ++++++++++++++++++
 .../interfaces/RetrieveDatatypesImpl.java     |  4 +-
 .../interfaces/RetrieveParentsImpl.java       |  4 +-
 .../transaction/RetrieveDatatypes.java        |  4 +-
 .../backend/transaction/RetrieveParents.java  | 27 ++++++---
 .../transaction/RetrieveProperties.java       |  2 +-
 .../transaction/RetrieveSparseEntity.java     |  7 ++-
 .../server/database/proto/FlatProperty.java   | 13 -----
 .../server/database/proto/ProtoProperty.java  |  6 --
 .../caosdb/server/query/Backreference.java    |  2 +-
 .../org/caosdb/server/query/Conjunction.java  |  2 +-
 .../org/caosdb/server/query/Disjunction.java  |  2 +-
 .../org/caosdb/server/query/Negation.java     |  2 +-
 .../java/org/caosdb/server/query/POV.java     |  2 +-
 .../java/org/caosdb/server/query/Query.java   |  2 +-
 .../org/caosdb/server/query/SubProperty.java  |  2 +-
 .../server/utils/ResultSetIterator.java       |  2 +-
 .../implementation/MySQL}/InsertTest.java     |  6 +-
 34 files changed, 119 insertions(+), 164 deletions(-)
 delete mode 100644 src/main/java/org/caosdb/server/database/Replacement.java
 rename src/main/java/org/caosdb/server/database/{ => backend/implementation/MySQL}/DatabaseUtils.java (87%)
 create mode 100644 src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
 rename src/test/java/org/caosdb/server/database/{ => backend/implementation/MySQL}/InsertTest.java (98%)

diff --git a/src/main/java/org/caosdb/server/CaosDBServer.java b/src/main/java/org/caosdb/server/CaosDBServer.java
index 05fb9485..deca9aae 100644
--- a/src/main/java/org/caosdb/server/CaosDBServer.java
+++ b/src/main/java/org/caosdb/server/CaosDBServer.java
@@ -223,7 +223,6 @@ public class CaosDBServer extends Application {
             public boolean notifyObserver(String e, Observable sender) {
               if (e.equals(ServerProperties.KEY_TIMEZONE)) {
 
-                String[] availableIDs = TimeZone.getAvailableIDs();
                 TimeZone newZoneId =
                     TimeZone.getTimeZone(getServerProperty(ServerProperties.KEY_TIMEZONE));
                 TimeZone.setDefault(newZoneId);
diff --git a/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java b/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
index 854fc07f..07503692 100644
--- a/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
+++ b/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
@@ -26,12 +26,9 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class ACMPermissions implements Comparable<ACMPermissions> {
 
-  private static Logger LOGGER = LoggerFactory.getLogger(ACMPermissions.class);
   public static final String USER_PARAMETER = "?USER?";
   public static final String REALM_PARAMETER = "?REALM?";
   public static final String ROLE_PARAMETER = "?ROLE?";
diff --git a/src/main/java/org/caosdb/server/database/Replacement.java b/src/main/java/org/caosdb/server/database/Replacement.java
deleted file mode 100644
index 8606d51b..00000000
--- a/src/main/java/org/caosdb/server/database/Replacement.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.caosdb.server.database;
-
-import org.caosdb.server.datatype.ReferenceValue;
-import org.caosdb.server.entity.EntityID;
-import org.caosdb.server.entity.RetrieveEntity;
-import org.caosdb.server.entity.StatementStatus;
-import org.caosdb.server.entity.wrapper.Property;
-
-public class Replacement extends Property {
-
-  public Property replacement;
-  public EntityID replacementId;
-
-  @Override
-  public EntityID getId() {
-    return replacementId;
-  }
-
-  public void setReplacementId(EntityID id) {
-    replacementId.link(id);
-  }
-
-  public Replacement(Property p) {
-    super(p);
-    replacementId = new EntityID();
-    replacement = new Property(new RetrieveEntity());
-
-    replacement.setDomain(p.getDomainEntity());
-    //    replacement.setDatatypeOverride(p.isDatatypeOverride());
-    //    replacement.setDatatype(p.getDatatype());
-    //    replacement.setNameOverride(p.isNameOverride());
-    //    replacement.setName(p.getName());
-    //    replacement.setDescOverride(p.isDescOverride());
-    //    replacement.setDescription(p.getDescription());
-
-    replacement.setId(replacementId);
-    replacement.setStatementStatus(StatementStatus.REPLACEMENT);
-    replacement.setValue(new ReferenceValue(p.getId()));
-    replacement.setPIdx(0);
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
similarity index 87%
rename from src/main/java/org/caosdb/server/database/DatabaseUtils.java
rename to src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
index 6d72d965..0347028b 100644
--- a/src/main/java/org/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,10 +18,8 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
-package org.caosdb.server.database;
+package org.caosdb.server.database.backend.implementation.MySQL;
 
 import com.google.common.base.Objects;
 import java.sql.ResultSet;
@@ -46,7 +45,6 @@ import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.StatementStatus;
-import org.caosdb.server.entity.wrapper.Parent;
 import org.caosdb.server.entity.wrapper.Property;
 
 public class DatabaseUtils {
@@ -154,13 +152,7 @@ public class DatabaseUtils {
     }
   }
 
-  public static void parseFromSparseEntities(final EntityInterface e, final SparseEntity spe) {
-    if (spe != null) {
-      e.parseSparseEntity(spe);
-    }
-  }
-
-  public static void parseOverrides(final List<FlatProperty> properties, final ResultSet rs)
+  public static void parseOverrides(final List<ProtoProperty> properties, final ResultSet rs)
       throws SQLException {
     while (rs.next()) {
       final int property_id = rs.getInt("property_id");
@@ -187,15 +179,15 @@ public class DatabaseUtils {
     }
   }
 
-  public static List<FlatProperty> parsePropertyResultset(final ResultSet rs) throws SQLException {
-    final List<FlatProperty> ret = new LinkedList<>();
+  public static List<ProtoProperty> parsePropertyResultset(final ResultSet rs) throws SQLException {
+    final List<ProtoProperty> ret = new LinkedList<>();
     while (rs.next()) {
-      FlatProperty fp = new FlatProperty();
-      fp.id = rs.getInt("PropertyID");
-      fp.value = bytes2UTF8(rs.getBytes("PropertyValue"));
-      fp.status = bytes2UTF8(rs.getBytes("PropertyStatus"));
-      fp.idx = rs.getInt("PropertyIndex");
-      ret.add(fp);
+      ProtoProperty pp = new ProtoProperty();
+      pp.id = rs.getInt("PropertyID");
+      pp.value = bytes2UTF8(rs.getBytes("PropertyValue"));
+      pp.status = bytes2UTF8(rs.getBytes("PropertyStatus"));
+      pp.idx = rs.getInt("PropertyIndex");
+      ret.add(pp);
     }
     return ret;
   }
@@ -239,9 +231,9 @@ public class DatabaseUtils {
     return ret;
   }
 
-  public static ArrayList<VerySparseEntity> parseParentResultSet(final ResultSet rs)
+  public static LinkedList<VerySparseEntity> parseParentResultSet(final ResultSet rs)
       throws SQLException {
-    final ArrayList<VerySparseEntity> ret = new ArrayList<VerySparseEntity>();
+    final LinkedList<VerySparseEntity> ret = new LinkedList<>();
     while (rs.next()) {
       final VerySparseEntity vsp = new VerySparseEntity();
       vsp.id = rs.getInt("ParentID");
@@ -254,25 +246,6 @@ public class DatabaseUtils {
     return ret;
   }
 
-  public static <K extends EntityInterface> K parseEntityFromVerySparseEntity(
-      final K entity, final VerySparseEntity vse) {
-    entity.setId(new EntityID(vse.id));
-    entity.setName(vse.name);
-    entity.setRole(vse.role);
-    entity.setDescription(vse.description);
-    return entity;
-  }
-
-  public static void parseParentsFromVerySparseEntity(
-      final EntityInterface entity, final List<VerySparseEntity> pars) {
-    for (final VerySparseEntity vsp : pars) {
-      final Parent p = new Parent(new RetrieveEntity(new EntityID(vsp.id)));
-      p.setName(vsp.name);
-      p.setDescription(vsp.description);
-      entity.addParent(p);
-    }
-  }
-
   public static ArrayList<Property> parseFromProtoProperties(
       EntityInterface entity, List<ProtoProperty> protos) {
     final ArrayList<Property> ret = new ArrayList<Property>();
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetAllNames.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetAllNames.java
index 09cae688..474048f1 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetAllNames.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetAllNames.java
@@ -5,7 +5,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.GetAllNamesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
index 5c9ca6f9..f3977068 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
@@ -33,7 +33,6 @@ import java.util.Deque;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.InsertEntityPropertiesImpl;
 import org.caosdb.server.database.exceptions.IntegrityException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
index 1b945acf..27227926 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
@@ -26,7 +26,6 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLIntegrityConstraintViolationException;
 import java.sql.Types;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.InsertSparseEntityImpl;
 import org.caosdb.server.database.exceptions.IntegrityException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
index f847bf63..9a2ea816 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
@@ -27,7 +27,6 @@ import java.sql.SQLException;
 import java.util.LinkedList;
 import java.util.List;
 import org.apache.shiro.SecurityUtils;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveDatatypes.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveDatatypes.java
index e085f051..68524941 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveDatatypes.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveDatatypes.java
@@ -25,8 +25,7 @@ package org.caosdb.server.database.backend.implementation.MySQL;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.ArrayList;
-import org.caosdb.server.database.DatabaseUtils;
+import java.util.List;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveDatatypesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
@@ -53,7 +52,7 @@ public class MySQLRetrieveDatatypes extends MySQLTransaction implements Retrieve
           + "FROM entities AS e WHERE e.role='DATATYPE'";
 
   @Override
-  public ArrayList<VerySparseEntity> execute() throws TransactionException {
+  public List<VerySparseEntity> execute() throws TransactionException {
     try {
       final PreparedStatement stmt = prepareStatement(STMT_GET_DATATYPE);
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
index 64ce7e90..1e2a6621 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
@@ -26,8 +26,7 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
-import java.util.ArrayList;
-import org.caosdb.server.database.DatabaseUtils;
+import java.util.LinkedList;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveParentsImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
@@ -43,7 +42,7 @@ public class MySQLRetrieveParents extends MySQLTransaction implements RetrievePa
   private static final String stmtStr = "call retrieveEntityParents(?, ?)";
 
   @Override
-  public ArrayList<VerySparseEntity> execute(final EntityID id, final String version)
+  public LinkedList<VerySparseEntity> execute(final EntityID id, final String version)
       throws TransactionException {
     try {
       ResultSet rs = null;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrievePasswordValidator.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrievePasswordValidator.java
index 1ff7dd7a..9fd7e0a8 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrievePasswordValidator.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrievePasswordValidator.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.database.backend.implementation.MySQL;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.security.NoSuchAlgorithmException;
 import java.sql.PreparedStatement;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
index c8843f9c..100df4ce 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
@@ -28,11 +28,9 @@ import java.sql.SQLException;
 import java.sql.Types;
 import java.util.LinkedList;
 import java.util.List;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrievePropertiesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.FlatProperty;
 import org.caosdb.server.database.proto.ProtoProperty;
 import org.caosdb.server.entity.EntityID;
 
@@ -92,32 +90,22 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
       final long t2 = System.currentTimeMillis();
       addMeasurement(this.getClass().getSimpleName() + ".retrieveFlatPropertiesStage1", t2 - t1);
 
-      final List<FlatProperty> props = DatabaseUtils.parsePropertyResultset(rs);
+      final List<ProtoProperty> properties = DatabaseUtils.parsePropertyResultset(rs);
 
       final PreparedStatement retrieveOverrides = prepareStatement(stmtStr2);
 
-      retrieveOverrides(domain, entity, version, retrieveOverrides, props);
+      retrieveOverrides(domain, entity, version, retrieveOverrides, properties);
 
-      return handleCollectionValues(props);
+      return properties;
     }
   }
 
-  @Deprecated
-  public List<ProtoProperty> handleCollectionValues(List<FlatProperty> props) throws SQLException {
-
-    List<ProtoProperty> result = new LinkedList<>();
-    for (FlatProperty fp : props) {
-      result.add(new ProtoProperty(fp));
-    }
-    return result;
-  }
-
   private void retrieveOverrides(
       final Integer domain,
       final Integer entity,
       final String version,
       final PreparedStatement retrieveOverrides,
-      final List<FlatProperty> props)
+      final List<ProtoProperty> props)
       throws SQLException {
 
     ResultSet rs = null;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
index ca3bd078..48cd84b3 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
@@ -26,7 +26,6 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java
index bde1878d..40f036f1 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.database.backend.implementation.MySQL;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
index 585be49a..6c4c2629 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
@@ -27,7 +27,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.LinkedList;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
index 1c803925..b2d0fb9e 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
@@ -27,7 +27,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLIntegrityConstraintViolationException;
 import java.sql.Types;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.UpdateSparseEntityImpl;
 import org.caosdb.server.database.exceptions.IntegrityException;
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
new file mode 100644
index 00000000..66bf99c4
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package org.caosdb.server.database.backend.implementation.MySQL;
+
+import org.caosdb.server.datatype.ReferenceValue;
+import org.caosdb.server.entity.EntityID;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.wrapper.Property;
+
+public class Replacement extends Property {
+
+  public Property replacement;
+  public EntityID replacementId;
+
+  @Override
+  public EntityID getId() {
+    return replacementId;
+  }
+
+  public void setReplacementId(EntityID id) {
+    replacementId.link(id);
+  }
+
+  public Replacement(Property p) {
+    super(p);
+    replacementId = new EntityID();
+    replacement = new Property(new RetrieveEntity());
+
+    replacement.setDomain(p.getDomainEntity());
+    replacement.setId(replacementId);
+    replacement.setStatementStatus(StatementStatus.REPLACEMENT);
+    replacement.setValue(new ReferenceValue(p.getId()));
+    replacement.setPIdx(0);
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveDatatypesImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveDatatypesImpl.java
index 91c68dc5..0886ff43 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveDatatypesImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveDatatypesImpl.java
@@ -22,11 +22,11 @@
  */
 package org.caosdb.server.database.backend.interfaces;
 
-import java.util.ArrayList;
+import java.util.List;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.VerySparseEntity;
 
 public interface RetrieveDatatypesImpl extends BackendTransactionImpl {
 
-  public abstract ArrayList<VerySparseEntity> execute() throws TransactionException;
+  public abstract List<VerySparseEntity> execute() throws TransactionException;
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
index 9bfddba1..9f7eba82 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
@@ -22,13 +22,13 @@
  */
 package org.caosdb.server.database.backend.interfaces;
 
-import java.util.ArrayList;
+import java.util.LinkedList;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.VerySparseEntity;
 import org.caosdb.server.entity.EntityID;
 
 public interface RetrieveParentsImpl extends BackendTransactionImpl {
 
-  public ArrayList<VerySparseEntity> execute(EntityID id, String version)
+  public LinkedList<VerySparseEntity> execute(EntityID id, String version)
       throws TransactionException;
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveDatatypes.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveDatatypes.java
index b4a726da..1ca9f60e 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveDatatypes.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveDatatypes.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.ArrayList;
+import java.util.List;
 import org.caosdb.server.database.BackendTransaction;
 import org.caosdb.server.database.backend.interfaces.RetrieveDatatypesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
@@ -35,7 +35,7 @@ import org.caosdb.server.entity.container.Container;
 
 public class RetrieveDatatypes extends BackendTransaction {
 
-  private ArrayList<VerySparseEntity> list;
+  private List<VerySparseEntity> list;
 
   @Override
   public void execute() throws TransactionException {
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveParents.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveParents.java
index b551430a..262ee271 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveParents.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveParents.java
@@ -24,15 +24,18 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.caosdb.server.caching.Cache;
 import org.caosdb.server.database.CacheableBackendTransaction;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.backend.interfaces.RetrieveParentsImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.VerySparseEntity;
+import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.wrapper.Parent;
 
 // TODO Problem with the caching.
 // When an old entity version has a parent which is deleted, the name is
@@ -47,9 +50,9 @@ import org.caosdb.server.entity.EntityInterface;
 // See also a failing test in caosdb-pyinttest:
 // tests/test_version.py::test_bug_cached_parent_name_in_old_version
 public class RetrieveParents
-    extends CacheableBackendTransaction<String, ArrayList<VerySparseEntity>> {
+    extends CacheableBackendTransaction<String, LinkedList<VerySparseEntity>> {
 
-  private static final ICacheAccess<String, ArrayList<VerySparseEntity>> cache =
+  private static final ICacheAccess<String, LinkedList<VerySparseEntity>> cache =
       Cache.getCache("BACKEND_EntityParents");
 
   /**
@@ -71,16 +74,26 @@ public class RetrieveParents
   }
 
   @Override
-  public ArrayList<VerySparseEntity> executeNoCache() throws TransactionException {
+  public LinkedList<VerySparseEntity> executeNoCache() throws TransactionException {
     final RetrieveParentsImpl t = getImplementation(RetrieveParentsImpl.class);
     return t.execute(this.entity.getId(), this.entity.getVersion().getId());
   }
 
   @Override
-  protected void process(final ArrayList<VerySparseEntity> t) throws TransactionException {
+  protected void process(final LinkedList<VerySparseEntity> t) throws TransactionException {
     this.entity.getParents().clear();
 
-    DatabaseUtils.parseParentsFromVerySparseEntity(this.entity, t);
+    parseParentsFromVerySparseEntity(this.entity, t);
+  }
+
+  private void parseParentsFromVerySparseEntity(
+      final EntityInterface entity, final List<VerySparseEntity> pars) {
+    for (final VerySparseEntity vsp : pars) {
+      final Parent p = new Parent(new RetrieveEntity(new EntityID(vsp.id)));
+      p.setName(vsp.name);
+      p.setDescription(vsp.description);
+      entity.addParent(p);
+    }
   }
 
   @Override
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
index caa4b6cf..f9c11303 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveProperties.java
@@ -29,7 +29,7 @@ import java.util.List;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.caosdb.server.caching.Cache;
 import org.caosdb.server.database.CacheableBackendTransaction;
-import org.caosdb.server.database.DatabaseUtils;
+import org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils;
 import org.caosdb.server.database.backend.interfaces.RetrievePropertiesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.ProtoProperty;
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
index aabd490b..f1ba8354 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
@@ -27,7 +27,6 @@ package org.caosdb.server.database.backend.transaction;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.caosdb.server.caching.Cache;
 import org.caosdb.server.database.CacheableBackendTransaction;
-import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.SparseEntity;
@@ -81,8 +80,10 @@ public class RetrieveSparseEntity extends CacheableBackendTransaction<String, Sp
 
   @Override
   protected void process(final SparseEntity t) throws TransactionException {
-    DatabaseUtils.parseFromSparseEntities(this.entity, t);
-    this.entity.setEntityStatus(EntityStatus.VALID);
+    if (t != null) {
+      this.entity.parseSparseEntity(t);
+      this.entity.setEntityStatus(EntityStatus.VALID);
+    }
   }
 
   @Override
diff --git a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
index b4f0f62a..b92d3def 100644
--- a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
+++ b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java
@@ -26,19 +26,6 @@ import java.io.Serializable;
 
 public class FlatProperty implements Serializable {
 
-  public FlatProperty() {}
-
-  public FlatProperty(FlatProperty fp) {
-    id = fp.id;
-    value = fp.value;
-    status = fp.status;
-    idx = fp.idx;
-    name = fp.name;
-    desc = fp.desc;
-    type = fp.type;
-    collection = fp.collection;
-  }
-
   private static final long serialVersionUID = 6039288034435124195L;
   public Integer id = null;
   public String value = null;
diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
index c6cdbd54..7a11cd4d 100644
--- a/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
+++ b/src/main/java/org/caosdb/server/database/proto/ProtoProperty.java
@@ -28,12 +28,6 @@ import java.util.List;
 public class ProtoProperty extends FlatProperty implements Serializable {
   private static final long serialVersionUID = 7731301985162924975L;
 
-  public ProtoProperty(FlatProperty fp) {
-    super(fp);
-  }
-
-  public ProtoProperty() {}
-
   public List<ProtoProperty> subProperties = null;
   public List<Object> collValues = null;
 }
diff --git a/src/main/java/org/caosdb/server/query/Backreference.java b/src/main/java/org/caosdb/server/query/Backreference.java
index 7d551fd4..b801ffaf 100644
--- a/src/main/java/org/caosdb/server/query/Backreference.java
+++ b/src/main/java/org/caosdb/server/query/Backreference.java
@@ -23,7 +23,7 @@
 package org.caosdb.server.query;
 
 import static java.sql.Types.VARCHAR;
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
diff --git a/src/main/java/org/caosdb/server/query/Conjunction.java b/src/main/java/org/caosdb/server/query/Conjunction.java
index 03b33124..59dfd57b 100644
--- a/src/main/java/org/caosdb/server/query/Conjunction.java
+++ b/src/main/java/org/caosdb/server/query/Conjunction.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.query;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
diff --git a/src/main/java/org/caosdb/server/query/Disjunction.java b/src/main/java/org/caosdb/server/query/Disjunction.java
index edd2e6da..925ef859 100644
--- a/src/main/java/org/caosdb/server/query/Disjunction.java
+++ b/src/main/java/org/caosdb/server/query/Disjunction.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.query;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
diff --git a/src/main/java/org/caosdb/server/query/Negation.java b/src/main/java/org/caosdb/server/query/Negation.java
index cfdfd05e..431124df 100644
--- a/src/main/java/org/caosdb/server/query/Negation.java
+++ b/src/main/java/org/caosdb/server/query/Negation.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.query;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index 0de160cd..ebc35a16 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -25,7 +25,7 @@ package org.caosdb.server.query;
 import static java.sql.Types.DOUBLE;
 import static java.sql.Types.INTEGER;
 import static java.sql.Types.VARCHAR;
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import de.timmfitschen.easyunits.parser.ParserException;
 import java.sql.CallableStatement;
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index c4a45863..abaca436 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -21,7 +21,7 @@
  */
 package org.caosdb.server.query;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.io.Serializable;
 import java.sql.CallableStatement;
diff --git a/src/main/java/org/caosdb/server/query/SubProperty.java b/src/main/java/org/caosdb/server/query/SubProperty.java
index 8ff167ea..d5ce9234 100644
--- a/src/main/java/org/caosdb/server/query/SubProperty.java
+++ b/src/main/java/org/caosdb/server/query/SubProperty.java
@@ -23,7 +23,7 @@
 package org.caosdb.server.query;
 
 import static java.sql.Types.VARCHAR;
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
diff --git a/src/main/java/org/caosdb/server/utils/ResultSetIterator.java b/src/main/java/org/caosdb/server/utils/ResultSetIterator.java
index 0912d278..71429f1a 100644
--- a/src/main/java/org/caosdb/server/utils/ResultSetIterator.java
+++ b/src/main/java/org/caosdb/server/utils/ResultSetIterator.java
@@ -1,6 +1,6 @@
 package org.caosdb.server.utils;
 
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
diff --git a/src/test/java/org/caosdb/server/database/InsertTest.java b/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java
similarity index 98%
rename from src/test/java/org/caosdb/server/database/InsertTest.java
rename to src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java
index 0484e623..03bd185c 100644
--- a/src/test/java/org/caosdb/server/database/InsertTest.java
+++ b/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java
@@ -20,10 +20,10 @@
  *
  * ** end header
  */
-package org.caosdb.server.database;
+package org.caosdb.server.database.backend.implementation.MySQL;
 
-import static org.caosdb.server.database.DatabaseUtils.deriveStage1Inserts;
-import static org.caosdb.server.database.DatabaseUtils.deriveStage2Inserts;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.deriveStage1Inserts;
+import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.deriveStage2Inserts;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-- 
GitLab


From ac56b7746d683050512ef58288e526f92c056397 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 01:10:28 +0200
Subject: [PATCH 6/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../backend/implementation/MySQL/DatabaseUtils.java       | 3 ++-
 .../backend/implementation/MySQL/Replacement.java         | 8 +++-----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
index 0347028b..20d43389 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
@@ -33,6 +33,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import org.caosdb.server.database.proto.FlatProperty;
 import org.caosdb.server.database.proto.ProtoProperty;
 import org.caosdb.server.database.proto.SparseEntity;
@@ -334,7 +335,7 @@ public class DatabaseUtils {
             pp.collValues
                 .stream()
                 .map((x) -> (Object) ((Entry<Integer, String>) x).getValue())
-                .toList();
+                .collect(Collectors.toList());
       }
       result.add(pp);
     }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
index 66bf99c4..fa2fb7cb 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
@@ -28,24 +28,22 @@ import org.caosdb.server.entity.wrapper.Property;
 public class Replacement extends Property {
 
   public Property replacement;
-  public EntityID replacementId;
 
   @Override
   public EntityID getId() {
-    return replacementId;
+    return replacement.getId();
   }
 
   public void setReplacementId(EntityID id) {
-    replacementId.link(id);
+    replacement.getId().link(id);
   }
 
   public Replacement(Property p) {
     super(p);
-    replacementId = new EntityID();
     replacement = new Property(new RetrieveEntity());
 
     replacement.setDomain(p.getDomainEntity());
-    replacement.setId(replacementId);
+    replacement.setId(new EntityID());
     replacement.setStatementStatus(StatementStatus.REPLACEMENT);
     replacement.setValue(new ReferenceValue(p.getId()));
     replacement.setPIdx(0);
-- 
GitLab


From cc12cb76926f2e01eb1c64aa51aa22aedd701d7e Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 11:11:32 +0200
Subject: [PATCH 7/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../implementation/MySQL/DatabaseUtils.java   |  9 ++++--
 .../MySQL/MySQLInsertEntityProperties.java    | 14 +++++----
 .../implementation/MySQL/Replacement.java     | 16 ++++++++--
 .../java/org/caosdb/server/entity/Entity.java |  6 ++--
 .../caosdb/server/entity/EntityInterface.java |  4 +--
 .../caosdb/server/entity/StatementStatus.java |  8 +----
 .../entity/StatementStatusInterface.java      | 29 +++++++++++++++++++
 .../server/entity/wrapper/EntityWrapper.java  |  8 ++---
 .../server/grpc/CaosDBToGrpcConverters.java   |  5 ++--
 9 files changed, 72 insertions(+), 27 deletions(-)
 create mode 100644 src/main/java/org/caosdb/server/entity/StatementStatusInterface.java

diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
index 20d43389..c123b467 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
@@ -297,7 +297,7 @@ public class DatabaseUtils {
     LinkedList<ProtoProperty> result = new LinkedList<>();
     Map<String, ProtoProperty> replacements = new HashMap<>();
     for (ProtoProperty pp : properties) {
-      if (pp.status.equals(StatementStatus.REPLACEMENT.toString())) {
+      if (pp.status.equals(ReplacementStatus.REPLACEMENT.name())) {
         replacements.put(pp.value, null);
       }
     }
@@ -322,7 +322,7 @@ public class DatabaseUtils {
       }
     }
     for (ProtoProperty pp : properties) {
-      if (pp.status.equals(StatementStatus.REPLACEMENT.toString())) {
+      if (pp.status.equals(ReplacementStatus.REPLACEMENT.name())) {
         replace(pp, replacements.get(pp.value), isHead);
       }
       if (pp.collValues != null) {
@@ -342,6 +342,11 @@ public class DatabaseUtils {
     return result;
   }
 
+  /*
+   * This replace function is used during the retrieval of properties. It is
+   * basically the opposite of the Replacement class. It copies the information
+   * from the replacement object back into the Property.
+   */
   private static void replace(ProtoProperty pp, ProtoProperty replacement, boolean isHead) {
     if (replacement == null) {
       if (isHead) {
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
index f3977068..b57719f5 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
@@ -46,7 +46,6 @@ import org.caosdb.server.datatype.ReferenceValue;
 import org.caosdb.server.datatype.SingleValue;
 import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.entity.StatementStatus;
 import org.caosdb.server.entity.wrapper.Property;
 
 public class MySQLInsertEntityProperties extends MySQLTransaction
@@ -116,10 +115,10 @@ public class MySQLInsertEntityProperties extends MySQLTransaction
   }
 
   private void insertStages(
-      final List<Property> stage1Inserts, final EntityID domain, final EntityID entity)
+      final List<Property> inserts, final EntityID domain, final EntityID entity)
       throws TransactionException {
 
-    for (final Property property : stage1Inserts) {
+    for (final Property property : inserts) {
 
       // prepare flat property
       final FlatProperty fp = new FlatProperty();
@@ -129,10 +128,15 @@ public class MySQLInsertEntityProperties extends MySQLTransaction
       fp.idx = property.getPIdx();
       fp.status = property.getStatementStatus().name();
 
-      if (property.getStatementStatus() == StatementStatus.REPLACEMENT) {
-        table = Table.reference_data;
+      if (property.getStatementStatus() == ReplacementStatus.REPLACEMENT) {
+        // special treatment: swap value and id. This is part of the back-end specification for the
+        // representation of replacement.
+
+        // value should be the
         fp.value = fp.id.toString();
+
         fp.id = ((ReferenceValue) property.getValue()).getId().toInteger();
+        table = Table.reference_data;
       } else {
 
         if (property.hasUnit()) {
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
index fa2fb7cb..61e431ba 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java
@@ -22,9 +22,21 @@ package org.caosdb.server.database.backend.implementation.MySQL;
 import org.caosdb.server.datatype.ReferenceValue;
 import org.caosdb.server.entity.EntityID;
 import org.caosdb.server.entity.RetrieveEntity;
-import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.StatementStatusInterface;
 import org.caosdb.server.entity.wrapper.Property;
 
+enum ReplacementStatus implements StatementStatusInterface {
+  REPLACEMENT
+}
+/**
+ * A wrapper of {@link Property} objects used by the back-end implementation for the MySQL/MariaDB
+ * back-end.
+ *
+ * <p>This class helps to transform deeply nested properties, properties with overridden data types,
+ * and much more to the flat (row-like) representation used internally by the back-end (which is an
+ * implementation detail of the back-end or part of the non-public API of the back-end from that
+ * point of view).
+ */
 public class Replacement extends Property {
 
   public Property replacement;
@@ -44,7 +56,7 @@ public class Replacement extends Property {
 
     replacement.setDomain(p.getDomainEntity());
     replacement.setId(new EntityID());
-    replacement.setStatementStatus(StatementStatus.REPLACEMENT);
+    replacement.setStatementStatus(ReplacementStatus.REPLACEMENT);
     replacement.setValue(new ReferenceValue(p.getId()));
     replacement.setPIdx(0);
   }
diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java
index 843449ce..614397c5 100644
--- a/src/main/java/org/caosdb/server/entity/Entity.java
+++ b/src/main/java/org/caosdb/server/entity/Entity.java
@@ -421,7 +421,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa
     }
   }
 
-  private StatementStatus statementStatus = null;
+  private StatementStatusInterface statementStatus = null;
 
   /**
    * statementStatus getter.
@@ -429,7 +429,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa
    * @return
    */
   @Override
-  public StatementStatus getStatementStatus() {
+  public StatementStatusInterface getStatementStatus() {
     return this.statementStatus;
   }
 
@@ -439,7 +439,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa
    * @return
    */
   @Override
-  public void setStatementStatus(final StatementStatus statementStatus) {
+  public void setStatementStatus(final StatementStatusInterface statementStatus) {
     this.statementStatus = statementStatus;
   }
 
diff --git a/src/main/java/org/caosdb/server/entity/EntityInterface.java b/src/main/java/org/caosdb/server/entity/EntityInterface.java
index d97970de..ba77b5c4 100644
--- a/src/main/java/org/caosdb/server/entity/EntityInterface.java
+++ b/src/main/java/org/caosdb/server/entity/EntityInterface.java
@@ -57,9 +57,9 @@ public interface EntityInterface
 
   public abstract boolean hasId();
 
-  public abstract StatementStatus getStatementStatus();
+  public abstract StatementStatusInterface getStatementStatus();
 
-  public abstract void setStatementStatus(StatementStatus statementStatus);
+  public abstract void setStatementStatus(StatementStatusInterface statementStatus);
 
   public abstract void setStatementStatus(String statementStatus);
 
diff --git a/src/main/java/org/caosdb/server/entity/StatementStatus.java b/src/main/java/org/caosdb/server/entity/StatementStatus.java
index 0a797175..83d0c57e 100644
--- a/src/main/java/org/caosdb/server/entity/StatementStatus.java
+++ b/src/main/java/org/caosdb/server/entity/StatementStatus.java
@@ -33,17 +33,11 @@ package org.caosdb.server.entity;
  * href="../../../../../specification/RecordType.html">the documentation of RecordTypes</a> for more
  * information.
  *
- * <p>2. Marking an entity as a ``REPLACEMENT`` which is needed for flat representation of deeply
- * nested properties. This constant is only used for internal processes and has no meaning in the
- * API. That is also the reason why this enum is not called "Importance". Apart from that, in most
- * cases its meaning is identical to the importance of an entity.
- *
  * @author Timm Fitschen (t.fitschen@indiscale.com)
  */
-public enum StatementStatus {
+public enum StatementStatus implements StatementStatusInterface {
   OBLIGATORY,
   RECOMMENDED,
   SUGGESTED,
   FIX,
-  REPLACEMENT
 }
diff --git a/src/main/java/org/caosdb/server/entity/StatementStatusInterface.java b/src/main/java/org/caosdb/server/entity/StatementStatusInterface.java
new file mode 100644
index 00000000..d1596afd
--- /dev/null
+++ b/src/main/java/org/caosdb/server/entity/StatementStatusInterface.java
@@ -0,0 +1,29 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package org.caosdb.server.entity;
+
+/**
+ * Interface for the StatementStatus. This may be used by back-end implementations to use back-end
+ * specific implementations (e.g.
+ * org.caosdb.server.database.backend.implementation.MySQL.ReplacementStatus).
+ */
+public interface StatementStatusInterface {
+  public String name();
+}
diff --git a/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java b/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
index f23408df..b294a17e 100644
--- a/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
+++ b/src/main/java/org/caosdb/server/entity/wrapper/EntityWrapper.java
@@ -38,7 +38,7 @@ import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.FileProperties;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Role;
-import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.StatementStatusInterface;
 import org.caosdb.server.entity.Version;
 import org.caosdb.server.entity.container.ParentContainer;
 import org.caosdb.server.entity.container.PropertyContainer;
@@ -112,13 +112,13 @@ public abstract class EntityWrapper implements EntityInterface {
   }
 
   @Override
-  public StatementStatus getStatementStatus() {
+  public StatementStatusInterface getStatementStatus() {
     return this.entity.getStatementStatus();
   }
 
   @Override
-  public void setStatementStatus(final StatementStatus statementStatus) {
-    this.entity.setStatementStatus(statementStatus);
+  public void setStatementStatus(final StatementStatusInterface replacement) {
+    this.entity.setStatementStatus(replacement);
   }
 
   @Override
diff --git a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
index 1b70a729..8ab8b057 100644
--- a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
+++ b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
@@ -75,6 +75,7 @@ import org.caosdb.server.entity.MagicTypes;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.StatementStatusInterface;
 import org.caosdb.server.entity.container.ParentContainer;
 import org.caosdb.server.entity.container.RetrieveContainer;
 import org.caosdb.server.entity.wrapper.Property;
@@ -359,8 +360,8 @@ public class CaosDBToGrpcConverters {
     return convertScalarValue(v.getWrapped()).build();
   }
 
-  private Importance convert(final StatementStatus statementStatus) {
-    switch (statementStatus) {
+  private Importance convert(final StatementStatusInterface statementStatus) {
+    switch ((StatementStatus) statementStatus) {
       case FIX:
         return Importance.IMPORTANCE_FIX;
       case OBLIGATORY:
-- 
GitLab


From 5d5ea96303228064c854194d1ce60b7c4ab298d7 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 16 Aug 2023 11:24:17 +0200
Subject: [PATCH 8/9] WIP: MAINT: refactor retrieve properties (move
 backend-logic to backend)

---
 .../MySQL/MySQLInsertEntityProperties.java               | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
index b57719f5..becb56a2 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java
@@ -130,11 +130,16 @@ public class MySQLInsertEntityProperties extends MySQLTransaction
 
       if (property.getStatementStatus() == ReplacementStatus.REPLACEMENT) {
         // special treatment: swap value and id. This is part of the back-end specification for the
-        // representation of replacement.
+        // representation of replacement. The reason why this happens here (and
+        // not in the replacement class for instance) is that the original
+        // Property must not be changed for this. Otherwise we would have to
+        // change it back after the insertion or internally used replacement ids
+        // would be leaked.
 
-        // value should be the
+        // value is to be the id of the property which is being replaced
         fp.value = fp.id.toString();
 
+        // id is to be the replacement id (an internally used/private id)
         fp.id = ((ReferenceValue) property.getValue()).getId().toInteger();
         table = Table.reference_data;
       } else {
-- 
GitLab


From f9f540e17d9eaee27d542f90e5eb10b94a7ce6be Mon Sep 17 00:00:00 2001
From: Florian Spreckelsen <f.spreckelsen@indiscale.com>
Date: Thu, 17 Aug 2023 17:48:21 +0000
Subject: [PATCH 9/9] REVIEW: apply suggested changes

---
 .../database/backend/implementation/MySQL/DatabaseUtils.java  | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
index c123b467..f7d364ae 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java
@@ -93,10 +93,6 @@ public class DatabaseUtils {
         && !p.isDatatypeOverride()
         && (!p.hasProperties() || hasUniquePropertyId(p, e))
         && !(p.getDatatype() instanceof AbstractCollectionDatatype)) {
-      // this property can be represented without any replacement. We explicitly
-      // setReplacement(null) because there is a corner case (related to the inheritance of
-      // properties) where there is a replacement present which belongs to the parent entity, see
-      // https://gitlab.com/caosdb/caosdb-server/-/issues/216.
       stage1Inserts.add(p);
     } else {
       Replacement r = new Replacement(p);
-- 
GitLab