| /* |
| * Copyright 2009-2013 by The Regents of the University of California |
| * Licensed 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 from |
| * |
| * 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 edu.uci.ics.asterix.metadata.entitytupletranslators; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.DataInput; |
| import java.io.DataInputStream; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.rmi.RemoteException; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.List; |
| |
| import edu.uci.ics.asterix.builders.IARecordBuilder; |
| import edu.uci.ics.asterix.builders.OrderedListBuilder; |
| import edu.uci.ics.asterix.builders.RecordBuilder; |
| import edu.uci.ics.asterix.common.exceptions.AsterixException; |
| import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider; |
| import edu.uci.ics.asterix.metadata.MetadataException; |
| import edu.uci.ics.asterix.metadata.MetadataNode; |
| import edu.uci.ics.asterix.metadata.bootstrap.MetadataPrimaryIndexes; |
| import edu.uci.ics.asterix.metadata.bootstrap.MetadataRecordTypes; |
| import edu.uci.ics.asterix.metadata.entities.AsterixBuiltinTypeMap; |
| import edu.uci.ics.asterix.metadata.entities.Datatype; |
| import edu.uci.ics.asterix.om.base.ABoolean; |
| import edu.uci.ics.asterix.om.base.AOrderedList; |
| import edu.uci.ics.asterix.om.base.ARecord; |
| import edu.uci.ics.asterix.om.base.AString; |
| import edu.uci.ics.asterix.om.base.IACursor; |
| import edu.uci.ics.asterix.om.types.AOrderedListType; |
| import edu.uci.ics.asterix.om.types.ARecordType; |
| import edu.uci.ics.asterix.om.types.ATypeTag; |
| import edu.uci.ics.asterix.om.types.AUnionType; |
| import edu.uci.ics.asterix.om.types.AUnorderedListType; |
| import edu.uci.ics.asterix.om.types.AbstractCollectionType; |
| import edu.uci.ics.asterix.om.types.BuiltinType; |
| import edu.uci.ics.asterix.om.types.IAType; |
| import edu.uci.ics.hyracks.algebricks.common.exceptions.NotImplementedException; |
| import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer; |
| import edu.uci.ics.hyracks.api.exceptions.HyracksDataException; |
| import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage; |
| import edu.uci.ics.hyracks.dataflow.common.data.accessors.ITupleReference; |
| import edu.uci.ics.hyracks.storage.am.btree.exceptions.BTreeDuplicateKeyException; |
| |
| /** |
| * Translates a Datatype metadata entity to an ITupleReference and vice versa. |
| */ |
| public class DatatypeTupleTranslator extends AbstractTupleTranslator<Datatype> { |
| // Field indexes of serialized Dataset in a tuple. |
| // First key field. |
| public static final int DATATYPE_DATAVERSENAME_TUPLE_FIELD_INDEX = 0; |
| // Second key field. |
| public static final int DATATYPE_DATATYPE_TUPLE_FIELD_INDEX = 1; |
| // Payload field containing serialized Datatype. |
| public static final int DATATYPE_PAYLOAD_TUPLE_FIELD_INDEX = 2; |
| |
| public enum DerivedTypeTag { |
| ENUM, |
| RECORD, |
| UNION, |
| UNORDEREDLIST, |
| ORDEREDLIST |
| }; |
| |
| @SuppressWarnings("unchecked") |
| private ISerializerDeserializer<ARecord> recordSerDes = AqlSerializerDeserializerProvider.INSTANCE |
| .getSerializerDeserializer(MetadataRecordTypes.DATATYPE_RECORDTYPE); |
| private final MetadataNode metadataNode; |
| private final long txnId; |
| |
| public DatatypeTupleTranslator(long txnId, MetadataNode metadataNode, boolean getTuple) { |
| super(getTuple, MetadataPrimaryIndexes.DATATYPE_DATASET.getFieldCount()); |
| this.txnId = txnId; |
| this.metadataNode = metadataNode; |
| } |
| |
| @Override |
| public Datatype getMetadataEntytiFromTuple(ITupleReference frameTuple) throws MetadataException, IOException { |
| byte[] serRecord = frameTuple.getFieldData(DATATYPE_PAYLOAD_TUPLE_FIELD_INDEX); |
| int recordStartOffset = frameTuple.getFieldStart(DATATYPE_PAYLOAD_TUPLE_FIELD_INDEX); |
| int recordLength = frameTuple.getFieldLength(DATATYPE_PAYLOAD_TUPLE_FIELD_INDEX); |
| ByteArrayInputStream stream = new ByteArrayInputStream(serRecord, recordStartOffset, recordLength); |
| DataInput in = new DataInputStream(stream); |
| ARecord datatypeRecord = (ARecord) recordSerDes.deserialize(in); |
| return createDataTypeFromARecord(datatypeRecord); |
| } |
| |
| private Datatype createDataTypeFromARecord(ARecord datatypeRecord) throws MetadataException { |
| String dataverseName = ((AString) datatypeRecord |
| .getValueByPos(MetadataRecordTypes.DATATYPE_ARECORD_DATAVERSENAME_FIELD_INDEX)).getStringValue(); |
| String datatypeName = ((AString) datatypeRecord |
| .getValueByPos(MetadataRecordTypes.DATATYPE_ARECORD_DATATYPENAME_FIELD_INDEX)).getStringValue(); |
| IAType type = AsterixBuiltinTypeMap.getBuiltinTypes().get(datatypeName); |
| if (type == null) { |
| // Derived Type |
| ARecord derivedTypeRecord = (ARecord) datatypeRecord |
| .getValueByPos(MetadataRecordTypes.DATATYPE_ARECORD_DERIVED_FIELD_INDEX); |
| DerivedTypeTag tag = DerivedTypeTag.valueOf(((AString) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_TAG_FIELD_INDEX)).getStringValue()); |
| boolean isAnonymous = ((ABoolean) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_ISANONYMOUS_FIELD_INDEX)).getBoolean(); |
| switch (tag) { |
| case ENUM: |
| throw new NotImplementedException("Enum type"); |
| case RECORD: { |
| ARecord recordType = (ARecord) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_RECORD_FIELD_INDEX); |
| boolean isOpen = ((ABoolean) recordType |
| .getValueByPos(MetadataRecordTypes.RECORDTYPE_ARECORD_ISOPEN_FIELD_INDEX)).getBoolean() |
| .booleanValue(); |
| int numberOfFields = ((AOrderedList) recordType |
| .getValueByPos(MetadataRecordTypes.RECORDTYPE_ARECORD_FIELDS_FIELD_INDEX)).size(); |
| IACursor cursor = ((AOrderedList) recordType |
| .getValueByPos(MetadataRecordTypes.RECORDTYPE_ARECORD_FIELDS_FIELD_INDEX)).getCursor(); |
| String[] fieldNames = new String[numberOfFields]; |
| IAType[] fieldTypes = new IAType[numberOfFields]; |
| int fieldId = 0; |
| String fieldTypeName; |
| while (cursor.next()) { |
| ARecord field = (ARecord) cursor.get(); |
| fieldNames[fieldId] = ((AString) field |
| .getValueByPos(MetadataRecordTypes.FIELD_ARECORD_FIELDNAME_FIELD_INDEX)) |
| .getStringValue(); |
| fieldTypeName = ((AString) field |
| .getValueByPos(MetadataRecordTypes.FIELD_ARECORD_FIELDTYPE_FIELD_INDEX)) |
| .getStringValue(); |
| fieldTypes[fieldId] = getTypeFromTypeName(dataverseName, fieldTypeName); |
| fieldId++; |
| } |
| try { |
| return new Datatype(dataverseName, datatypeName, new ARecordType(datatypeName, fieldNames, |
| fieldTypes, isOpen), isAnonymous); |
| } catch (AsterixException e) { |
| throw new MetadataException(e); |
| } |
| } |
| case UNION: { |
| IACursor cursor = ((AOrderedList) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_UNION_FIELD_INDEX)).getCursor(); |
| List<IAType> unionList = new ArrayList<IAType>(); |
| String itemTypeName; |
| while (cursor.next()) { |
| itemTypeName = ((AString) cursor.get()).getStringValue(); |
| unionList.add(getTypeFromTypeName(dataverseName, itemTypeName)); |
| } |
| return new Datatype(dataverseName, datatypeName, new AUnionType(unionList, datatypeName), |
| isAnonymous); |
| } |
| case UNORDEREDLIST: { |
| String unorderedlistTypeName = ((AString) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_UNORDEREDLIST_FIELD_INDEX)) |
| .getStringValue(); |
| return new Datatype(dataverseName, datatypeName, new AUnorderedListType(getTypeFromTypeName( |
| dataverseName, unorderedlistTypeName), datatypeName), isAnonymous); |
| } |
| case ORDEREDLIST: { |
| String orderedlistTypeName = ((AString) derivedTypeRecord |
| .getValueByPos(MetadataRecordTypes.DERIVEDTYPE_ARECORD_ORDEREDLIST_FIELD_INDEX)) |
| .getStringValue(); |
| return new Datatype(dataverseName, datatypeName, new AOrderedListType(getTypeFromTypeName( |
| dataverseName, orderedlistTypeName), datatypeName), isAnonymous); |
| } |
| default: |
| throw new UnsupportedOperationException("Unsupported derived type: " + tag); |
| } |
| } |
| return new Datatype(dataverseName, datatypeName, type, false); |
| } |
| |
| private IAType getTypeFromTypeName(String dataverseName, String typeName) throws MetadataException { |
| IAType type = AsterixBuiltinTypeMap.getBuiltinTypes().get(typeName); |
| if (type == null) { |
| try { |
| return metadataNode.getDatatype(txnId, dataverseName, typeName).getDatatype(); |
| } catch (RemoteException e) { |
| throw new MetadataException(e); |
| } |
| } |
| return type; |
| } |
| |
| @Override |
| public ITupleReference getTupleFromMetadataEntity(Datatype dataType) throws IOException, MetadataException { |
| // write the key in the first two fields of the tuple |
| tupleBuilder.reset(); |
| aString.setValue(dataType.getDataverseName()); |
| stringSerde.serialize(aString, tupleBuilder.getDataOutput()); |
| tupleBuilder.addFieldEndOffset(); |
| aString.setValue(dataType.getDatatypeName()); |
| stringSerde.serialize(aString, tupleBuilder.getDataOutput()); |
| tupleBuilder.addFieldEndOffset(); |
| |
| // write the payload in the third field of the tuple |
| recordBuilder.reset(MetadataRecordTypes.DATATYPE_RECORDTYPE); |
| |
| // write field 0 |
| fieldValue.reset(); |
| aString.setValue(dataType.getDataverseName()); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| recordBuilder.addField(MetadataRecordTypes.DATATYPE_ARECORD_DATAVERSENAME_FIELD_INDEX, fieldValue); |
| |
| // write field 1 |
| fieldValue.reset(); |
| aString.setValue(dataType.getDatatypeName()); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| recordBuilder.addField(MetadataRecordTypes.DATATYPE_ARECORD_DATATYPENAME_FIELD_INDEX, fieldValue); |
| |
| // write field 2 |
| ATypeTag tag = dataType.getDatatype().getTypeTag(); |
| if (isDerivedType(tag)) { |
| fieldValue.reset(); |
| try { |
| writeDerivedTypeRecord(dataType, fieldValue.getDataOutput()); |
| } catch (AsterixException e) { |
| throw new MetadataException(e); |
| } |
| recordBuilder.addField(MetadataRecordTypes.DATATYPE_ARECORD_DERIVED_FIELD_INDEX, fieldValue); |
| } |
| |
| // write field 3 |
| fieldValue.reset(); |
| aString.setValue(Calendar.getInstance().getTime().toString()); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| recordBuilder.addField(MetadataRecordTypes.DATATYPE_ARECORD_TIMESTAMP_FIELD_INDEX, fieldValue); |
| |
| // write record |
| try { |
| recordBuilder.write(tupleBuilder.getDataOutput(), true); |
| } catch (AsterixException e) { |
| throw new MetadataException(e); |
| } |
| tupleBuilder.addFieldEndOffset(); |
| |
| tuple.reset(tupleBuilder.getFieldEndOffsets(), tupleBuilder.getByteArray()); |
| return tuple; |
| } |
| |
| private void writeDerivedTypeRecord(Datatype type, DataOutput out) throws IOException, AsterixException { |
| DerivedTypeTag tag; |
| IARecordBuilder derivedRecordBuilder = new RecordBuilder(); |
| ArrayBackedValueStorage fieldValue = new ArrayBackedValueStorage(); |
| switch (type.getDatatype().getTypeTag()) { |
| case UNION: |
| tag = DerivedTypeTag.UNION; |
| break; |
| case ORDEREDLIST: |
| tag = DerivedTypeTag.ORDEREDLIST; |
| break; |
| case UNORDEREDLIST: |
| tag = DerivedTypeTag.UNORDEREDLIST; |
| break; |
| case RECORD: |
| tag = DerivedTypeTag.RECORD; |
| break; |
| default: |
| throw new UnsupportedOperationException("No metadata record Type for" |
| + type.getDatatype().getDisplayName()); |
| } |
| |
| derivedRecordBuilder.reset(MetadataRecordTypes.DERIVEDTYPE_RECORDTYPE); |
| |
| // write field 0 |
| fieldValue.reset(); |
| aString.setValue(tag.toString()); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_TAG_FIELD_INDEX, fieldValue); |
| |
| // write field 1 |
| fieldValue.reset(); |
| booleanSerde.serialize(type.getIsAnonymous() ? ABoolean.TRUE : ABoolean.FALSE, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_ISANONYMOUS_FIELD_INDEX, fieldValue); |
| |
| switch (tag) { |
| case ENUM: |
| break; |
| case RECORD: |
| fieldValue.reset(); |
| writeRecordType(type, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_RECORD_FIELD_INDEX, fieldValue); |
| break; |
| case UNION: |
| fieldValue.reset(); |
| writeUnionType(type, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_UNION_FIELD_INDEX, fieldValue); |
| break; |
| case UNORDEREDLIST: |
| fieldValue.reset(); |
| writeCollectionType(type, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_UNORDEREDLIST_FIELD_INDEX, |
| fieldValue); |
| break; |
| case ORDEREDLIST: |
| fieldValue.reset(); |
| writeCollectionType(type, fieldValue.getDataOutput()); |
| derivedRecordBuilder.addField(MetadataRecordTypes.DERIVEDTYPE_ARECORD_ORDEREDLIST_FIELD_INDEX, |
| fieldValue); |
| break; |
| } |
| derivedRecordBuilder.write(out, true); |
| } |
| |
| private void writeCollectionType(Datatype instance, DataOutput out) throws HyracksDataException { |
| AbstractCollectionType listType = (AbstractCollectionType) instance.getDatatype(); |
| String itemTypeName = listType.getItemType().getTypeName(); |
| if (isDerivedType(listType.getItemType().getTypeTag())) { |
| try { |
| itemTypeName = handleNestedDerivedType(itemTypeName, instance.getDatatypeName() + "_ItemType", |
| listType.getItemType(), instance); |
| } catch (Exception e) { |
| // TODO: This should not be a HyracksDataException. Can't |
| // fix this currently because of BTree exception model whose |
| // fixes must get in. |
| throw new HyracksDataException(e); |
| } |
| } |
| aString.setValue(itemTypeName); |
| stringSerde.serialize(aString, out); |
| } |
| |
| private void writeUnionType(Datatype instance, DataOutput dataOutput) throws HyracksDataException { |
| List<IAType> unionList = ((AUnionType) instance.getDatatype()).getUnionList(); |
| OrderedListBuilder listBuilder = new OrderedListBuilder(); |
| listBuilder.reset(new AOrderedListType(BuiltinType.ASTRING, null)); |
| ArrayBackedValueStorage itemValue = new ArrayBackedValueStorage(); |
| String typeName = null; |
| |
| int i = 0; |
| for (IAType t : unionList) { |
| typeName = t.getTypeName(); |
| if (isDerivedType(t.getTypeTag())) { |
| try { |
| typeName = handleNestedDerivedType(typeName, |
| "Type_#" + i + "_UnionType_" + instance.getDatatypeName(), t, instance); |
| } catch (Exception e) { |
| // TODO: This should not be a HyracksDataException. Can't |
| // fix this currently because of BTree exception model whose |
| // fixes must get in. |
| throw new HyracksDataException(e); |
| } |
| } |
| itemValue.reset(); |
| aString.setValue(typeName); |
| stringSerde.serialize(aString, itemValue.getDataOutput()); |
| listBuilder.addItem(itemValue); |
| i++; |
| } |
| listBuilder.write(dataOutput, true); |
| } |
| |
| private void writeRecordType(Datatype instance, DataOutput out) throws IOException, AsterixException { |
| |
| ArrayBackedValueStorage fieldValue = new ArrayBackedValueStorage(); |
| ArrayBackedValueStorage itemValue = new ArrayBackedValueStorage(); |
| IARecordBuilder recordRecordBuilder = new RecordBuilder(); |
| IARecordBuilder fieldRecordBuilder = new RecordBuilder(); |
| |
| ARecordType recType = (ARecordType) instance.getDatatype(); |
| OrderedListBuilder listBuilder = new OrderedListBuilder(); |
| listBuilder.reset(new AOrderedListType(MetadataRecordTypes.FIELD_RECORDTYPE, null)); |
| String fieldTypeName = null; |
| for (int i = 0; i < recType.getFieldNames().length; i++) { |
| fieldTypeName = recType.getFieldTypes()[i].getTypeName(); |
| if (isDerivedType(recType.getFieldTypes()[i].getTypeTag())) { |
| try { |
| fieldTypeName = handleNestedDerivedType(fieldTypeName, "Field_" + recType.getFieldNames()[i] |
| + "_in_" + instance.getDatatypeName(), recType.getFieldTypes()[i], instance); |
| } catch (Exception e) { |
| // TODO: This should not be a HyracksDataException. Can't |
| // fix this currently because of BTree exception model whose |
| // fixes must get in. |
| throw new HyracksDataException(e); |
| } |
| } |
| |
| itemValue.reset(); |
| fieldRecordBuilder.reset(MetadataRecordTypes.FIELD_RECORDTYPE); |
| |
| // write field 0 |
| fieldValue.reset(); |
| aString.setValue(recType.getFieldNames()[i]); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| fieldRecordBuilder.addField(MetadataRecordTypes.FIELD_ARECORD_FIELDNAME_FIELD_INDEX, fieldValue); |
| |
| // write field 1 |
| fieldValue.reset(); |
| aString.setValue(fieldTypeName); |
| stringSerde.serialize(aString, fieldValue.getDataOutput()); |
| fieldRecordBuilder.addField(MetadataRecordTypes.FIELD_ARECORD_FIELDTYPE_FIELD_INDEX, fieldValue); |
| |
| // write record |
| fieldRecordBuilder.write(itemValue.getDataOutput(), true); |
| |
| // add item to the list of fields |
| listBuilder.addItem(itemValue); |
| } |
| |
| recordRecordBuilder.reset(MetadataRecordTypes.RECORD_RECORDTYPE); |
| // write field 0 |
| fieldValue.reset(); |
| booleanSerde.serialize(recType.isOpen() ? ABoolean.TRUE : ABoolean.FALSE, fieldValue.getDataOutput()); |
| recordRecordBuilder.addField(MetadataRecordTypes.RECORDTYPE_ARECORD_ISOPEN_FIELD_INDEX, fieldValue); |
| |
| // write field 1 |
| fieldValue.reset(); |
| listBuilder.write(fieldValue.getDataOutput(), true); |
| recordRecordBuilder.addField(MetadataRecordTypes.RECORDTYPE_ARECORD_FIELDS_FIELD_INDEX, fieldValue); |
| |
| // write record |
| recordRecordBuilder.write(out, true); |
| } |
| |
| private String handleNestedDerivedType(String typeName, String suggestedTypeName, IAType nestedType, |
| Datatype topLevelType) throws Exception { |
| MetadataNode mn = MetadataNode.INSTANCE; |
| try { |
| if (typeName == null) { |
| typeName = suggestedTypeName; |
| metadataNode.addDatatype(txnId, new Datatype(topLevelType.getDataverseName(), typeName, nestedType, |
| true)); |
| |
| } |
| mn.insertIntoDatatypeSecondaryIndex(txnId, topLevelType.getDataverseName(), typeName, |
| topLevelType.getDatatypeName()); |
| |
| } catch (BTreeDuplicateKeyException e) { |
| // The key may have been inserted by a previous DDL statement or by |
| // a previous nested type. |
| } |
| return typeName; |
| |
| } |
| |
| private boolean isDerivedType(ATypeTag tag) { |
| if (tag == ATypeTag.RECORD || tag == ATypeTag.ORDEREDLIST || tag == ATypeTag.UNORDEREDLIST |
| || tag == ATypeTag.UNION) |
| return true; |
| return false; |
| } |
| } |