| /* |
| * 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.translator; |
| |
| import static org.apache.asterix.om.types.BuiltinType.ANY; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.asterix.common.annotations.IRecordFieldDataGen; |
| import org.apache.asterix.common.annotations.RecordDataGenAnnotation; |
| import org.apache.asterix.common.exceptions.CompilationException; |
| import org.apache.asterix.common.exceptions.ErrorCode; |
| import org.apache.asterix.common.metadata.DataverseName; |
| import org.apache.asterix.common.metadata.MetadataConstants; |
| import org.apache.asterix.common.metadata.Namespace; |
| import org.apache.asterix.lang.common.expression.OrderedListTypeDefinition; |
| import org.apache.asterix.lang.common.expression.RecordTypeDefinition; |
| import org.apache.asterix.lang.common.expression.RecordTypeDefinition.RecordKind; |
| import org.apache.asterix.lang.common.expression.TypeExpression; |
| import org.apache.asterix.lang.common.expression.TypeReferenceExpression; |
| import org.apache.asterix.lang.common.expression.UnorderedListTypeDefinition; |
| import org.apache.asterix.lang.common.struct.Identifier; |
| import org.apache.asterix.metadata.MetadataManager; |
| import org.apache.asterix.metadata.MetadataTransactionContext; |
| import org.apache.asterix.metadata.entities.Datatype; |
| import org.apache.asterix.metadata.utils.TypeUtil; |
| import org.apache.asterix.om.types.AOrderedListType; |
| import org.apache.asterix.om.types.ARecordType; |
| import org.apache.asterix.om.types.AUnionType; |
| import org.apache.asterix.om.types.AUnorderedListType; |
| import org.apache.asterix.om.types.AbstractCollectionType; |
| import org.apache.asterix.om.types.AbstractComplexType; |
| import org.apache.asterix.om.types.BuiltinTypeMap; |
| import org.apache.asterix.om.types.IAType; |
| import org.apache.asterix.om.types.TypeSignature; |
| import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; |
| import org.apache.hyracks.algebricks.common.utils.Pair; |
| import org.apache.hyracks.api.exceptions.SourceLocation; |
| import org.apache.hyracks.util.LogRedactionUtil; |
| |
| public class TypeTranslator { |
| |
| private TypeTranslator() { |
| } |
| |
| public static Map<TypeSignature, IAType> computeTypes(String typeDatabase, DataverseName typeDataverse, |
| String typeName, TypeExpression typeExpr, String defaultDatabase, DataverseName defaultDataverse, |
| MetadataTransactionContext mdTxnCtx) throws AlgebricksException { |
| Map<TypeSignature, IAType> typeMap = new HashMap<>(); |
| computeTypes(typeDatabase, typeDataverse, typeName, typeExpr, defaultDatabase, defaultDataverse, mdTxnCtx, |
| typeMap); |
| return typeMap; |
| } |
| |
| public static void computeTypes(String typeDatabase, DataverseName typeDataverse, String typeName, |
| TypeExpression typeExpr, String defaultDatabase, DataverseName defaultDataverse, |
| MetadataTransactionContext mdTxnCtx, Map<TypeSignature, IAType> outTypeMap) throws AlgebricksException { |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes = new HashMap<>(); |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes = new HashMap<>(); |
| Map<TypeSignature, List<TypeSignature>> incompleteTopLevelTypeReferences = new HashMap<>(); |
| firstPass(typeDatabase, typeDataverse, typeName, typeExpr, outTypeMap, incompleteFieldTypes, |
| incompleteItemTypes, incompleteTopLevelTypeReferences, typeDatabase, typeDataverse); |
| secondPass(mdTxnCtx, outTypeMap, incompleteFieldTypes, incompleteItemTypes, incompleteTopLevelTypeReferences, |
| typeDatabase, typeDataverse, typeExpr.getSourceLocation()); |
| |
| for (IAType type : outTypeMap.values()) { |
| if (type.getTypeTag().isDerivedType()) { |
| ((AbstractComplexType) type).generateNestedDerivedTypeNames(); |
| } |
| } |
| } |
| |
| private static void firstPass(String typeDatabase, DataverseName typeDataverse, String typeName, |
| TypeExpression typeExpr, Map<TypeSignature, IAType> outTypeMap, |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| Map<TypeSignature, List<TypeSignature>> incompleteTopLevelTypeReferences, String defaultDatabase, |
| DataverseName defaultDataverse) throws AlgebricksException { |
| |
| if (BuiltinTypeMap.getBuiltinType(typeName) != null) { |
| throw new CompilationException(ErrorCode.COMPILATION_ERROR, typeExpr.getSourceLocation(), |
| "Cannot redefine builtin type " + typeName); |
| } |
| TypeSignature typeSignature = new TypeSignature(typeDatabase, typeDataverse, typeName); |
| switch (typeExpr.getTypeKind()) { |
| case TYPEREFERENCE: { |
| TypeReferenceExpression tre = (TypeReferenceExpression) typeExpr; |
| TypeSignature treSignature = createTypeSignature(tre, defaultDatabase, defaultDataverse); |
| IAType t = solveTypeReference(treSignature, outTypeMap); |
| if (t != null) { |
| outTypeMap.put(typeSignature, t); |
| } else { |
| addIncompleteTopLevelTypeReference(typeSignature, treSignature, incompleteTopLevelTypeReferences); |
| } |
| break; |
| } |
| case RECORD: { |
| RecordTypeDefinition rtd = (RecordTypeDefinition) typeExpr; |
| ARecordType recType = computeRecordType(typeSignature, rtd, outTypeMap, incompleteFieldTypes, |
| incompleteItemTypes, typeDatabase, typeDataverse); |
| outTypeMap.put(typeSignature, recType); |
| break; |
| } |
| case ORDEREDLIST: { |
| OrderedListTypeDefinition oltd = (OrderedListTypeDefinition) typeExpr; |
| AOrderedListType olType = computeOrderedListType(typeSignature, oltd, outTypeMap, incompleteItemTypes, |
| incompleteFieldTypes, typeDatabase, typeDataverse); |
| outTypeMap.put(typeSignature, olType); |
| break; |
| } |
| case UNORDEREDLIST: { |
| UnorderedListTypeDefinition ultd = (UnorderedListTypeDefinition) typeExpr; |
| AUnorderedListType ulType = computeUnorderedListType(typeSignature, ultd, outTypeMap, |
| incompleteItemTypes, incompleteFieldTypes, typeDatabase, typeDataverse); |
| outTypeMap.put(typeSignature, ulType); |
| break; |
| } |
| default: { |
| throw new IllegalStateException(); |
| } |
| } |
| } |
| |
| private static void secondPass(MetadataTransactionContext mdTxnCtx, Map<TypeSignature, IAType> typeMap, |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| Map<TypeSignature, List<TypeSignature>> incompleteTopLevelTypeReferences, String typeDatabase, |
| DataverseName typeDataverse, SourceLocation sourceLoc) throws AlgebricksException { |
| // solve remaining top level references |
| for (TypeSignature typeSignature : incompleteTopLevelTypeReferences.keySet()) { |
| IAType t; |
| Datatype dt = MetadataManager.INSTANCE.getDatatype(mdTxnCtx, typeSignature.getDatabaseName(), |
| typeSignature.getDataverseName(), typeSignature.getName()); |
| if (dt == null) { |
| throw new CompilationException(ErrorCode.UNKNOWN_TYPE, sourceLoc, typeSignature.getName()); |
| } else { |
| t = dt.getDatatype(); |
| } |
| for (TypeSignature sign : incompleteTopLevelTypeReferences.get(typeSignature)) { |
| typeMap.put(sign, t); |
| } |
| } |
| // solve remaining field type references |
| for (String trefName : incompleteFieldTypes.keySet()) { |
| IAType t; |
| Datatype dt = MetadataManager.INSTANCE.getDatatype(mdTxnCtx, typeDatabase, typeDataverse, trefName); |
| if (dt == null) { |
| dt = MetadataManager.INSTANCE.getDatatype(mdTxnCtx, MetadataConstants.SYSTEM_DATABASE, |
| MetadataConstants.METADATA_DATAVERSE_NAME, trefName); |
| } |
| if (dt == null) { |
| throw new CompilationException(ErrorCode.UNKNOWN_TYPE, sourceLoc, trefName); |
| } else { |
| t = dt.getDatatype(); |
| } |
| Map<ARecordType, List<Integer>> fieldsToFix = incompleteFieldTypes.get(trefName); |
| for (ARecordType recType : fieldsToFix.keySet()) { |
| List<Integer> positions = fieldsToFix.get(recType); |
| IAType[] fldTypes = recType.getFieldTypes(); |
| for (Integer pos : positions) { |
| if (fldTypes[pos] == null) { |
| fldTypes[pos] = t; |
| } else { // nullable |
| AUnionType nullableUnion = (AUnionType) fldTypes[pos]; |
| nullableUnion.setActualType(t); |
| } |
| } |
| } |
| } |
| |
| // solve remaining item type references |
| for (TypeSignature typeSignature : incompleteItemTypes.keySet()) { |
| IAType t; |
| Datatype dt; |
| if (MetadataManager.INSTANCE != null) { |
| dt = MetadataManager.INSTANCE.getDatatype(mdTxnCtx, typeSignature.getDatabaseName(), |
| typeSignature.getDataverseName(), typeSignature.getName()); |
| if (dt == null) { |
| throw new CompilationException(ErrorCode.UNKNOWN_TYPE, sourceLoc, typeSignature.getName()); |
| } |
| t = dt.getDatatype(); |
| } else { |
| t = typeMap.get(typeSignature); |
| } |
| for (AbstractCollectionType act : incompleteItemTypes.get(typeSignature)) { |
| act.setItemType(t); |
| } |
| } |
| } |
| |
| private static AOrderedListType computeOrderedListType(TypeSignature typeSignature, OrderedListTypeDefinition oltd, |
| Map<TypeSignature, IAType> typeMap, Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, String defaultDatabase, |
| DataverseName defaultDataverse) throws AlgebricksException { |
| TypeExpression tExpr = oltd.getItemTypeExpression(); |
| String typeName = typeSignature != null ? typeSignature.getName() : null; |
| AOrderedListType aolt = new AOrderedListType(ANY, typeName); |
| setCollectionItemType(tExpr, typeMap, incompleteItemTypes, incompleteFieldTypes, aolt, defaultDatabase, |
| defaultDataverse); |
| return aolt; |
| } |
| |
| private static AUnorderedListType computeUnorderedListType(TypeSignature typeSignature, |
| UnorderedListTypeDefinition ultd, Map<TypeSignature, IAType> typeMap, |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, String defaultDatabase, |
| DataverseName defaultDataverse) throws AlgebricksException { |
| TypeExpression tExpr = ultd.getItemTypeExpression(); |
| String typeName = typeSignature != null ? typeSignature.getName() : null; |
| AUnorderedListType ault = new AUnorderedListType(ANY, typeName); |
| setCollectionItemType(tExpr, typeMap, incompleteItemTypes, incompleteFieldTypes, ault, defaultDatabase, |
| defaultDataverse); |
| return ault; |
| } |
| |
| private static void setCollectionItemType(TypeExpression tExpr, Map<TypeSignature, IAType> typeMap, |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, AbstractCollectionType act, |
| String defaultDatabase, DataverseName defaultDataverse) throws AlgebricksException { |
| switch (tExpr.getTypeKind()) { |
| case ORDEREDLIST: { |
| OrderedListTypeDefinition oltd = (OrderedListTypeDefinition) tExpr; |
| IAType t = computeOrderedListType(null, oltd, typeMap, incompleteItemTypes, incompleteFieldTypes, |
| defaultDatabase, defaultDataverse); |
| act.setItemType(t); |
| break; |
| } |
| case UNORDEREDLIST: { |
| UnorderedListTypeDefinition ultd = (UnorderedListTypeDefinition) tExpr; |
| IAType t = computeUnorderedListType(null, ultd, typeMap, incompleteItemTypes, incompleteFieldTypes, |
| defaultDatabase, defaultDataverse); |
| act.setItemType(t); |
| break; |
| } |
| case RECORD: { |
| RecordTypeDefinition rtd = (RecordTypeDefinition) tExpr; |
| IAType t = computeRecordType(null, rtd, typeMap, incompleteFieldTypes, incompleteItemTypes, |
| defaultDatabase, defaultDataverse); |
| act.setItemType(t); |
| break; |
| } |
| case TYPEREFERENCE: { |
| TypeReferenceExpression tre = (TypeReferenceExpression) tExpr; |
| TypeSignature treSignature = createTypeSignature(tre, defaultDatabase, defaultDataverse); |
| IAType tref = solveTypeReference(treSignature, typeMap); |
| if (tref != null) { |
| act.setItemType(tref); |
| } else { |
| addIncompleteCollectionTypeReference(act, tre, incompleteItemTypes, defaultDatabase, |
| defaultDataverse); |
| } |
| break; |
| } |
| default: { |
| throw new IllegalStateException(); |
| } |
| } |
| } |
| |
| private static void addIncompleteCollectionTypeReference(AbstractCollectionType collType, |
| TypeReferenceExpression tre, Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, |
| String defaultDatabase, DataverseName defaultDataverse) { |
| TypeSignature typeSignature = createTypeSignature(tre, defaultDatabase, defaultDataverse); |
| List<AbstractCollectionType> typeList = |
| incompleteItemTypes.computeIfAbsent(typeSignature, k -> new ArrayList<>()); |
| typeList.add(collType); |
| } |
| |
| private static void addIncompleteFieldTypeReference(ARecordType recType, int fldPosition, |
| TypeReferenceExpression tre, Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes) { |
| String typeName = tre.getIdent().second.getValue(); |
| Map<ARecordType, List<Integer>> refMap = incompleteFieldTypes.computeIfAbsent(typeName, k -> new HashMap<>()); |
| List<Integer> typeList = refMap.computeIfAbsent(recType, k -> new ArrayList<>()); |
| typeList.add(fldPosition); |
| } |
| |
| private static void addIncompleteTopLevelTypeReference(TypeSignature typeSignature, TypeSignature treSignature, |
| Map<TypeSignature, List<TypeSignature>> incompleteTopLevelTypeReferences) { |
| List<TypeSignature> refList = |
| incompleteTopLevelTypeReferences.computeIfAbsent(treSignature, k -> new ArrayList<>()); |
| refList.add(typeSignature); |
| } |
| |
| private static IAType solveTypeReference(TypeSignature typeSignature, Map<TypeSignature, IAType> typeMap) { |
| IAType builtin = BuiltinTypeMap.getBuiltinType(typeSignature.getName()); |
| if (builtin != null) { |
| return builtin; |
| } else { |
| return typeMap.get(typeSignature); |
| } |
| } |
| |
| private static ARecordType computeRecordType(TypeSignature typeSignature, RecordTypeDefinition rtd, |
| Map<TypeSignature, IAType> typeMap, Map<String, Map<ARecordType, List<Integer>>> incompleteFieldTypes, |
| Map<TypeSignature, List<AbstractCollectionType>> incompleteItemTypes, String defaultDatabase, |
| DataverseName defaultDataverse) throws AlgebricksException { |
| List<String> names = rtd.getFieldNames(); |
| int n = names.size(); |
| String[] fldNames = new String[n]; |
| IAType[] fldTypes = new IAType[n]; |
| int i = 0; |
| for (String s : names) { |
| if (names.indexOf(s) < i) { |
| throw new CompilationException(ErrorCode.DUPLICATE_FIELD_NAME, rtd.getSourceLocation(), |
| LogRedactionUtil.userData(s)); |
| } |
| fldNames[i++] = s; |
| } |
| boolean isOpen = rtd.getRecordKind() == RecordKind.OPEN; |
| ARecordType recType = |
| new ARecordType(typeSignature == null ? null : typeSignature.getName(), fldNames, fldTypes, isOpen); |
| List<IRecordFieldDataGen> fieldDataGen = rtd.getFieldDataGen(); |
| if (fieldDataGen.size() == n) { |
| IRecordFieldDataGen[] rfdg = new IRecordFieldDataGen[n]; |
| rfdg = fieldDataGen.toArray(rfdg); |
| recType.getAnnotations().add(new RecordDataGenAnnotation(rfdg, rtd.getUndeclaredFieldsDataGen())); |
| } |
| |
| for (int j = 0; j < n; j++) { |
| TypeExpression texpr = rtd.getFieldTypes().get(j); |
| IAType type; |
| switch (texpr.getTypeKind()) { |
| case TYPEREFERENCE: { |
| TypeReferenceExpression tre = (TypeReferenceExpression) texpr; |
| TypeSignature signature = createTypeSignature(tre, defaultDatabase, defaultDataverse); |
| type = solveTypeReference(signature, typeMap); |
| if (type == null) { |
| addIncompleteFieldTypeReference(recType, j, tre, incompleteFieldTypes); |
| } |
| break; |
| } |
| case RECORD: { |
| RecordTypeDefinition recTypeDef2 = (RecordTypeDefinition) texpr; |
| type = computeRecordType(null, recTypeDef2, typeMap, incompleteFieldTypes, incompleteItemTypes, |
| defaultDatabase, defaultDataverse); |
| break; |
| } |
| case ORDEREDLIST: { |
| OrderedListTypeDefinition oltd = (OrderedListTypeDefinition) texpr; |
| type = computeOrderedListType(null, oltd, typeMap, incompleteItemTypes, incompleteFieldTypes, |
| defaultDatabase, defaultDataverse); |
| break; |
| } |
| case UNORDEREDLIST: { |
| UnorderedListTypeDefinition ultd = (UnorderedListTypeDefinition) texpr; |
| type = computeUnorderedListType(null, ultd, typeMap, incompleteItemTypes, incompleteFieldTypes, |
| defaultDatabase, defaultDataverse); |
| break; |
| } |
| default: { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| Boolean nullable = rtd.getNullableFields().get(j); |
| Boolean missable = rtd.getMissableFields().get(j); |
| fldTypes[j] = TypeUtil.createQuantifiedType(type, nullable, missable); |
| } |
| |
| return recType; |
| } |
| |
| private static TypeSignature createTypeSignature(TypeReferenceExpression tre, String defaultDatabase, |
| DataverseName defaultDataverse) { |
| Pair<Namespace, Identifier> treTypeName = tre.getIdent(); |
| DataverseName activeDataverse; |
| String activeDatabase; |
| if (treTypeName.first == null) { |
| activeDataverse = defaultDataverse; |
| activeDatabase = defaultDatabase; |
| } else { |
| activeDataverse = treTypeName.first.getDataverseName(); |
| activeDatabase = treTypeName.first.getDatabaseName(); |
| } |
| return new TypeSignature(activeDatabase, activeDataverse, treTypeName.second.getValue()); |
| } |
| } |