[NO ISSUE][IDX] Refactoring of array index code.
- user model changes: no
- storage format changes: no
- interface changes: no
Refactor: no longer using depth indicators, rather using UNNEST flags with
flattened field names. We can no longer have back-to-back UNNESTs (i.e. arrays
within arrays without an ASSIGN intermediate), as no such array index
can be created..
Change-Id: Id337d1032796e1d1c2ce68ea2861b4a81dd19aa5
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/12063
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Glenn Galvizo <ggalvizo@uci.edu>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
index c3859be..81c6c2a 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
@@ -728,10 +728,10 @@
// Walk the array path.
List<String> flatFirstFieldName = ArrayIndexUtil.getFlattenedKeyFieldNames(
workingElement.getUnnestList(), workingElement.getProjectList().get(0));
- List<Integer> firstArrayIndicator = ArrayIndexUtil
- .getArrayDepthIndicator(workingElement.getUnnestList(), workingElement.getProjectList().get(0));
+ List<Boolean> firstUnnestFlags = ArrayIndexUtil.getUnnestFlags(workingElement.getUnnestList(),
+ workingElement.getProjectList().get(0));
ArrayIndexUtil.walkArrayPath((isOpenOrNestedField) ? null : recordType, flatFirstFieldName,
- firstArrayIndicator, branchCreator);
+ firstUnnestFlags, branchCreator);
// For all other elements in the PROJECT list, add an assign.
for (int j = 1; j < workingElement.getProjectList().size(); j++) {
@@ -1054,25 +1054,19 @@
@Override
public void executeActionOnEachArrayStep(ARecordType startingStepRecordType, IAType workingType,
- List<String> fieldName, boolean isFirstArrayStep, boolean isFirstUnnestInStep,
- boolean isLastUnnestInIntermediateStep) throws AlgebricksException {
+ List<String> fieldName, boolean isFirstArrayStep, boolean isLastUnnestInIntermediateStep)
+ throws AlgebricksException {
if (!isFirstWalk) {
// We have already built the UNNEST path, do not build again.
return;
}
+ // Get the field we want to UNNEST from our record.
ILogicalExpression accessToUnnestVar;
- if (isFirstUnnestInStep) {
- // This is the first UNNEST step. Get the field we want to UNNEST from our record.
- accessToUnnestVar = (startingStepRecordType != null)
- ? getFieldAccessFunction(new MutableObject<>(createLastRecordVarRef()),
- startingStepRecordType.getFieldIndex(fieldName.get(0)), fieldName)
- : getFieldAccessFunction(new MutableObject<>(createLastRecordVarRef()), -1, fieldName);
- } else {
- // This is the second+ UNNEST step. Refer back to the previously unnested variable.
- accessToUnnestVar = new VariableReferenceExpression(this.lastFieldVars.get(0));
- this.lastFieldVars.clear();
- }
+ accessToUnnestVar = (startingStepRecordType != null)
+ ? getFieldAccessFunction(new MutableObject<>(createLastRecordVarRef()),
+ startingStepRecordType.getFieldIndex(fieldName.get(0)), fieldName)
+ : getFieldAccessFunction(new MutableObject<>(createLastRecordVarRef()), -1, fieldName);
UnnestingFunctionCallExpression scanCollection = new UnnestingFunctionCallExpression(
BuiltinFunctions.getBuiltinFunctionInfo(BuiltinFunctions.SCAN_COLLECTION),
Collections.singletonList(new MutableObject<>(accessToUnnestVar)));
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
index c78e89f..3f4d3f2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
@@ -3060,7 +3060,6 @@
MutableInt fieldSource = new MutableInt(0);
ARecordType workingRecordType = subTree.getRecordType();
- // TODO: (GLENN) Refactor this to use ArrayIndexUtil.
// Iterate through our array index structure. We must match the depth and field names for the caller's variable
// to qualify for an array-index optimization.
LogicalVariable varFromParent = assignVar;
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/ArrayBTreeAccessMethod.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/ArrayBTreeAccessMethod.java
index 653d15b..d364b53 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/ArrayBTreeAccessMethod.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/ArrayBTreeAccessMethod.java
@@ -179,7 +179,6 @@
@Override
protected IAType getIndexedKeyType(Index.IIndexDetails chosenIndexDetails, int keyPos) throws CompilationException {
- // TODO (GLENN): This assumes a flattened key list. Refactor / clarify this when removing depth indicators.
Index.ArrayIndexDetails arrayIndexDetails = (Index.ArrayIndexDetails) chosenIndexDetails;
int elementPos = 0;
for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/ArrayBTreeResourceFactoryProvider.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/ArrayBTreeResourceFactoryProvider.java
index 5654d16..c0bdc75 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/ArrayBTreeResourceFactoryProvider.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/ArrayBTreeResourceFactoryProvider.java
@@ -18,7 +18,6 @@
*/
package org.apache.asterix.metadata.declared;
-import java.util.List;
import java.util.Map;
import org.apache.asterix.common.config.DatasetConfig.DatasetType;
@@ -134,10 +133,8 @@
sourceType = metaType;
}
for (int i = 0; i < e.getProjectList().size(); i++) {
- List<String> project = e.getProjectList().get(i);
Pair<IAType, Boolean> keyTypePair = ArrayIndexUtil.getNonNullableOpenFieldType(e.getTypeList().get(i),
- ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), project), sourceType,
- ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), project));
+ e.getUnnestList(), e.getProjectList().get(i), sourceType);
IAType keyType = keyTypePair.first;
secondaryTypeTraits[secondaryTypeTraitPos++] = typeTraitProvider.getTypeTrait(keyType);
}
@@ -175,10 +172,8 @@
sourceType = metaType;
}
for (int i = 0; i < e.getProjectList().size(); i++) {
- List<String> project = e.getProjectList().get(i);
Pair<IAType, Boolean> keyTypePair = ArrayIndexUtil.getNonNullableOpenFieldType(e.getTypeList().get(i),
- ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), project), sourceType,
- ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), project));
+ e.getUnnestList(), e.getProjectList().get(i), sourceType);
IAType keyType = keyTypePair.first;
secondaryCmpFactories[secondaryCmpFactoriesPos++] =
cmpFactoryProvider.getBinaryComparatorFactory(keyType, true);
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/ArrayIndexUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/ArrayIndexUtil.java
index f4dfe56..0778ad9 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/ArrayIndexUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/ArrayIndexUtil.java
@@ -39,14 +39,18 @@
public class ArrayIndexUtil {
/**
- * @deprecated Use the project + unnest scheme instead of array indicators.
+ * Similar function to Index's "getSubFieldType", but accounts for array fields as well.
*/
- public static IAType getSubFieldInArrayType(ARecordType recordType, List<String> subFieldName,
- List<Integer> arrayDepthIndicators) throws AlgebricksException {
- IAType subType = recordType.getFieldType(subFieldName.get(0));
- for (int i = 1; i < subFieldName.size(); i++) {
+ public static IAType getSubFieldType(ARecordType recordType, List<List<String>> unnestList,
+ List<String> projectList) throws AlgebricksException {
+ List<String> flattenedFieldName = ArrayIndexUtil.getFlattenedKeyFieldNames(unnestList, projectList);
+ List<Boolean> unnestFlags = ArrayIndexUtil.getUnnestFlags(unnestList, projectList);
+ IAType subType = recordType.getFieldType(flattenedFieldName.get(0));
+
+ for (int i = 1; i < flattenedFieldName.size(); i++) {
if (subType == null) {
return null;
+
} else if (subType.getTypeTag().equals(ATypeTag.UNION)) {
// Support enforced types here.
subType = ((AUnionType) subType).getActualType();
@@ -56,31 +60,30 @@
"Field accessor is not defined for values of type " + subType.getTypeTag());
}
}
- if (subType.getTypeTag().equals(ATypeTag.OBJECT) && arrayDepthIndicators.get(i - 1) == 0) {
- subType = ((ARecordType) subType).getFieldType(subFieldName.get(i));
+
+ if (subType.getTypeTag().equals(ATypeTag.OBJECT) && !unnestFlags.get(i - 1)) {
+ subType = ((ARecordType) subType).getFieldType(flattenedFieldName.get(i));
+
} else if ((subType.getTypeTag().equals(ATypeTag.ARRAY) || subType.getTypeTag().equals(ATypeTag.MULTISET))
- && arrayDepthIndicators.get(i - 1) > 0) {
- for (int j = 0; j < arrayDepthIndicators.get(i - 1); j++) {
- subType = TypeComputeUtils.extractListItemType(subType);
- }
- subType = (subType != null) ? ((ARecordType) subType).getFieldType(subFieldName.get(i)) : null;
+ && unnestFlags.get(i - 1)) {
+ subType = TypeComputeUtils.extractListItemType(subType);
+ subType = (subType != null) ? ((ARecordType) subType).getFieldType(flattenedFieldName.get(i)) : null;
+
} else {
throw new AsterixException(ErrorCode.COMPILATION_ERROR,
- (arrayDepthIndicators.get(i - 1) > 0)
- ? "Object type given, but array depth indicator is " + "non-zero."
- : "Array/multiset type given, but array depth indicator is zero.");
+ unnestFlags.get(i - 1) ? "Object type given, but unnest flag is also raised."
+ : "Array/multiset type given, but unnest flag is lowered.");
}
}
- if (subType != null && arrayDepthIndicators.get(arrayDepthIndicators.size() - 1) > 0) {
+
+ if (subType != null && unnestFlags.get(unnestFlags.size() - 1)) {
// If the end field is an array, we must extract the list item here as well.
- for (int j = 0; j < arrayDepthIndicators.get(arrayDepthIndicators.size() - 1); j++) {
- if (subType instanceof AbstractCollectionType) {
- subType = TypeComputeUtils.extractListItemType(subType);
- } else {
- throw new AsterixException(ErrorCode.COMPILATION_ERROR,
- "Array type expected for last term, but given: "
- + ((subType != null) ? subType.getTypeTag() : "null"));
- }
+ if (subType instanceof AbstractCollectionType) {
+ subType = TypeComputeUtils.extractListItemType(subType);
+
+ } else {
+ throw new AsterixException(ErrorCode.COMPILATION_ERROR,
+ "Array type expected for last term, but given: " + subType.getTypeTag());
}
}
return subType;
@@ -88,15 +91,18 @@
/**
* Given a path of complex types (i.e. lists + records), determine the nullability of the field.
- * @deprecated Use the project + unnest scheme instead of array indicators.
*/
- public static boolean isSubFieldNullable(ARecordType recordType, List<String> subFieldName,
- List<Integer> arrayIndicators) throws AlgebricksException {
- IAType subType = recordType.getFieldType(subFieldName.get(0));
- for (int i = 1; i < subFieldName.size(); i++) {
+ public static boolean isSubFieldNullable(ARecordType recordType, List<List<String>> unnestList,
+ List<String> projectList) throws AlgebricksException {
+ List<String> flattenedFieldName = ArrayIndexUtil.getFlattenedKeyFieldNames(unnestList, projectList);
+ List<Boolean> unnestFlags = ArrayIndexUtil.getUnnestFlags(unnestList, projectList);
+ IAType subType = recordType.getFieldType(flattenedFieldName.get(0));
+
+ for (int i = 1; i < flattenedFieldName.size(); i++) {
if (subType == null) {
return true;
}
+
if (subType.getTypeTag().equals(ATypeTag.UNION)) {
if (NonTaggedFormatUtil.isOptional(subType)) {
return true;
@@ -109,12 +115,12 @@
}
if (subType instanceof ARecordType) {
- subType = ((ARecordType) subType).getFieldType(subFieldName.get(i));
- } else if (subType instanceof AbstractCollectionType && arrayIndicators.get(i - 1) > 0) {
- for (int j = 0; j < arrayIndicators.get(i - 1); j++) {
- subType = TypeComputeUtils.extractListItemType(subType);
- }
- subType = (subType != null) ? ((ARecordType) subType).getFieldType(subFieldName.get(i)) : null;
+ subType = ((ARecordType) subType).getFieldType(flattenedFieldName.get(i));
+
+ } else if (subType instanceof AbstractCollectionType && unnestFlags.get(i - 1)) {
+ subType = TypeComputeUtils.extractListItemType(subType);
+ subType = (subType != null) ? ((ARecordType) subType).getFieldType(flattenedFieldName.get(i)) : null;
+
} else {
throw CompilationException.create(ErrorCode.COMPILATION_ILLEGAL_STATE,
"Illegal field type " + subType.getTypeTag() + " when checking field nullability");
@@ -125,32 +131,37 @@
/**
* Similar function to Index's "getNonNullableOpenFieldType", but accounts for array fields as well.
- * @deprecated Use the project + unnest scheme instead of array indicators.
*/
- public static Pair<IAType, Boolean> getNonNullableOpenFieldType(IAType fieldType, List<String> fieldName,
- ARecordType recType, List<Integer> arrayIndicators) throws AlgebricksException {
+ public static Pair<IAType, Boolean> getNonNullableOpenFieldType(IAType fieldType, List<List<String>> unnestList,
+ List<String> projectList, ARecordType recType) throws AlgebricksException {
Pair<IAType, Boolean> keyPairType = null;
IAType subType = recType;
boolean nullable = false;
- for (int i = 0; i < fieldName.size(); i++) {
+
+ List<String> flattenedFieldName = ArrayIndexUtil.getFlattenedKeyFieldNames(unnestList, projectList);
+ List<Boolean> unnestFlags = ArrayIndexUtil.getUnnestFlags(unnestList, projectList);
+ for (int i = 0; i < flattenedFieldName.size(); i++) {
if (subType instanceof AUnionType) {
nullable = nullable || ((AUnionType) subType).isUnknownableType();
subType = ((AUnionType) subType).getActualType();
}
if (subType instanceof ARecordType) {
- subType = ((ARecordType) subType).getFieldType(fieldName.get(i));
+ subType = ((ARecordType) subType).getFieldType(flattenedFieldName.get(i));
+
} else if ((subType instanceof AOrderedListType || subType instanceof AUnorderedListType)
- && arrayIndicators.get(i - 1) > 0) {
- for (int j = 0; j < arrayIndicators.get(i - 1); j++) {
- subType = TypeComputeUtils.extractListItemType(subType);
- }
+ && unnestFlags.get(i - 1)) {
+ subType = TypeComputeUtils.extractListItemType(subType);
if (subType instanceof ARecordType) {
- subType = ((ARecordType) subType).getFieldType(fieldName.get(i));
+ subType = ((ARecordType) subType).getFieldType(flattenedFieldName.get(i));
+
} else {
- throw AsterixException.create(ErrorCode.COMPILATION_ILLEGAL_STATE, "Unexpected type " + fieldType);
+ throw AsterixException.create(ErrorCode.COMPILATION_ILLEGAL_STATE,
+ "Unexpected type " + subType + ", expected record.");
}
+
} else {
- throw AsterixException.create(ErrorCode.COMPILATION_ILLEGAL_STATE, "Unexpected type " + fieldType);
+ throw AsterixException.create(ErrorCode.COMPILATION_ILLEGAL_STATE,
+ "Unexpected type " + subType + ", expected record, array, or multi-set.");
}
if (subType == null) {
@@ -158,18 +169,20 @@
break;
}
}
+
if (subType != null) {
- IAType keyType = ArrayIndexUtil.getSubFieldInArrayType(recType, fieldName, arrayIndicators);
+ IAType keyType = ArrayIndexUtil.getSubFieldType(recType, unnestList, projectList);
Pair<IAType, Boolean> pair = Index.getNonNullableType(keyType);
- pair.second = pair.second || ArrayIndexUtil.isSubFieldNullable(recType, fieldName, arrayIndicators);
+ pair.second = pair.second || ArrayIndexUtil.isSubFieldNullable(recType, unnestList, projectList);
keyPairType = pair;
}
+
keyPairType.second = keyPairType.second || nullable;
return keyPairType;
}
/**
- * @deprecated Use new unnestList and projectList scheme.
+ * @return The concatenation of the unnest list fields and the project field (for use in creating a unique name).
*/
public static List<String> getFlattenedKeyFieldNames(List<List<String>> unnestList, List<String> projectList) {
if (unnestList == null) {
@@ -188,6 +201,41 @@
}
/**
+ * @return Mapping to the flattened key field names, determine where the UNNESTs occur.
+ */
+ public static List<Boolean> getUnnestFlags(List<List<String>> unnestList, List<String> projectList) {
+ if (unnestList.isEmpty()) {
+ // A simple element has no UNNEST flags raised..
+ List<Boolean> unnestFlags = new ArrayList<>();
+ for (String ignored : projectList) {
+ unnestFlags.add(false);
+ }
+ return unnestFlags;
+
+ } else {
+ List<Boolean> unnestFlagsPrefix = new ArrayList<>();
+ for (List<String> unnestField : unnestList) {
+ for (int i = 0; i < unnestField.size() - 1; i++) {
+ unnestFlagsPrefix.add(false);
+ }
+ unnestFlagsPrefix.add(true);
+ }
+
+ if (projectList == null) {
+ // Stop here. The prefix is the flag vector itself.
+ return unnestFlagsPrefix;
+
+ } else {
+ List<Boolean> unnestFlags = new ArrayList<>(unnestFlagsPrefix);
+ for (int i = 0; i < projectList.size(); i++) {
+ unnestFlags.add(false);
+ }
+ return unnestFlags;
+ }
+ }
+ }
+
+ /**
* @deprecated Use new unnestList and projectList scheme.
*/
public static List<Integer> getArrayDepthIndicator(List<List<String>> unnestList, List<String> projectList) {
@@ -244,18 +292,14 @@
}
/**
- * Given the {@code Index}'s representation of an array path (i.e. a concatenation of record paths, with array
- * steps specified in depths corresponding to an index in the aforementioned record path array), traverse each
- * distinct record path and invoke the appropriate commands for each scenario.
- * <p>
- * Here, we keep track of the record/list type at each step and give this to each command.
+ * Traverse each distinct record path and invoke the appropriate commands for each scenario. Here, we keep track
+ * of the record/list type at each step and give this to each command.
*/
public static void walkArrayPath(ARecordType baseRecordType, List<String> flattenedFieldName,
- List<Integer> flattenedDepthIndicators, TypeTrackerCommandExecutor commandExecutor)
- throws AlgebricksException {
- ArrayPath arrayPath = new ArrayPath(flattenedFieldName, flattenedDepthIndicators).invoke();
+ List<Boolean> unnestFlags, TypeTrackerCommandExecutor commandExecutor) throws AlgebricksException {
+ ArrayPath arrayPath = new ArrayPath(flattenedFieldName, unnestFlags).invoke();
List<List<String>> fieldNamesPerArray = arrayPath.fieldNamesPerArray;
- List<Integer> depthOfArraySteps = arrayPath.depthOfArraySteps;
+ List<Boolean> unnestFlagsPerArray = arrayPath.unnestFlagsPerArray;
// If we are given no base record type, then we do not need to keep track of the record type. We are solely
// using this walk for its flags.
@@ -275,7 +319,7 @@
startingStepRecordType).first;
}
- for (int j = 0; j < depthOfArraySteps.get(i); j++) {
+ if (unnestFlagsPerArray.get(i)) {
if (isTrackingType) {
workingType = TypeComputeUtils.extractListItemType(workingType);
if (workingType == null) {
@@ -284,17 +328,14 @@
}
}
boolean isFirstArrayStep = i == 0;
- boolean isFirstUnnestInStep = j == 0;
- boolean isLastUnnestInIntermediateStep =
- j == depthOfArraySteps.get(i) - 1 && i < fieldNamesPerArray.size() - 1;
+ boolean isLastUnnestInIntermediateStep = i < fieldNamesPerArray.size() - 1;
commandExecutor.executeActionOnEachArrayStep(startingStepRecordType, workingType,
- fieldNamesPerArray.get(i), isFirstArrayStep, isFirstUnnestInStep,
- isLastUnnestInIntermediateStep);
+ fieldNamesPerArray.get(i), isFirstArrayStep, isLastUnnestInIntermediateStep);
}
if (i == fieldNamesPerArray.size() - 1) {
- boolean requiresOnlyOneUnnest = depthOfArraySteps.stream().reduce(0, Integer::sum).equals(1);
- boolean isNonArrayStep = depthOfArraySteps.get(i) == 0;
+ boolean requiresOnlyOneUnnest = fieldNamesPerArray.size() == 1;
+ boolean isNonArrayStep = !unnestFlagsPerArray.get(i);
commandExecutor.executeActionOnFinalArrayStep(startingStepRecordType, fieldNamesPerArray.get(i),
isNonArrayStep, requiresOnlyOneUnnest);
}
@@ -302,28 +343,25 @@
}
/**
- * Given the {@code Index}'s representation of an array path (i.e. a concatenation of record paths, with array
- * steps specified in depths corresponding to an index in the aforementioned record path array), traverse each
- * distinct record path and invoke the appropriate commands for each scenario.
- * <p>
- * Here, we keep track of the total number of actions performed and give this to each command.
+ * Traverse each distinct record path and invoke the appropriate commands for each scenario. Here, we keep track
+ * of the total number of actions performed and give this to each command.
*/
- public static void walkArrayPath(List<String> flattenedFieldName, List<Integer> flattenedDepthIndicators,
+ public static void walkArrayPath(List<String> flattenedFieldName, List<Boolean> unnestFlags,
ActionCounterCommandExecutor commandExecutor) throws AlgebricksException {
- ArrayPath arrayPath = new ArrayPath(flattenedFieldName, flattenedDepthIndicators).invoke();
+ ArrayPath arrayPath = new ArrayPath(flattenedFieldName, unnestFlags).invoke();
List<List<String>> fieldNamesPerArray = arrayPath.fieldNamesPerArray;
- List<Integer> depthOfArraySteps = arrayPath.depthOfArraySteps;
+ List<Boolean> unnestFlagsPerArray = arrayPath.unnestFlagsPerArray;
int numberOfActionsPerformed = 0;
for (int i = 0; i < fieldNamesPerArray.size(); i++) {
- int unnestLevel = depthOfArraySteps.get(i);
+ boolean isUnnestFlagRaised = unnestFlagsPerArray.get(i);
if (i == 0) {
commandExecutor.executeActionOnFirstArrayStep();
numberOfActionsPerformed++;
- unnestLevel--;
+ isUnnestFlagRaised = false;
}
- for (int j = 0; j < unnestLevel; j++) {
+ if (isUnnestFlagRaised) {
commandExecutor.executeActionOnIntermediateArrayStep(numberOfActionsPerformed++);
}
@@ -343,8 +381,8 @@
public interface TypeTrackerCommandExecutor {
void executeActionOnEachArrayStep(ARecordType startingStepRecordType, IAType workingType,
- List<String> fieldName, boolean isFirstArrayStep, boolean isFirstUnnestInStep,
- boolean isLastUnnestInIntermediateStep) throws AlgebricksException;
+ List<String> fieldName, boolean isFirstArrayStep, boolean isLastUnnestInIntermediateStep)
+ throws AlgebricksException;
void executeActionOnFinalArrayStep(ARecordType startingStepRecordType, List<String> fieldName,
boolean isNonArrayStep, boolean requiresOnlyOneUnnest) throws AlgebricksException;
@@ -352,24 +390,24 @@
private static class ArrayPath {
private final List<String> flattenedFieldName;
- private final List<Integer> flattenedDepthIndicators;
+ private final List<Boolean> unnestFlags;
private List<List<String>> fieldNamesPerArray;
- private List<Integer> depthOfArraySteps;
+ private List<Boolean> unnestFlagsPerArray;
- public ArrayPath(List<String> flattenedFieldName, List<Integer> flattenedDepthIndicators) {
+ public ArrayPath(List<String> flattenedFieldName, List<Boolean> unnestFlags) {
this.flattenedFieldName = flattenedFieldName;
- this.flattenedDepthIndicators = flattenedDepthIndicators;
+ this.unnestFlags = unnestFlags;
}
public ArrayPath invoke() {
fieldNamesPerArray = new ArrayList<>();
- depthOfArraySteps = new ArrayList<>();
+ unnestFlagsPerArray = new ArrayList<>();
List<String> workingRecordPath = new ArrayList<>();
- for (int i = 0; i < flattenedDepthIndicators.size(); i++) {
+ for (int i = 0; i < unnestFlags.size(); i++) {
workingRecordPath.add(flattenedFieldName.get(i));
- if (i == flattenedDepthIndicators.size() - 1 || flattenedDepthIndicators.get(i) > 0) {
- depthOfArraySteps.add(flattenedDepthIndicators.get(i));
+ if (i == unnestFlags.size() - 1 || unnestFlags.get(i)) {
+ unnestFlagsPerArray.add(unnestFlags.get(i));
fieldNamesPerArray.add(workingRecordPath);
workingRecordPath = new ArrayList<>();
}
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtil.java
index b2026d7..6a0c44f 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtil.java
@@ -153,9 +153,7 @@
ARecordType sourceType =
(e.getSourceIndicator() == Index.RECORD_INDICATOR) ? recordType : metaRecordType;
Pair<IAType, Boolean> keyPairType = ArrayIndexUtil.getNonNullableOpenFieldType(e.getTypeList().get(i),
- ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), e.getProjectList().get(i)),
- sourceType,
- ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), e.getProjectList().get(i)));
+ e.getUnnestList(), e.getProjectList().get(i), sourceType);
indexKeyTypes.add(keyPairType.first);
}
}
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SecondaryArrayIndexBTreeOperationsHelper.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SecondaryArrayIndexBTreeOperationsHelper.java
index fdb21a2..c1f8c7b 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SecondaryArrayIndexBTreeOperationsHelper.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SecondaryArrayIndexBTreeOperationsHelper.java
@@ -57,7 +57,6 @@
import org.apache.hyracks.algebricks.runtime.base.IPushRuntimeFactory;
import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
import org.apache.hyracks.algebricks.runtime.base.IUnnestingEvaluatorFactory;
-import org.apache.hyracks.algebricks.runtime.evaluators.ColumnAccessEvalFactory;
import org.apache.hyracks.algebricks.runtime.operators.aggreg.SimpleAlgebricksAccumulatingAggregatorFactory;
import org.apache.hyracks.algebricks.runtime.operators.base.SinkRuntimeFactory;
import org.apache.hyracks.algebricks.runtime.operators.meta.AlgebricksMetaOperatorDescriptor;
@@ -78,14 +77,13 @@
public class SecondaryArrayIndexBTreeOperationsHelper extends SecondaryTreeIndexOperationsHelper {
private final int numAtomicSecondaryKeys, numArraySecondaryKeys, numTotalSecondaryKeys;
- private final Index.ArrayIndexDetails arrayIndexDetails;
private final EvalFactoryAndRecDescStackBuilder evalFactoryAndRecDescStackBuilder =
new EvalFactoryAndRecDescStackBuilder();
- // TODO (GLENN): Phase these out and use the UNNEST / PROJECT scheme instead.
+ private final Index.ArrayIndexDetails arrayIndexDetails;
private final List<List<String>> flattenedFieldNames;
private final List<IAType> flattenedKeyTypes;
- private final List<List<Integer>> depthIndicators;
+ private final List<List<Boolean>> unnestFlags;
protected SecondaryArrayIndexBTreeOperationsHelper(Dataset dataset, Index index, MetadataProvider metadataProvider,
SourceLocation sourceLoc) throws AlgebricksException {
@@ -94,19 +92,19 @@
flattenedFieldNames = new ArrayList<>();
flattenedKeyTypes = new ArrayList<>();
- depthIndicators = new ArrayList<>();
+ unnestFlags = new ArrayList<>();
for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
if (e.getUnnestList().isEmpty()) {
flattenedFieldNames.add(e.getProjectList().get(0));
flattenedKeyTypes.add(e.getTypeList().get(0));
- depthIndicators
- .add(ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), e.getProjectList().get(0)));
+ unnestFlags.add(ArrayIndexUtil.getUnnestFlags(e.getUnnestList(), e.getProjectList().get(0)));
+
} else {
for (int i = 0; i < e.getProjectList().size(); i++) {
List<String> project = e.getProjectList().get(i);
flattenedFieldNames.add(ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), project));
- depthIndicators.add(ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), project));
flattenedKeyTypes.add(e.getTypeList().get(i));
+ unnestFlags.add(ArrayIndexUtil.getUnnestFlags(e.getUnnestList(), project));
}
}
}
@@ -127,7 +125,7 @@
numArraySecondaryKeys = numTotalSecondaryKeys - numAtomicSecondaryKeys;
}
- private int findPosOfArrayIndex() throws AsterixException {
+ private int findPosOfArrayIndexElement() throws AsterixException {
for (int i = 0; i < arrayIndexDetails.getElementList().size(); i++) {
if (!arrayIndexDetails.getElementList().get(i).getUnnestList().isEmpty()) {
return i;
@@ -163,9 +161,7 @@
ARecordType sourceType = (e.getSourceIndicator() == 0) ? itemType : metaType;
addSKEvalFactories(isOverridingKeyFieldTypes ? enforcedItemType : sourceType, flattenedListPos, false);
Pair<IAType, Boolean> keyTypePair = ArrayIndexUtil.getNonNullableOpenFieldType(e.getTypeList().get(i),
- ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), e.getProjectList().get(i)),
- sourceType,
- ArrayIndexUtil.getArrayDepthIndicator(e.getUnnestList(), e.getProjectList().get(i)));
+ e.getUnnestList(), e.getProjectList().get(i), sourceType);
IAType keyType = keyTypePair.first;
anySecondaryKeyIsNullable = anySecondaryKeyIsNullable || keyTypePair.second;
ISerializerDeserializer keySerde = serdeProvider.getSerializerDeserializer(keyType);
@@ -242,14 +238,15 @@
return;
}
- List<Integer> arrayDepthIndicators = depthIndicators.get(fieldPos);
- List<String> fieldNames = flattenedFieldNames.get(fieldPos);
- if (arrayDepthIndicators.stream().noneMatch(b -> b > 0)) {
+ List<String> flattenedFieldName = flattenedFieldNames.get(fieldPos);
+ List<Boolean> workingUnnestFlags = unnestFlags.get(fieldPos);
+ if (workingUnnestFlags.stream().noneMatch(b -> b)) {
addAtomicFieldToBuilder(recordType, fieldPos);
+
} else {
EvalFactoryAndRecDescInvoker commandExecutor =
new EvalFactoryAndRecDescInvoker(!evalFactoryAndRecDescStackBuilder.isUnnestEvalPopulated());
- ArrayIndexUtil.walkArrayPath(recordType, fieldNames, arrayDepthIndicators, commandExecutor);
+ ArrayIndexUtil.walkArrayPath(recordType, flattenedFieldName, workingUnnestFlags, commandExecutor);
}
}
@@ -278,13 +275,12 @@
sourceOp = targetOp;
}
- // TODO (GLENN): Refactor to use UNNEST + PROJECT scheme.
// Perform the unnest work.
final Mutable<IOperatorDescriptor> sourceOpRef = new MutableObject<>(sourceOp);
final Mutable<IOperatorDescriptor> targetOpRef = new MutableObject<>(targetOp);
LoadingJobBuilder jobBuilder = new LoadingJobBuilder(spec, sourceOpRef, targetOpRef);
- int posOfArrayIndex = findPosOfArrayIndex();
- ArrayIndexUtil.walkArrayPath(flattenedFieldNames.get(posOfArrayIndex), depthIndicators.get(posOfArrayIndex),
+ int posOfArrayElement = findPosOfArrayIndexElement();
+ ArrayIndexUtil.walkArrayPath(flattenedFieldNames.get(posOfArrayElement), unnestFlags.get(posOfArrayElement),
jobBuilder);
sourceOp = sourceOpRef.getValue();
@@ -450,8 +446,7 @@
: inputWidth + numTotalSecondaryKeys + numFilterFields).toArray();
for (int i = 0; i < numTotalSecondaryKeys; i++) {
int sizeOfFieldNamesForI = flattenedFieldNames.get(i).size();
- if (depthIndicators.get(i).get(sizeOfFieldNamesForI - 1) != 0
- && (depthIndicators.get(i).stream().anyMatch(b -> b > 0))) {
+ if (unnestFlags.get(i).get(sizeOfFieldNamesForI - 1)) {
projectionList[i] = numPrimaryKeys + 1;
} else {
projectionList[i] = outColumns[outColumnsCursor++];
@@ -469,9 +464,9 @@
outColumns = IntStream.range(inputWidth, inputWidth + numArraySecondaryKeys).toArray();
for (int i = 0; i < numTotalSecondaryKeys; i++) {
int sizeOfFieldNamesForI = flattenedFieldNames.get(i).size();
- if (depthIndicators.get(i).stream().noneMatch(b -> b > 0)) {
+ if (unnestFlags.get(i).stream().noneMatch(b -> b)) {
projectionList[i] = numPrimaryKeys + atomicSKCursor++;
- } else if (depthIndicators.get(i).get(sizeOfFieldNamesForI - 1) == 0) {
+ } else if (!unnestFlags.get(i).get(sizeOfFieldNamesForI - 1)) {
projectionList[i] = outColumns[arraySKCursor++];
} else {
projectionList[i] = numPrimaryKeys + numAtomicSecondaryKeys + numFilterFields + 1;
@@ -525,25 +520,20 @@
@Override
public void executeActionOnEachArrayStep(ARecordType startingStepRecordType, IAType workingType,
- List<String> fieldName, boolean isFirstArrayStep, boolean isFirstUnnestInStep,
- boolean isLastUnnestInIntermediateStep) throws AlgebricksException {
+ List<String> fieldName, boolean isFirstArrayStep, boolean isLastUnnestInIntermediateStep)
+ throws AlgebricksException {
if (!this.isFirstWalk) {
// We have already added the appropriate UNNESTs.
return;
}
int sourceColumnForNestedArrays = numPrimaryKeys + numAtomicSecondaryKeys + numFilterFields;
- if (isFirstUnnestInStep) {
- int sourceColumnForFirstUnnestInAtomicPath =
- isFirstArrayStep ? numPrimaryKeys : sourceColumnForNestedArrays;
- IScalarEvaluatorFactory sef = metadataProvider.getDataFormat().getFieldAccessEvaluatorFactory(
- metadataProvider.getFunctionManager(), startingStepRecordType, fieldName,
- sourceColumnForFirstUnnestInAtomicPath, sourceLoc);
- evalFactoryAndRecDescStackBuilder.addUnnest(sef, workingType);
- } else {
- IScalarEvaluatorFactory sef = new ColumnAccessEvalFactory(sourceColumnForNestedArrays);
- evalFactoryAndRecDescStackBuilder.addUnnest(sef, workingType);
- }
+ int sourceColumnForFirstUnnestInAtomicPath =
+ isFirstArrayStep ? numPrimaryKeys : sourceColumnForNestedArrays;
+ IScalarEvaluatorFactory sef = metadataProvider.getDataFormat().getFieldAccessEvaluatorFactory(
+ metadataProvider.getFunctionManager(), startingStepRecordType, fieldName,
+ sourceColumnForFirstUnnestInAtomicPath, sourceLoc);
+ evalFactoryAndRecDescStackBuilder.addUnnest(sef, workingType);
}
@Override
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
index dd303fc..b1d610f 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
@@ -61,8 +61,8 @@
}
private static class EnforcedTypeBuilder {
- private final Deque<Triple<IAType, String, Integer>> typeStack = new ArrayDeque<>();
- private List<Integer> keyDepthIndicators;
+ private final Deque<Triple<IAType, String, Boolean>> typeStack = new ArrayDeque<>();
+ private List<Boolean> keyUnnestFlags;
private List<String> keyFieldNames;
private ARecordType baseRecordType;
private IAType keyFieldType;
@@ -72,11 +72,11 @@
private IAType endOfOpenTypeBuild;
private int indexOfOpenPart;
- public void reset(ARecordType baseRecordType, List<String> keyFieldNames, List<Integer> keyDepthIndicators,
+ public void reset(ARecordType baseRecordType, List<String> keyFieldNames, List<Boolean> keyUnnestFlags,
IAType keyFieldType) {
this.baseRecordType = baseRecordType;
this.keyFieldNames = keyFieldNames;
- this.keyDepthIndicators = keyDepthIndicators;
+ this.keyUnnestFlags = keyUnnestFlags;
this.keyFieldType = keyFieldType;
}
@@ -90,21 +90,19 @@
IAType typeIntermediate = baseRecordType;
List<String> subFieldName = new ArrayList<>();
for (int i = 0; i < keyFieldNames.size() - 1; i++) {
- typeStack.push(new Triple<>(typeIntermediate, keyFieldNames.get(i),
- (i == 0) ? 0 : keyDepthIndicators.get(i - 1)));
+ typeStack.push(
+ new Triple<>(typeIntermediate, keyFieldNames.get(i), i != 0 && keyUnnestFlags.get(i - 1)));
bridgeNameFoundFromOpenTypeBuild = typeIntermediate.getTypeName();
- if (i == 0 || keyDepthIndicators.get(i - 1) == 0) {
+ if (i == 0 || !keyUnnestFlags.get(i - 1)) {
subFieldName.add(keyFieldNames.get(i));
} else {
- // We have a multi-valued intermediate. Traverse the array first, then add our field name.
- for (int j = 0; j < keyDepthIndicators.get(i - 1); j++) {
- typeIntermediate = TypeComputeUtils.extractListItemType(typeIntermediate);
- if (typeIntermediate == null) {
- String fName = String.join(".", subFieldName);
- throw new AsterixException(ErrorCode.COMPILATION_ERROR,
- "Wrong level of array nesting for field: " + fName);
- }
+ // We have a multi-valued intermediate. Perform our UNNEST then add our field name.
+ typeIntermediate = TypeComputeUtils.extractListItemType(typeIntermediate);
+ if (typeIntermediate == null) {
+ String fName = String.join(".", subFieldName);
+ throw new AsterixException(ErrorCode.COMPILATION_ERROR,
+ "No list item type found. Wrong type given from field " + fName);
}
subFieldName.add(keyFieldNames.get(i));
}
@@ -133,27 +131,27 @@
}
private IAType buildNewForOpenType() {
- int depthOfOpenType = keyDepthIndicators.subList(indexOfOpenPart + 1, keyDepthIndicators.size()).stream()
- .filter(i -> i != 0).findFirst().orElse(0);
- IAType resultant = nestArrayType(keyFieldType, depthOfOpenType);
+ boolean isTypeWithUnnest = keyUnnestFlags.subList(indexOfOpenPart + 1, keyUnnestFlags.size()).stream()
+ .filter(i -> i).findFirst().orElse(false);
+ IAType resultant = nestArrayType(keyFieldType, isTypeWithUnnest);
// Build the type (list or record) that holds the type (list or record) above.
resultant = nestArrayType(
new ARecordType(keyFieldNames.get(keyFieldNames.size() - 2),
new String[] { keyFieldNames.get(keyFieldNames.size() - 1) },
new IAType[] { AUnionType.createUnknownableType(resultant) }, true),
- keyDepthIndicators.get(indexOfOpenPart));
+ keyUnnestFlags.get(indexOfOpenPart));
// Create open part of the nested field.
for (int i = keyFieldNames.size() - 3; i > (indexOfOpenPart - 1); i--) {
resultant = nestArrayType(
new ARecordType(keyFieldNames.get(i), new String[] { keyFieldNames.get(i + 1) },
new IAType[] { AUnionType.createUnknownableType(resultant) }, true),
- keyDepthIndicators.get(i));
+ keyUnnestFlags.get(i));
}
// Now update the parent to include this optional field, accounting for intermediate arrays.
- Triple<IAType, String, Integer> gapTriple = this.typeStack.pop();
+ Triple<IAType, String, Boolean> gapTriple = this.typeStack.pop();
ARecordType parentRecord =
(ARecordType) unnestArrayType(TypeComputeUtils.getActualType(gapTriple.first), gapTriple.third);
IAType[] parentFieldTypes = ArrayUtils.addAll(parentRecord.getFieldTypes().clone(),
@@ -168,9 +166,9 @@
private IAType buildNewForFullyClosedType() throws AsterixException {
// The schema is closed all the way to the field itself.
IAType typeIntermediate = TypeComputeUtils.getActualType(endOfOpenTypeBuild);
- int depthOfOpenType = (indexOfOpenPart == 0) ? 0 : keyDepthIndicators.get(indexOfOpenPart - 1);
- int depthOfKeyType = keyDepthIndicators.get(indexOfOpenPart);
- ARecordType lastNestedRecord = (ARecordType) unnestArrayType(typeIntermediate, depthOfOpenType);
+ boolean isOpenTypeWithUnnest = indexOfOpenPart != 0 && keyUnnestFlags.get(indexOfOpenPart - 1);
+ boolean isKeyTypeWithUnnest = keyUnnestFlags.get(indexOfOpenPart);
+ ARecordType lastNestedRecord = (ARecordType) unnestArrayType(typeIntermediate, isOpenTypeWithUnnest);
Map<String, IAType> recordNameTypesMap = createRecordNameTypeMap(lastNestedRecord);
// If an enforced field already exists, verify that the type is correct.
@@ -186,21 +184,21 @@
}
if (enforcedFieldType == null) {
recordNameTypesMap.put(keyFieldNames.get(keyFieldNames.size() - 1),
- AUnionType.createUnknownableType(nestArrayType(keyFieldType, depthOfKeyType)));
+ AUnionType.createUnknownableType(nestArrayType(keyFieldType, isKeyTypeWithUnnest)));
}
// Build the nested record, and account for the wrapping array.
IAType resultant = nestArrayType(
new ARecordType(lastNestedRecord.getTypeName(), recordNameTypesMap.keySet().toArray(new String[0]),
recordNameTypesMap.values().toArray(new IAType[0]), lastNestedRecord.isOpen()),
- depthOfOpenType);
+ isOpenTypeWithUnnest);
return keepUnknown(endOfOpenTypeBuild, resultant);
}
private ARecordType buildRestOfRecord(IAType newTypeToAdd) {
IAType resultant = TypeComputeUtils.getActualType(newTypeToAdd);
while (!typeStack.isEmpty()) {
- Triple<IAType, String, Integer> typeFromStack = typeStack.pop();
+ Triple<IAType, String, Boolean> typeFromStack = typeStack.pop();
IAType typeIntermediate = unnestArrayType(typeFromStack.first, typeFromStack.third);
ARecordType recordType = (ARecordType) typeIntermediate;
IAType[] fieldTypes = recordType.getFieldTypes().clone();
@@ -228,18 +226,13 @@
return updatedRecordType;
}
- private static IAType nestArrayType(IAType originalType, int depthOfArrays) {
- IAType resultant = originalType;
- for (int i = 0; i < depthOfArrays; i++) {
- resultant =
- new AOrderedListType(resultant, (i == depthOfArrays - 1) ? originalType.getTypeName() : null);
- }
- return resultant;
+ private static IAType nestArrayType(IAType originalType, boolean isWithinArray) {
+ return (isWithinArray) ? new AOrderedListType(originalType, originalType.getTypeName()) : originalType;
}
- private static IAType unnestArrayType(IAType originalType, int depthOfArrays) {
+ private static IAType unnestArrayType(IAType originalType, boolean isWithinArray) {
IAType resultant = originalType;
- for (int i = 0; i < depthOfArrays; i++) {
+ if (isWithinArray) {
resultant = TypeComputeUtils.extractListItemType(resultant);
if (resultant != null) {
resultant = TypeComputeUtils.getActualType(resultant);
@@ -299,7 +292,7 @@
"Indexing an open field is only supported on the record part");
}
enforcedTypeBuilder.reset(enforcedRecordType, keyFieldNames.get(i),
- Collections.nCopies(keyFieldNames.get(i).size(), 0), keyFieldTypes.get(i));
+ Collections.nCopies(keyFieldNames.get(i).size(), false), keyFieldTypes.get(i));
validateRecord(enforcedRecordType);
enforcedRecordType = enforcedTypeBuilder.build();
}
@@ -319,7 +312,7 @@
"Indexing an open field is only supported on the record part");
}
enforcedTypeBuilder.reset(enforcedRecordType, keyFieldNames.get(i),
- Collections.nCopies(keyFieldNames.get(i).size(), 0), keyFieldTypes.get(i));
+ Collections.nCopies(keyFieldNames.get(i).size(), false), keyFieldTypes.get(i));
validateRecord(enforcedRecordType);
enforcedRecordType = enforcedTypeBuilder.build();
}
@@ -342,7 +335,7 @@
List<String> project = projectList.get(i);
enforcedTypeBuilder.reset(enforcedRecordType,
ArrayIndexUtil.getFlattenedKeyFieldNames(unnestList, project),
- ArrayIndexUtil.getArrayDepthIndicator(unnestList, project), typeList.get(i));
+ ArrayIndexUtil.getUnnestFlags(unnestList, project), typeList.get(i));
validateRecord(enforcedRecordType);
enforcedRecordType = enforcedTypeBuilder.build();
}