=== modified file 'src/com/goldencode/p2j/persist/TemporaryBuffer.java'
--- src/com/goldencode/p2j/persist/TemporaryBuffer.java	2020-11-18 17:33:45 +0000
+++ src/com/goldencode/p2j/persist/TemporaryBuffer.java	2020-11-23 17:31:35 +0000
@@ -3374,6 +3374,7 @@
          
          if (!srcBuf.isTableDefinitelyEmpty())
          {
+            DmoMeta srcMeta = srcBuf.getDmoInfo();
             DmoMeta dstMeta = dstBuf.getDmoInfo();
             
             // skip validation when:
@@ -3382,9 +3383,10 @@
             // TODO: enhance this if we are not in append mode, the dst and src tables are different, and they 
             // have the same set of unique indexes (by the field position, not name) and the same set of
             // mandatory fields (by field position, not name)
-            boolean bulkInsert = !append &&
-               ((dstBuf.getDMOImplementationClass() == srcBuf.getDMOImplementationClass()) ||
-                (dstMeta.getUniqueConstraints().isEmpty() && dstMeta.getMandatoryFields().isEmpty()));
+            boolean bulkInsert = !append && srcMeta.getSignature().equals(dstMeta.getSignature());
+            
+            /*((dstBuf.getDMOImplementationClass() == srcBuf.getDMOImplementationClass()) ||
+                (dstMeta.getUniqueConstraints().isEmpty() && dstMeta.getMandatoryFields().isEmpty()));*/
             
             if (bulkInsert)
             {

=== modified file 'src/com/goldencode/p2j/persist/orm/DmoMeta.java'
--- src/com/goldencode/p2j/persist/orm/DmoMeta.java	2020-10-14 21:06:19 +0000
+++ src/com/goldencode/p2j/persist/orm/DmoMeta.java	2020-11-23 18:03:22 +0000
@@ -202,6 +202,9 @@
    /** The set of properties and their metadata, indexed by their in-memory positions. */
    private Pair<Property, PropertyMeta>[] properties = null;
    
+   /** The set of properties and their metadata, indexed by their in-memory positions. */
+   private String signature;
+   
    /** The unique id of this DMO. */
    private final int id = idSeed.incrementAndGet();
    
@@ -311,6 +314,10 @@
          fields.put(property.name, property);
       }
       
+      // build a String signature suitable for identifying if two DMOs are compatible in certain 
+      // operations (for example copy-temp-table).
+      signature = SignatureBuilder.buildSignature(getFields(false), getUniqueProperties());
+      
       // the eventual relations cannot be created at this moment, they will be processed at a
       // later time, when the respective DMO interfaces will be loaded. 
    }
@@ -753,6 +760,23 @@
       return uniqueConstraints;
    }
    
