blob: ce30325a8bae0af8952b3bb6df8a1b9ad656451a [file] [log] [blame]
/*
* 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());
}
}