You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by dl...@apache.org on 2021/10/07 04:31:18 UTC

[asterixdb] branch master updated: [NO ISSUE][COMP] Introduce RecordFieldOrderAnnotation for record types

This is an automated email from the ASF dual-hosted git repository.

dlych pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 89d95a5  [NO ISSUE][COMP] Introduce RecordFieldOrderAnnotation for record types
89d95a5 is described below

commit 89d95a57e1590c38239376eaf0578c6d2f6f4338
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Wed Oct 6 13:09:41 2021 -0700

    [NO ISSUE][COMP] Introduce RecordFieldOrderAnnotation for record types
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    Details:
    - Introduce RecordFieldOrderAnnotation for record types
      which acts as a hint specifying field order
    - Generate these annotations for types created by
      OpenRecordConstructorResultType type computer
    - Modify TypeResolverUtil to propagate these annotations
      if they're available
    - Add testcase
    
    Change-Id: I53c8b15f2d2299e7df670bcd2ed7880e3bcca344
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/13586
    Reviewed-by: Ali Alsuliman <al...@gmail.com>
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
---
 .../common/annotations/IRecordTypeAnnotation.java  |  7 +--
 .../annotations/RecordFieldOrderAnnotation.java    | 62 ++++++++++++++++++++++
 .../dataflow/data/common/TypeResolverUtil.java     | 38 +++++++++++--
 .../impl/OpenRecordConstructorResultType.java      | 17 ++++--
 .../org/apache/asterix/om/types/ARecordType.java   | 21 +++++++-
 .../dataflow/data/common/TypeResolverUtilTest.java | 40 ++++++++++++++
 6 files changed, 173 insertions(+), 12 deletions(-)

diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/IRecordTypeAnnotation.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/IRecordTypeAnnotation.java
index 5edef2f..ec0917e 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/IRecordTypeAnnotation.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/IRecordTypeAnnotation.java
@@ -19,9 +19,10 @@
 package org.apache.asterix.common.annotations;
 
 public interface IRecordTypeAnnotation {
-    public enum Kind {
-        RECORD_DATA_GEN
+    enum Kind {
+        RECORD_DATA_GEN,
+        RECORD_FIELD_ORDER
     }
 
-    public Kind getKind();
+    Kind getKind();
 }
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/RecordFieldOrderAnnotation.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/RecordFieldOrderAnnotation.java
new file mode 100644
index 0000000..04cf589
--- /dev/null
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/annotations/RecordFieldOrderAnnotation.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.common.annotations;
+
+import java.io.Serializable;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+
+/**
+ * Contains an ordered set of fields of a record
+ */
+public final class RecordFieldOrderAnnotation implements IRecordTypeAnnotation, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final LinkedHashSet<String> fieldNames;
+
+    public RecordFieldOrderAnnotation(LinkedHashSet<String> fieldNames) {
+        this.fieldNames = Objects.requireNonNull(fieldNames);
+    }
+
+    public LinkedHashSet<String> getFieldNames() {
+        return fieldNames;
+    }
+
+    @Override
+    public Kind getKind() {
+        return Kind.RECORD_FIELD_ORDER;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        RecordFieldOrderAnnotation that = (RecordFieldOrderAnnotation) o;
+        return fieldNames.equals(that.fieldNames);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fieldNames);
+    }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
index 3cc9e3d..9d52a85 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
@@ -21,9 +21,12 @@ package org.apache.asterix.dataflow.data.common;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.asterix.common.annotations.IRecordTypeAnnotation;
+import org.apache.asterix.common.annotations.RecordFieldOrderAnnotation;
 import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
 import org.apache.asterix.om.types.AOrderedListType;
 import org.apache.asterix.om.types.ARecordType;
