[ASTERIXDB-2607][RT][*DB] Fix detecting duplicate fields in record construction

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
Fix detecting duplicate field names in record construction.

Change-Id: Ib11647cee4e6dd07f04b9e91ce03cb72f293bcb2
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3479
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
index 1273ed1..742cfb3 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
@@ -218,16 +218,25 @@
                 openPartOffsetArray = new byte[openPartOffsetArraySize];
             }
 
+            // field names with the same hash code should be adjacent to each other after sorting
             Arrays.sort(this.openPartOffsets, 0, numberOfOpenFields);
             if (numberOfOpenFields > 1) {
                 byte[] openBytes = openPartOutputStream.getByteArray();
-                for (int i = 1; i < numberOfOpenFields; i++) {
-                    if (utf8Comparator.compare(openBytes, (int) openPartOffsets[i - 1], openFieldNameLengths[i - 1],
-                            openBytes, (int) openPartOffsets[i], openFieldNameLengths[i]) == 0) {
+                for (int i = 0, k = 1; i < numberOfOpenFields - 1;) {
+                    if (utf8Comparator.compare(openBytes, (int) openPartOffsets[i], openFieldNameLengths[i], openBytes,
+                            (int) openPartOffsets[k], openFieldNameLengths[k]) == 0) {
                         String field = utf8SerDer.deserialize(new DataInputStream(new ByteArrayInputStream(openBytes,
                                 (int) openPartOffsets[i], openFieldNameLengths[i])));
                         throw new HyracksDataException(
-                                "Open fields " + (i - 1) + " and " + i + " have the same field name \"" + field + "\"");
+                                "Open fields " + i + " and " + k + " have the same field name \"" + field + "\"");
+                    }
+                    if (sameHashes(openPartOffsets, i, k) && k < numberOfOpenFields - 1) {
+                        // keep comparing the current field i with the next field k
+                        k++;
+                    } else {
+                        // the current field i has no duplicates; move to the adjacent one
+                        i++;
+                        k = i + 1;
                     }
                 }
             }
@@ -249,6 +258,10 @@
         writeRecord(out, writeTypeTag, h, recordLength);
     }
 
+    private static boolean sameHashes(long[] hashAndOffset, int fieldId1, int fieldId2) {
+        return (int) (hashAndOffset[fieldId1] >>> 32) == (int) (hashAndOffset[fieldId2] >>> 32);
+    }
+
     private void writeRecord(DataOutput out, boolean writeTypeTag, int headerSize, int recordLength)
             throws HyracksDataException {
         try {