+   public Set<Property> getUniqueProperties()
+   {
+      Set<Property> uniqueProp = new HashSet<>();
+      List<Set<String>> indexList = getUniqueConstraints();
+      
+      for (Set<String> index : indexList)
+      {
+         for (String field : index)
+         {
+            Property prop = fields.get(field);
+            uniqueProp.add(prop);
+         }
+      }
+      
+      return uniqueProp;
+   }
+   
    /**
     * Get the {@code Property} that correspond to the k-th entry in {@code data} table of the class
     * implementing this DMO.
@@ -1085,6 +1109,16 @@
    }
    
    /**
+    * Get the structure-based signature for this DMO.
+    * 
+    * @return  The signature of this DMO.
+    */
+   public String getSignature()
+   {
+      return signature;
+   }
+   
+   /**
     * Obtain a short description of this object, usually for debugging.
     * 
     * @return  a short description of this object.

=== added file 'src/com/goldencode/p2j/persist/orm/SignatureBuilder.java'
--- src/com/goldencode/p2j/persist/orm/SignatureBuilder.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/persist/orm/SignatureBuilder.java	2020-11-23 18:01:48 +0000
@@ -0,0 +1,164 @@
+package com.goldencode.p2j.persist.orm;
+
+import java.util.*;
+import java.util.logging.*;
+
+import com.goldencode.p2j.util.*;
+
+/**
+ * Compute a string based signature for a DMO, which contains information regarding the 
+ * type of each field, extents and eventually some restrictions like mandatory or unique.
+ * In order to have the prefix-code property for each generated string, the following 
+ * standards should be respected:
+ * - The properties are represented in the order they are provided.
+ * - A data type representation should be a single upper case character.
+ * - In case the field has an extent, the length of the extent will follow the data type 
+ * representation as a number (no matter the number of digits)
+ * - At the end of the representation of one property, there will be a sequence of lower-case
+ * characters showing which restrictions are imposed on this property (mandatory/unique). The
+ * order of these is important.
+ */
+public class SignatureBuilder
+{
+   /** 
+    * A map statically initialized with the classic JVM data type single-char representation.
+    * This can also contain other mappings created at the runtime.
+    */
+   private static Map<Class<? extends BaseDataType>, Character> typeMapping;
+   
+   /** The character used for representing a mandatory property */
+   private static char CHAR_MANDANTORY = 'm';
+
+   /** The character used for representing a unique property */
+   private static char CHAR_UNIQUE = 'u';
+   
+   /** Logger */
+   private static final Logger LOG = LogHelper.getLogger(SignatureBuilder.class.getName());
+   
+   /** Static initialization of the type mapping */
+   static 
+   {
+      typeMapping = new IdentityHashMap<>();
+
+      typeMapping.put(character.class, 'C');
+      typeMapping.put(decimal.class, 'F');
+      typeMapping.put(integer.class, 'I');
+      typeMapping.put(int64.class, 'J');
+      typeMapping.put(logical.class, 'Z');
+   }
+   
+   /**
+    * This builds a string which structurally represents a DMO. The DMOs which are compatible,
+    * from strcture's point of view, should have the same signature. This is useful for quickly
+    * checking compatibility (example: copy-temp-table).
+    * 
+    * @param   it
+    *          An iterator to the whole collection of properties for the processed DMO.
+    * @param   uniqueProperties
+    *          A set of properties which are unique. These should be flagged in the signature.
+    * @return  A string representing the signature for a DMO having the specified properties..
+    */
+   static String buildSignature(Iterator<Property> it, Set<Property> uniqueProperties)
+   {
+      StringBuilder sb = new StringBuilder();
+      
+      TypeIdentifierProvider findSuitableCandidate = (type) -> 
+      {
+         Set<Character> values = new HashSet<Character>(typeMapping.values());
+         for (char raw : type.getName().toCharArray())
+         {
+            char candidate = Character.toUpperCase(raw);
+            if (!values.contains(candidate))
+            {
+               typeMapping.put(type, candidate);
+               return candidate;
+            }
+         }
+         return null;
+      };
+      
+      TypeIdentifierProvider findAnyCandidate = (type) -> 
+      {
+         Set<Character> values = new HashSet<Character>(typeMapping.values());
+         for (char candidate = 'A'; candidate <= 'Z'; candidate++)
+         {
+            if (!values.contains(candidate))
+            {
+               typeMapping.put(type, candidate);
+               return candidate;
+            }
+         }
+         return null;
+      };
+      
+      while (it.hasNext())
+      {
+         Property prop = it.next();
+         Class<? extends BaseDataType> type = (Class<? extends BaseDataType>) prop._fwdType;
+         Character ch = typeMapping.get(type);
+         if (ch == null)
+         {
+            // for this kind of situations, find a suitable representation for this type
+            ch = findSuitableCandidate.provide(type);
+         }
+         if (ch == null)
+         {
+            // can't find a suitable representation, assign a random character
+            ch = findAnyCandidate.provide(type);
+         }
+         if (ch == null)
+         {
+            if (LOG.isLoggable(Level.WARNING))
+            {
+               LOG.log(Level.WARNING, "Couldn't find a character representation for " + type.getName());
+            }
+            return null;
+         }
+         
+         sb.append(ch);
+         
+         int extent = prop.extent;
+         if (extent > 0)
+         {
+            sb.append(extent);
+         }
+         
+         processFieldRestrictions(prop, uniqueProperties.contains(prop), sb);
+      }
+      
+      return sb.toString();
+   }
+   
+   /**
+    * Helper which sets restrictions like mandatory or unique to a specified field.
+    * 
+    * @param   prop
+    *          The property for which the restriction analysis is done.
+    * @param   unique
+    *          A flag which indicates that the property should be unique.
+    * @param   sb
+    *          The string builder which should be completed with the found restrictions.
+    */
+   private static void processFieldRestrictions(Property prop, 
+                                                boolean unique,
+                                                StringBuilder sb)
+   {
+      if (prop.mandatory)
+      {
+         sb.append(CHAR_MANDANTORY);
+      }
+      
+      if (unique)
+      {
+         sb.append(CHAR_UNIQUE);
+      }
+   }
+   
+   /**
+    * Helper for anonymous functions used in identifying a suitable character for a specified data type. 
+    */
+   interface TypeIdentifierProvider 
+   {
+      Character provide(Class<? extends BaseDataType> type);
+   }
+}