@@ -168,9 +171,38 @@ public class TypeResolverUtil {
                 generalizeRecordFields(leftType, rightType, allPossibleAdditionalFieldNames, fieldNames, fieldTypes);
         boolean rightAllMatched =
                 generalizeRecordFields(rightType, leftType, allPossibleAdditionalFieldNames, fieldNames, fieldTypes);
-        return new ARecordType("generalized-record-type", fieldNames.toArray(new String[fieldNames.size()]),
-                fieldTypes.toArray(new IAType[fieldTypes.size()]), !(canBeClosed && leftAllMatched && rightAllMatched),
-                knowsAdditonalFieldNames ? allPossibleAdditionalFieldNames : null);
+        boolean resultTypeIsOpen = !(canBeClosed && leftAllMatched && rightAllMatched);
+        String[] fieldNamesArray = fieldNames.toArray(new String[0]);
+        IAType[] fieldTypesArray = fieldTypes.toArray(new IAType[0]);
+        ARecordType resultType;
+        if (resultTypeIsOpen && knowsAdditonalFieldNames) {
+            resultType = new ARecordType("generalized-record-type", fieldNamesArray, fieldTypesArray, resultTypeIsOpen,
+                    allPossibleAdditionalFieldNames);
+            LinkedHashSet<String> resultFieldOrder = generalizeRecordFieldOrderHint(leftType, rightType);
+            if (resultFieldOrder != null) {
+                resultType.getAnnotations().add(new RecordFieldOrderAnnotation(resultFieldOrder));
+            }
+        } else {
+            resultType = new ARecordType("generalized-record-type", fieldNamesArray, fieldTypesArray, resultTypeIsOpen);
+        }
+        return resultType;
+    }
+
+    private static LinkedHashSet<String> generalizeRecordFieldOrderHint(ARecordType leftType, ARecordType rightType) {
+        IRecordTypeAnnotation leftFieldOrderHint =
+                leftType.findAnnotation(IRecordTypeAnnotation.Kind.RECORD_FIELD_ORDER);
+        if (leftFieldOrderHint == null) {
+            return null;
+        }
+        IRecordTypeAnnotation rightFieldOrderHint =
+                rightType.findAnnotation(IRecordTypeAnnotation.Kind.RECORD_FIELD_ORDER);
+        if (rightFieldOrderHint == null) {
+            return null;
+        }
+        LinkedHashSet<String> resultFieldOrder = new LinkedHashSet<>();
+        resultFieldOrder.addAll(((RecordFieldOrderAnnotation) leftFieldOrderHint).getFieldNames());
+        resultFieldOrder.addAll(((RecordFieldOrderAnnotation) rightFieldOrderHint).getFieldNames());
+        return resultFieldOrder;
     }
 
     // Generates closed fields and possible additional fields of a generalized type of two record types.
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OpenRecordConstructorResultType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OpenRecordConstructorResultType.java
index 6deb17c..6b08230 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OpenRecordConstructorResultType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OpenRecordConstructorResultType.java
@@ -22,9 +22,11 @@ package org.apache.asterix.om.typecomputer.impl;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.asterix.common.annotations.RecordFieldOrderAnnotation;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
@@ -51,7 +53,7 @@ public class OpenRecordConstructorResultType implements IResultTypeComputer {
             IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
         AbstractFunctionCallExpression f = (AbstractFunctionCallExpression) expression;
 
-        /**
+        /*
          * if type has been top-down propagated, use the enforced type
          */
         ARecordType type = (ARecordType) TypeCastUtils.getRequiredType(f);
@@ -66,6 +68,7 @@ public class OpenRecordConstructorResultType implements IResultTypeComputer {
         // but are additional possible field names. For example, a field "foo" with type
         // ANY cannot be in the closed part, but "foo" is a possible field name.
         Set<String> allPossibleAdditionalFieldNames = new HashSet<>();
+        LinkedHashSet<String> allPossibleFieldNamesOrdered = new LinkedHashSet<>();
         boolean canProvideAdditionFieldInfo = true;
         boolean isOpen = false;
         while (argIter.hasNext()) {
@@ -91,11 +94,17 @@ public class OpenRecordConstructorResultType implements IResultTypeComputer {
                 }
                 isOpen = true;
             }
+            allPossibleFieldNamesOrdered.add(fieldName);
         }
         String[] fieldNames = namesList.toArray(new String[0]);
         IAType[] fieldTypes = typesList.toArray(new IAType[0]);
-        return canProvideAdditionFieldInfo
-                ? new ARecordType(null, fieldNames, fieldTypes, isOpen, allPossibleAdditionalFieldNames)
-                : new ARecordType(null, fieldNames, fieldTypes, isOpen);
+        ARecordType resultType;
+        if (isOpen && canProvideAdditionFieldInfo) {
+            resultType = new ARecordType(null, fieldNames, fieldTypes, isOpen, allPossibleAdditionalFieldNames);
+            resultType.getAnnotations().add(new RecordFieldOrderAnnotation(allPossibleFieldNamesOrdered));
+        } else {
+            resultType = new ARecordType(null, fieldNames, fieldTypes, isOpen);
+        }
+        return resultType;
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
index a79b8de..e256e1b 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
@@ -22,8 +22,10 @@ package org.apache.asterix.om.types;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.apache.asterix.common.annotations.IRecordTypeAnnotation;
@@ -145,6 +147,17 @@ public class ARecordType extends AbstractComplexType {
         return annotations;
     }
 
+    public IRecordTypeAnnotation findAnnotation(IRecordTypeAnnotation.Kind kind) {
+        if (annotations != null) {
+            for (IRecordTypeAnnotation ant : annotations) {
+                if (ant.getKind().equals(kind)) {
+                    return ant;
+                }
+            }
+        }
+        return null;
+    }
+
     @Override
     public String toString() {
         return append(new StringBuilder()).toString();
@@ -312,7 +325,10 @@ public class ARecordType extends AbstractComplexType {
                 newTypes[i] = type.fieldTypes[i];
             }
         }
-        return new ARecordType(type.typeName, type.fieldNames, newTypes, type.isOpen);
+        Set<String> newAllPossibleAdditionalFieldNames =
+                allPossibleAdditionalFieldNames != null ? new HashSet<>(allPossibleAdditionalFieldNames) : null;
+        return new ARecordType(type.typeName, type.fieldNames, newTypes, type.isOpen,
+                newAllPossibleAdditionalFieldNames);
     }
 
     @Override
@@ -344,7 +360,8 @@ public class ARecordType extends AbstractComplexType {
         }
         ARecordType rt = (ARecordType) obj;
         return (isOpen == rt.isOpen) && Arrays.deepEquals(fieldNames, rt.fieldNames)
-                && Arrays.deepEquals(fieldTypes, rt.fieldTypes);
+                && Arrays.deepEquals(fieldTypes, rt.fieldTypes)
+                && Objects.equals(allPossibleAdditionalFieldNames, rt.allPossibleAdditionalFieldNames);
     }
 
     @Override
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/dataflow/data/common/TypeResolverUtilTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/dataflow/data/common/TypeResolverUtilTest.java
index 5303870..dad6ee9 100644
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/dataflow/data/common/TypeResolverUtilTest.java
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/dataflow/data/common/TypeResolverUtilTest.java
@@ -20,11 +20,15 @@
 package org.apache.asterix.dataflow.data.common;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.asterix.common.annotations.IRecordTypeAnnotation;
+import org.apache.asterix.common.annotations.RecordFieldOrderAnnotation;
 import org.apache.asterix.om.types.AOrderedListType;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.AUnionType;
@@ -124,6 +128,42 @@ public class TypeResolverUtilTest {
     }
 
     @Test
+    public void testRecordTypeFieldOrderHint() {
+        // Constructs input types.
+        ARecordType leftRecordType = new ARecordType(null, new String[] { "a", "b", "c" },
+                new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING, BuiltinType.ASTRING, }, true,
+                new HashSet<>(Arrays.asList("d", "e")));
+        leftRecordType.getAnnotations()
+                .add(new RecordFieldOrderAnnotation(new LinkedHashSet<>(Arrays.asList("a", "b", "c", "d", "e"))));
+
+        ARecordType rightRecordType = new ARecordType(null, new String[] { "a", "c", "d" },
+                new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING, BuiltinType.ASTRING, }, true,
+                new HashSet<>(Arrays.asList("e", "f")));
+        rightRecordType.getAnnotations()
+                .add(new RecordFieldOrderAnnotation(new LinkedHashSet<>(Arrays.asList("a", "c", "d", "e", "f"))));
+
+        // Resolves input types to a generalized type.
+        List<IAType> inputTypes = new ArrayList<>();
+        inputTypes.add(leftRecordType);
+        inputTypes.add(rightRecordType);
+        ARecordType resolvedType = (ARecordType) TypeResolverUtil.resolve(inputTypes);
+
+        // Constructs the expected type.
+        Set<String> possibleAdditionalFields = new HashSet<>(Arrays.asList("b", "d", "e", "f"));
+        ARecordType expectedType = new ARecordType(null, new String[] { "a", "c" },
+                new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING }, true, possibleAdditionalFields);
+        expectedType.getAnnotations()
+                .add(new RecordFieldOrderAnnotation(new LinkedHashSet<>(Arrays.asList("a", "b", "c", "d", "e", "f"))));
+
+        // Compares the resolved type with the expected type.
+        Assert.assertEquals(expectedType, resolvedType);
+
+        IRecordTypeAnnotation expecedAnn = expectedType.findAnnotation(IRecordTypeAnnotation.Kind.RECORD_FIELD_ORDER);
+        IRecordTypeAnnotation resolvedAnn = resolvedType.findAnnotation(IRecordTypeAnnotation.Kind.RECORD_FIELD_ORDER);
+        Assert.assertEquals(expecedAnn, resolvedAnn);
+    }
+
+    @Test
     public void testOrderedListType() {
         // Constructs input types.
         ARecordType leftRecordType = new ARecordType("null", new String[] { "a", "b" },