/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.transformation.metadata;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.script.ScriptEngine;
import org.teiid.core.designer.TeiidDesignerException;
import org.teiid.core.designer.TeiidDesignerRuntimeException;
import org.teiid.core.designer.id.UUID;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.ModelType;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.index.CompositeIndexSelector;
import org.teiid.designer.core.index.IEntryResult;
import org.teiid.designer.core.index.Index;
import org.teiid.designer.core.index.IndexConstants;
import org.teiid.designer.core.index.IndexSelector;
import org.teiid.designer.core.index.RuntimeIndexSelector;
import org.teiid.designer.core.index.SimpleIndexUtil;
import org.teiid.designer.metadata.runtime.ColumnRecord;
import org.teiid.designer.metadata.runtime.ColumnRecordComparator;
import org.teiid.designer.metadata.runtime.ColumnSetRecord;
import org.teiid.designer.metadata.runtime.DatatypeRecord;
import org.teiid.designer.metadata.runtime.ForeignKeyRecord;
import org.teiid.designer.metadata.runtime.MetadataConstants;
import org.teiid.designer.metadata.runtime.MetadataRecord;
import org.teiid.designer.metadata.runtime.ModelRecord;
import org.teiid.designer.metadata.runtime.ProcedureParameterRecord;
import org.teiid.designer.metadata.runtime.ProcedureRecord;
import org.teiid.designer.metadata.runtime.PropertyRecord;
import org.teiid.designer.metadata.runtime.TableRecord;
import org.teiid.designer.metadata.runtime.TransformationRecord;
import org.teiid.designer.metadata.runtime.VdbRecord;
import org.teiid.designer.metadata.runtime.impl.RecordFactory;
import org.teiid.designer.query.IQueryFactory;
import org.teiid.designer.query.IQueryService;
import org.teiid.designer.query.metadata.IQueryMetadataInterface;
import org.teiid.designer.query.metadata.IQueryNode;
import org.teiid.designer.query.metadata.IStoredProcedureInfo;
import org.teiid.designer.query.sql.lang.ISPParameter;
import org.teiid.designer.runtime.version.spi.ITeiidServerVersion;
import org.teiid.designer.transformation.TransformationPlugin;
import org.teiid.designer.type.IDataTypeManagerService;
import org.teiid.designer.udf.IFunctionLibrary;
import org.teiid.designer.udf.UdfManager;
import org.teiid.designer.xml.IMappingDocumentFactory;
import org.teiid.designer.xml.IMappingNode;
/**
* Modelers implementation of QueryMetadataInterface that reads columns, groups, models etc. index files for various metadata
* properties.
*
* @since 8.0
*/
public class TransformationMetadata implements IQueryMetadataInterface {
// Fix Me: The following constants come from org.teiid.designer.metamodels.relational.NullableType
private static int NULLABLE = 1;
private static int NULLABLE_UNKNOWN = 2;
// Fix Me: The following constants come from org.teiid.designer.metamodels.relational.SearchabilityType
private static int SEARCHABLE = 0;
private static int ALL_EXCEPT_LIKE = 1;
private static int LIKE_ONLY = 2;
/** Delimiter character used when specifying fully qualified entity names */
public static final char DELIMITER_CHAR = IndexConstants.NAME_DELIM_CHAR;
protected static final String DELIMITER_STRING = CoreStringUtil.Constants.EMPTY_STRING + IndexConstants.NAME_DELIM_CHAR;
private static ColumnRecordComparator columnComparator = new ColumnRecordComparator();
// error message cached to avaid i18n lookup each time
private static String NOT_EXISTS_MESSAGE = CoreStringUtil.Constants.SPACE
+ TransformationPlugin.Util.getString("TransformationMetadata.does_not_exist._1"); //$NON-NLS-1$
// context object all the info needed for metadata lookup
private final QueryMetadataContext context;
// ==================================================================================
// C O N S T R U C T O R S
// ==================================================================================
/**
* TransformationMetadata constructor
*
* @param context Object containing the info needed to lookup metadta.
*/
protected TransformationMetadata(final QueryMetadataContext context) {
CoreArgCheck.isNotNull(context);
this.context = context;
}
// ==================================================================================
// I N T E R F A C E M E T H O D S
// ==================================================================================
@Override
public ITeiidServerVersion getTeiidVersion() {
return ModelerCore.getTeiidServerVersion();
}
@Override
public Object getElementID(final String elementName) throws Exception {
CoreArgCheck.isNotEmpty(elementName);
// elementfull names always contain atlest 3 segments(modelname.groupName.elementName)
if (CoreStringUtil.startsWithIgnoreCase(elementName, UUID.PROTOCOL)
|| CoreStringUtil.getTokens(elementName, DELIMITER_STRING).size() >= 3) {
// Query the index files
return getRecordByType(elementName, IndexConstants.RECORD_TYPE.COLUMN);
}
throw new Exception(elementName + NOT_EXISTS_MESSAGE);
}
@Override
public Object getGroupID(final String groupName) throws Exception {
CoreArgCheck.isNotEmpty(groupName);
// groupfull names always contain atlest 2 segments(modelname.groupName)
if (CoreStringUtil.startsWithIgnoreCase(groupName, UUID.PROTOCOL)
|| CoreStringUtil.getTokens(groupName, DELIMITER_STRING).size() >= 2) {
// Query the index files
return getRecordByType(groupName, IndexConstants.RECORD_TYPE.TABLE);
}
throw new Exception(groupName + NOT_EXISTS_MESSAGE);
}
@Override
public Collection getGroupsForPartialName(final String partialGroupName) throws Exception {
CoreArgCheck.isNotEmpty(partialGroupName);
Collection tableRecords = null;
String partialName = partialGroupName;
// if it the group is a UUID
if (!CoreStringUtil.startsWithIgnoreCase(partialGroupName, UUID.PROTOCOL)) {
// Prepend a "." so only match full part names
partialName = DELIMITER_CHAR + partialGroupName;
}
// Query the index files
tableRecords = findMetadataRecords(IndexConstants.RECORD_TYPE.TABLE, partialName, true);
// Extract the fully qualified names to return
final Collection tableNames = new ArrayList(tableRecords.size());
for (Iterator recordIter = tableRecords.iterator(); recordIter.hasNext();) {
// get the table record for this result
TableRecord tableRecord = (TableRecord)recordIter.next();
tableNames.add(getFullName(tableRecord));
}
return tableNames;
}
@Override
public Object getModelID(final Object groupOrElementID) throws Exception {
CoreArgCheck.isInstanceOf(MetadataRecord.class, groupOrElementID);
MetadataRecord metadataRecord = (MetadataRecord)groupOrElementID;
// get modelName
String modelName = metadataRecord.getModelName();
// Query the index files
return getRecordByType(modelName, IndexConstants.RECORD_TYPE.MODEL);
}
@Override
public String getFullName(final Object metadataID) {
CoreArgCheck.isInstanceOf(MetadataRecord.class, metadataID);
MetadataRecord metadataRecord = (MetadataRecord)metadataID;
return metadataRecord.getFullName();
}
@Override
public List getElementIDsInGroupID(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
// Query the index files
final String groupName = tableRecord.getFullName();
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
final Collection results = findChildRecords(tableRecord, IndexConstants.RECORD_TYPE.COLUMN);
if (results.isEmpty()) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Group(0}_does_not_have_elements", groupName)); //$NON-NLS-1$
}
List columnRecords = new ArrayList(results);
// Sort the column records according to their positions
Collections.sort(columnRecords, columnComparator);
return columnRecords;
}
@Override
public Object getGroupIDForElementID(final Object elementID) throws Exception {
if (elementID instanceof ColumnRecord) {
ColumnRecord columnRecord = (ColumnRecord)elementID;
String tableUUID = columnRecord.getParentUUID();
return this.getGroupID(tableUUID);
} else if (elementID instanceof ProcedureParameterRecord) {
ProcedureParameterRecord columnRecord = (ProcedureParameterRecord)elementID;
String tableUUID = columnRecord.getParentUUID();
return this.getGroupID(tableUUID);
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public IStoredProcedureInfo getStoredProcedureInfoForProcedure(final String procedureName)
throws Exception {
IStoredProcedureInfo result = getStoredProcInfoDirect(procedureName);
if (result == null) {
throw new Exception(procedureName + NOT_EXISTS_MESSAGE);
}
return result;
}
@Override
public boolean hasProcedure(String name) {
try {
return getStoredProcInfoDirect(name) != null;
} catch (Exception e) {
return false;
}
}
/**
* Method to convert the parameter type returned from a ProcedureParameterRecord to the parameter type expected by
* StoredProcedureInfo
*
* @param parameterType
* @return
*/
private ISPParameter.ParameterInfo convertParamRecordTypeToStoredProcedureType(final int parameterType) {
switch (parameterType) {
case MetadataConstants.PARAMETER_TYPES.IN_PARM:
return ISPParameter.ParameterInfo.IN;
case MetadataConstants.PARAMETER_TYPES.OUT_PARM:
return ISPParameter.ParameterInfo.OUT;
case MetadataConstants.PARAMETER_TYPES.INOUT_PARM:
return ISPParameter.ParameterInfo.INOUT;
case MetadataConstants.PARAMETER_TYPES.RETURN_VALUE:
return ISPParameter.ParameterInfo.RETURN_VALUE;
case MetadataConstants.PARAMETER_TYPES.RESULT_SET:
return ISPParameter.ParameterInfo.RESULT_SET;
default:
throw new RuntimeException();
}
}
@Override
public String getElementType(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getRuntimeType();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getRuntimeType();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public String getDefaultValue(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getDefaultValue();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getDefaultValue();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public Object getMinimumValue(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getMinValue();
} else if (elementID instanceof ProcedureParameterRecord) {
return null;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public Object getMaximumValue(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getMaxValue();
} else if (elementID instanceof ProcedureParameterRecord) {
return null;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public boolean isVirtualGroup(final Object groupID) {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
return ((TableRecord)groupID).isVirtual();
}
@Override
public boolean isProcedure(final Object groupID) {
if (groupID instanceof ProcedureRecord) {
return true;
}
if (groupID instanceof TableRecord) {
return false;
}
throw createInvalidRecordTypeException(groupID);
}
@Override
public boolean isVirtualModel(final Object modelID) {
CoreArgCheck.isInstanceOf(ModelRecord.class, modelID);
ModelRecord modelRecord = (ModelRecord)modelID;
return (modelRecord.getModelType() == ModelType.VIRTUAL);
}
@Override
public IQueryNode getVirtualPlan(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupName = tableRecord.getFullName();
if (tableRecord.isVirtual()) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.SELECT_TRANSFORM, groupName, false);
// this group may be an xml document
if (results.isEmpty()) {
results = findMetadataRecords(IndexConstants.RECORD_TYPE.MAPPING_TRANSFORM, groupName, false);
}
int resultSize = results.size();
if (resultSize == 1) {
// get the transform record for this result
final TransformationRecord transformRecord = (TransformationRecord)results.iterator().next();
String transQuery = transformRecord.getTransformation();
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
IQueryNode queryNode = factory.createQueryNode(transQuery);
// get any bindings and add them onto the query node
List bindings = transformRecord.getBindings();
if (bindings != null) {
for (Iterator bindIter = bindings.iterator(); bindIter.hasNext();) {
queryNode.addBinding((String)bindIter.next());
}
}
return queryNode;
}
// no transfomation available
if (resultSize == 0) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Could_not_find_query_plan_for_the_group__5") + groupName); //$NON-NLS-1$
}
// there should be only one result entry for a fully qualified name
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.GroupID_ambiguous_there_are_multiple_virtual_plans_available_for_this_groupID__1") + groupName); //$NON-NLS-1$
}
}
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.QueryPlan_could_not_be_found_for_physical_group__6") + groupName); //$NON-NLS-1$
}
@Override
public String getInsertPlan(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupName = tableRecord.getFullName();
if (tableRecord.isVirtual()) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.INSERT_TRANSFORM, groupName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the transform record for this result
final TransformationRecord transformRecord = (TransformationRecord)results.iterator().next();
return transformRecord.getTransformation();
}
// no transfomation available
if (resultSize == 0) {
return null;
}
// there should be only one result entry for a fully qualified name
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.GroupID_ambiguous_there_are_multiple_insert_plans_available_for_this_groupID__2") + groupName); //$NON-NLS-1$
}
}
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.InsertPlan_could_not_be_found_for_physical_group__8") + groupName); //$NON-NLS-1$
}
@Override
public String getUpdatePlan(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupName = tableRecord.getFullName();
if (tableRecord.isVirtual()) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.UPDATE_TRANSFORM, groupName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the transform record for this result
final TransformationRecord transformRecord = (TransformationRecord)results.iterator().next();
return transformRecord.getTransformation();
}
// no transfomation available
if (resultSize == 0) {
return null;
}
// there should be only one result entry for a fully qualified name
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.GroupID_ambiguous_there_are_multiple_update_plans_available_for_this_groupID__3") + groupName); //$NON-NLS-1$
}
}
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.InsertPlan_could_not_be_found_for_physical_group__10") + groupName); //$NON-NLS-1$
}
@Override
public String getDeletePlan(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupName = tableRecord.getFullName();
if (tableRecord.isVirtual()) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.DELETE_TRANSFORM, groupName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the transform record for this result
final TransformationRecord transformRecord = (TransformationRecord)results.iterator().next();
return transformRecord.getTransformation();
}
// no transfomation available
if (resultSize == 0) {
return null;
}
// there should be only one result entry for a fully qualified name
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.GroupID_ambiguous_there_are_multiple_delete_plans_available_for_this_groupID__4") + groupName); //$NON-NLS-1$
}
}
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.DeletePlan_could_not_be_found_for_physical_group__12") + groupName); //$NON-NLS-1$
}
@Override
public boolean modelSupports(final Object modelID,
final int modelConstant) {
CoreArgCheck.isInstanceOf(ModelRecord.class, modelID);
switch (modelConstant) {
default:
throw new UnsupportedOperationException(
TransformationPlugin.Util.getString("TransformationMetadata.Unknown_support_constant___12") + modelConstant); //$NON-NLS-1$
}
}
@Override
public boolean groupSupports(final Object groupID,
final int groupConstant) {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
switch (groupConstant) {
case SupportConstants.Group.UPDATE:
return tableRecord.supportsUpdate();
default:
throw new UnsupportedOperationException(
TransformationPlugin.Util.getString("TransformationMetadata.Unknown_support_constant___12") + groupConstant); //$NON-NLS-1$
}
}
@Override
public boolean elementSupports(final Object elementID,
final int elementConstant) {
if (elementID instanceof ColumnRecord) {
ColumnRecord columnRecord = (ColumnRecord)elementID;
switch (elementConstant) {
case SupportConstants.Element.NULL:
int ntype1 = columnRecord.getNullType();
return (ntype1 == NULLABLE);
case SupportConstants.Element.NULL_UNKNOWN:
int ntype2 = columnRecord.getNullType();
return (ntype2 == NULLABLE_UNKNOWN);
case SupportConstants.Element.SEARCHABLE_COMPARE:
int stype1 = columnRecord.getSearchType();
return (stype1 == SEARCHABLE || stype1 == ALL_EXCEPT_LIKE);
case SupportConstants.Element.SEARCHABLE_LIKE:
int stype2 = columnRecord.getSearchType();
return (stype2 == SEARCHABLE || stype2 == LIKE_ONLY);
case SupportConstants.Element.SELECT:
return columnRecord.isSelectable();
case SupportConstants.Element.UPDATE:
return columnRecord.isUpdatable();
case SupportConstants.Element.DEFAULT_VALUE:
Object defaultValue = columnRecord.getDefaultValue();
if (defaultValue == null) {
return false;
}
return true;
case SupportConstants.Element.AUTO_INCREMENT:
return columnRecord.isAutoIncrementable();
case SupportConstants.Element.CASE_SENSITIVE:
return columnRecord.isCaseSensitive();
case SupportConstants.Element.SIGNED:
return columnRecord.isSigned();
default:
throw new UnsupportedOperationException(
TransformationPlugin.Util.getString("TransformationMetadata.Unknown_support_constant___12") + elementConstant); //$NON-NLS-1$
}
} else if (elementID instanceof ProcedureParameterRecord) {
ProcedureParameterRecord columnRecord = (ProcedureParameterRecord)elementID;
switch (elementConstant) {
case SupportConstants.Element.NULL:
int ntype1 = columnRecord.getNullType();
return (ntype1 == NULLABLE);
case SupportConstants.Element.NULL_UNKNOWN:
int ntype2 = columnRecord.getNullType();
return (ntype2 == NULLABLE_UNKNOWN);
case SupportConstants.Element.SEARCHABLE_COMPARE:
case SupportConstants.Element.SEARCHABLE_LIKE:
return false;
case SupportConstants.Element.SELECT:
if (columnRecord.getType() == MetadataConstants.PARAMETER_TYPES.IN_PARM) {
return false;
}
return true;
case SupportConstants.Element.UPDATE:
return false;
case SupportConstants.Element.DEFAULT_VALUE:
Object defaultValue = columnRecord.getDefaultValue();
if (defaultValue == null) {
return false;
}
return true;
case SupportConstants.Element.AUTO_INCREMENT:
return false;
case SupportConstants.Element.CASE_SENSITIVE:
return false;
case SupportConstants.Element.SIGNED:
return true;
default:
throw new UnsupportedOperationException(
TransformationPlugin.Util.getString("TransformationMetadata.Unknown_support_constant___12") + elementConstant); //$NON-NLS-1$
}
} else {
throw createInvalidRecordTypeException(elementID);
}
}
private IllegalArgumentException createInvalidRecordTypeException(Object elementID) {
return new IllegalArgumentException(
TransformationPlugin.Util.getString("TransformationMetadata.Invalid_type", elementID.getClass().getName())); //$NON-NLS-1$
}
@Override
public int getMaxSetSize(final Object modelID) {
CoreArgCheck.isInstanceOf(ModelRecord.class, modelID);
return ((ModelRecord)modelID).getMaxSetSize();
}
@Override
public Collection getIndexesInGroup(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
// get the indexIDs
Collection indexIDs = tableRecord.getIndexIDs();
Collection indexRecords = new HashSet(indexIDs.size());
// find a index record for each ID
for (Iterator indexIter = tableRecord.getIndexIDs().iterator(); indexIter.hasNext();) {
String indexID = (String)indexIter.next();
// Query the index files
final Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.INDEX, indexID, false);
if (results.size() != 1) {
if (results.isEmpty()) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.No_metadata_info_available_for_the_index_with_UUID_{0}._1", indexID)); //$NON-NLS-1$
}
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Ambigous_index_with_UUID_{0},_found_multiple_indexes_with_the_given_UUID._2", indexID)); //$NON-NLS-1$
}
indexRecords.addAll(results);
}
return indexRecords;
}
@Override
public Collection getUniqueKeysInGroup(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
// Query the index files
// find all unique keys
return findChildRecords(tableRecord, IndexConstants.RECORD_TYPE.UNIQUE_KEY);
}
@Override
public Collection getForeignKeysInGroup(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
// Query the index files
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
return findChildRecords(tableRecord, IndexConstants.RECORD_TYPE.FOREIGN_KEY);
}
@Override
public Object getPrimaryKeyIDForForeignKeyID(final Object foreignKeyID)
throws Exception {
CoreArgCheck.isInstanceOf(ForeignKeyRecord.class, foreignKeyID);
ForeignKeyRecord fkRecord = (ForeignKeyRecord)foreignKeyID;
String uuid = (String)fkRecord.getUniqueKeyID();
return this.getRecordByType(uuid, IndexConstants.RECORD_TYPE.PRIMARY_KEY);
}
@Override
public Collection getAccessPatternsInGroup(final Object groupID)
throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
// Query the index files
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
return findChildRecords(tableRecord, IndexConstants.RECORD_TYPE.ACCESS_PATTERN);
}
@Override
public List getElementIDsInIndex(final Object index) throws Exception {
CoreArgCheck.isInstanceOf(ColumnSetRecord.class, index);
ColumnSetRecord indexRecord = (ColumnSetRecord)index;
boolean recordMatch = (indexRecord.getRecordType() == IndexConstants.RECORD_TYPE.INDEX);
if (!recordMatch) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.The_metadataID_passed_does_not_match_a_index_record._1")); //$NON-NLS-1$
}
List uuids = indexRecord.getColumnIDs();
List columnRecords = new ArrayList(uuids.size());
for (Iterator uuidIter = uuids.iterator(); uuidIter.hasNext();) {
String uuid = (String)uuidIter.next();
columnRecords.add(this.getElementID(uuid));
}
return columnRecords;
}
@Override
public List getElementIDsInKey(final Object key) throws Exception {
CoreArgCheck.isInstanceOf(ColumnSetRecord.class, key);
ColumnSetRecord keyRecord = (ColumnSetRecord)key;
boolean recordMatch = (keyRecord.getRecordType() == IndexConstants.RECORD_TYPE.FOREIGN_KEY
|| keyRecord.getRecordType() == IndexConstants.RECORD_TYPE.PRIMARY_KEY || keyRecord.getRecordType() == IndexConstants.RECORD_TYPE.UNIQUE_KEY);
if (!recordMatch) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Expected_id_of_the_type_key_record_as_the_argument_2")); //$NON-NLS-1$
}
List uuids = keyRecord.getColumnIDs();
// Get the table record for this key
final String groupUUID = keyRecord.getParentUUID();
CoreArgCheck.isNotNull(groupUUID);
final TableRecord tableRecord = (TableRecord)this.getGroupID(groupUUID);
// Query the index files
final Collection results = findChildRecordsForColumns(tableRecord, IndexConstants.RECORD_TYPE.COLUMN, uuids);
if (results.isEmpty()) {
throw new Exception(tableRecord.getFullName() + NOT_EXISTS_MESSAGE);
}
return new ArrayList(results);
}
@Override
public List getElementIDsInAccessPattern(final Object accessPattern)
throws Exception {
CoreArgCheck.isInstanceOf(ColumnSetRecord.class, accessPattern);
ColumnSetRecord accessRecord = (ColumnSetRecord)accessPattern;
boolean recordMatch = (accessRecord.getRecordType() == IndexConstants.RECORD_TYPE.ACCESS_PATTERN);
if (!recordMatch) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Expected_id_of_the_type_accesspattern_record_as_the_argument_3")); //$NON-NLS-1$
}
List uuids = accessRecord.getColumnIDs();
// Get the table record for this key
final String groupUUID = accessRecord.getParentUUID();
CoreArgCheck.isNotNull(groupUUID);
final TableRecord tableRecord = (TableRecord)this.getGroupID(groupUUID);
// Query the index files
final Collection results = findChildRecordsForColumns(tableRecord, IndexConstants.RECORD_TYPE.COLUMN, uuids);
if (results.isEmpty()) {
throw new Exception(tableRecord.getFullName() + NOT_EXISTS_MESSAGE);
}
return new ArrayList(results);
}
@Override
public boolean isXMLGroup(final Object groupID) {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
if (tableRecord.getTableType() == MetadataConstants.TABLE_TYPES.DOCUMENT_TYPE) {
return true;
}
return false;
}
@Override
public boolean hasMaterialization(final Object groupID) {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
return tableRecord.isMaterialized();
}
@Override
public Object getMaterialization(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
if (tableRecord.isMaterialized()) {
String uuid = (String)tableRecord.getMaterializedTableID();
return this.getGroupID(uuid);
}
return null;
}
@Override
public Object getMaterializationStage(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
if (tableRecord.isMaterialized()) {
String uuid = (String)tableRecord.getMaterializedStageTableID();
return this.getGroupID(uuid);
}
return null;
}
@Override
public IMappingNode getMappingNode(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
final String groupName = tableRecord.getFullName();
if (tableRecord.isVirtual()) {
// get the transform record for this group
TransformationRecord transformRecord = null;
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.MAPPING_TRANSFORM, groupName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the columnset record for this result
transformRecord = (TransformationRecord)results.iterator().next();
} else {
if (resultSize == 0) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Could_not_find_transformation_record_for_the_group__1") + groupName); //$NON-NLS-1$
}
// there should be only one for a fully qualified elementName
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Multiple_transformation_records_found_for_the_group___1") + groupName); //$NON-NLS-1$
}
}
// get mappin transform
String document = transformRecord.getTransformation();
InputStream inputStream = new ByteArrayInputStream(document.getBytes());
IMappingNode mappingDoc = null;
try {
IQueryService queryService = ModelerCore.getTeiidQueryService();
IMappingDocumentFactory factory = queryService.getMappingDocumentFactory();
mappingDoc = factory.loadMappingDocument(inputStream, groupName);
return mappingDoc;
} catch (Exception e) {
throw new TeiidDesignerException(
e,
TransformationPlugin.Util.getString("TransformationMetadata.Error_trying_to_read_virtual_document_{0},_with_body__n{1}_1", groupName, mappingDoc)); //$NON-NLS-1$
} finally {
try {
inputStream.close();
} catch (Exception e) {
// Ignore
}
}
}
return null;
}
@Override
public String getVirtualDatabaseName() {
// Query the index files
try {
final VdbRecord vdbRecord = (VdbRecord)this.getRecordByType(null, IndexConstants.RECORD_TYPE.VDB_ARCHIVE);
return vdbRecord.getName();
} catch (Exception e) {
return null;
}
}
@Override
public Collection<Object> getXMLTempGroups(final Object groupID) throws Exception {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
int tableType = tableRecord.getTableType();
if (tableType == MetadataConstants.TABLE_TYPES.DOCUMENT_TYPE) {
// Query the index files
final Collection results = findChildRecordsWithoutFiltering(tableRecord, IndexConstants.RECORD_TYPE.TABLE);
if (!results.isEmpty()) {
Collection tempGroups = new HashSet(results.size());
for (Iterator resultIter = results.iterator(); resultIter.hasNext();) {
TableRecord record = (TableRecord)resultIter.next();
if (record.getTableType() == MetadataConstants.TABLE_TYPES.XML_STAGING_TABLE_TYPE) {
tempGroups.add(record);
}
}
return tempGroups;
}
}
return Collections.EMPTY_SET;
}
@Override
public float getCardinality(final Object groupID) {
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
return ((TableRecord)groupID).getCardinality();
}
@Override
public List getXMLSchemas(final Object groupID) throws Exception {
if (!(getIndexSelector() instanceof CompositeIndexSelector || getIndexSelector() instanceof RuntimeIndexSelector)) {
return Collections.EMPTY_LIST;
}
CoreArgCheck.isInstanceOf(TableRecord.class, groupID);
TableRecord tableRecord = (TableRecord)groupID;
// lookup transformation record for the group
String groupName = tableRecord.getFullName();
TransformationRecord transformRecord = null;
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.MAPPING_TRANSFORM, groupName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the columnset record for this result
transformRecord = (TransformationRecord)results.iterator().next();
} else {
if (resultSize == 0) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Could_not_find_transformation_record_for_the_group__1") + groupName); //$NON-NLS-1$
}
// there should be only one for a fully qualified elementName
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Multiple_transformation_records_found_for_the_group___1") + groupName); //$NON-NLS-1$
}
}
// get the schema Paths
List<String> schemaPaths = transformRecord.getSchemaPaths();
List<String> fullPaths = new LinkedList<String>();
for (String string : schemaPaths) {
fullPaths.add(string);
}
// get schema contents
List schemas = getIndexSelector().getFileContentsAsString(fullPaths);
if (schemas == null || schemas.isEmpty()) {
schemas = getIndexSelector().getFileContentsAsString(schemaPaths);
if (schemas == null || schemas.isEmpty()) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Error_trying_to_read_schemas_for_the_document/table____1") + groupName); //$NON-NLS-1$
}
}
return schemas;
}
@Override
public String getNameInSource(final Object metadataID) {
CoreArgCheck.isInstanceOf(MetadataRecord.class, metadataID);
return ((MetadataRecord)metadataID).getNameInSource();
}
@Override
public int getElementLength(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getLength();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getLength();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public int getPosition(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getPosition();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getPosition();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public int getPrecision(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getPrecision();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getPrecision();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public int getRadix(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getRadix();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getRadix();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public String getFormat(Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getFormat();
}
throw createInvalidRecordTypeException(elementID);
}
@Override
public int getScale(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getScale();
} else if (elementID instanceof ProcedureParameterRecord) {
return ((ProcedureParameterRecord)elementID).getScale();
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public float getDistinctValues(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getDistinctValues();
} else if (elementID instanceof ProcedureParameterRecord) {
return -1;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public float getNullValues(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getNullValues();
} else if (elementID instanceof ProcedureParameterRecord) {
return -1;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public String getNativeType(final Object elementID) {
if (elementID instanceof ColumnRecord) {
return ((ColumnRecord)elementID).getNativeType();
} else if (elementID instanceof ProcedureParameterRecord) {
return null;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
@Override
public Properties getExtensionProperties(final Object metadataID)
throws Exception {
CoreArgCheck.isInstanceOf(MetadataRecord.class, metadataID);
MetadataRecord metadataRecord = (MetadataRecord)metadataID;
Properties extProps = new Properties();
// find the entities properties records
String uuid = metadataRecord.getUUID();
String prefixString = getUUIDPrefixPattern(IndexConstants.RECORD_TYPE.PROPERTY, uuid);
Index[] indexes = getIndexes(IndexConstants.RECORD_TYPE.PROPERTY, getIndexSelector());
IEntryResult[] results = queryIndex(indexes, prefixString.toCharArray(), true, true);
if (results != null && results.length > 0) {
// get the property records for this result
for (int i = 0; i < results.length; i++) {
if (results[i] != null) {
PropertyRecord record = (PropertyRecord)findMetadataRecord(results[i]);
if (record != null) {
extProps.setProperty(record.getPropertyName(), record.getPropertyValue());
}
}
}
}
return extProps;
}
@Override
public byte[] getBinaryVDBResource(String resourcePath) throws Exception {
String content = this.getCharacterVDBResource(resourcePath);
if (content != null) {
return content.getBytes();
}
return null;
}
@Override
public String getCharacterVDBResource(String resourcePath) throws Exception {
IndexSelector selector = this.getIndexSelector();
// make sure the selector is initialized
try {
selector.getIndexes();
} catch (IOException e) {
throw new TeiidDesignerException(
e,
TransformationPlugin.Util.getString("TransformationMetadata.error_intialize_selector")); //$NON-NLS-1$
}
// look for the resource in only the first available indexSelector
// built in assumption is that first selector is always for the vdb logged in
if (selector instanceof CompositeIndexSelector) {
CompositeIndexSelector compSelector = (CompositeIndexSelector)selector;
List selectors = compSelector.getIndexSelectors();
if (selectors.size() > 0) {
IndexSelector firstSelector = (IndexSelector)selectors.get(0);
return firstSelector.getFileContentAsString(resourcePath);
}
}
return selector.getFileContentAsString(resourcePath);
}
@Override
public String[] getVDBResourcePaths() throws Exception {
IndexSelector selector = this.getIndexSelector();
// make sure the selector is initialized
try {
selector.getIndexes();
} catch (IOException e) {
throw new TeiidDesignerException(
e,
TransformationPlugin.Util.getString("TransformationMetadata.error_intialize_selector")); //$NON-NLS-1$
}
// look for the resource in only the first available indexSelector
// built in assumption is that first selector is always for the vdb logged in
if (selector instanceof CompositeIndexSelector) {
CompositeIndexSelector compSelector = (CompositeIndexSelector)selector;
List selectors = compSelector.getIndexSelectors();
if (selectors.size() > 0) {
IndexSelector firstSelector = (IndexSelector)selectors.get(0);
return firstSelector.getFilePaths();
}
}
return selector.getFilePaths();
}
@Override
public String getModeledType(final Object elementID) throws Exception {
DatatypeRecord record = getDatatypeRecord(elementID);
if (record != null) {
return record.getDatatypeID();
}
return null;
}
@Override
public String getModeledBaseType(final Object elementID) throws Exception {
DatatypeRecord record = getDatatypeRecord(elementID);
if (record != null) {
return record.getBasetypeID();
}
return null;
}
@Override
public String getModeledPrimitiveType(final Object elementID) throws Exception {
DatatypeRecord record = getDatatypeRecord(elementID);
if (record != null) {
return record.getPrimitiveTypeID();
}
return null;
}
@Override
public Object getPrimaryKey(Object metadataID) {
CoreArgCheck.isInstanceOf(TableRecord.class, metadataID);
TableRecord tableRecord = (TableRecord)metadataID;
final String groupUUID = tableRecord.getUUID();
CoreArgCheck.isNotNull(groupUUID);
Collection pk;
try {
pk = findChildRecords(tableRecord, IndexConstants.RECORD_TYPE.PRIMARY_KEY);
} catch (Exception e) {
throw new TeiidDesignerRuntimeException(e);
}
if (pk.size() > 1) {
throw new TeiidDesignerRuntimeException("Multiple primary keys for table"); //$NON-NLS-1$
}
return pk.iterator().next();
}
@Override
public String getName(Object metadataID) {
CoreArgCheck.isInstanceOf(MetadataRecord.class, metadataID);
MetadataRecord metadataRecord = (MetadataRecord)metadataID;
return metadataRecord.getName();
}
@Override
public IFunctionLibrary getFunctionLibrary() {
return UdfManager.getInstance().getFunctionLibrary();
}
// ==================================================================================
// P R O T E C T E D M E T H O D S
// ==================================================================================
protected DatatypeRecord getDatatypeRecord(final Object elementID)
throws Exception {
if (elementID instanceof ColumnRecord) {
String uuid = ((ColumnRecord)elementID).getDatatypeUUID();
if (!CoreStringUtil.isEmpty(uuid)) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.DATATYPE, uuid, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the datatype record for this result
return (DatatypeRecord)results.iterator().next();
} else if (resultSize == 0) {
// there should be only one for the UUID
throw new Exception(uuid + NOT_EXISTS_MESSAGE);
} else {
// there should be only one for the UUID
throw new Exception(TransformationPlugin.Util.getString("TransformationMetadata.0", uuid)); //$NON-NLS-1$
}
}
return null;
} else if (elementID instanceof ProcedureParameterRecord) {
String uuid = ((ProcedureParameterRecord)elementID).getDatatypeUUID();
if (!CoreStringUtil.isEmpty(uuid)) {
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.DATATYPE, uuid, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the datatype record for this result
return (DatatypeRecord)results.iterator().next();
} else if (resultSize == 0) {
// there should be only one for the UUID
throw new Exception(uuid + NOT_EXISTS_MESSAGE);
} else {
// there should be only one for the UUID
throw new Exception(TransformationPlugin.Util.getString("TransformationMetadata.0", uuid)); //$NON-NLS-1$
}
}
return null;
} else {
throw createInvalidRecordTypeException(elementID);
}
}
/**
* Return the array of MtkIndex instances representing temporary indexes
*
* @param selector
* @return
* @throws TeiidComponentException
*/
protected Index[] getIndexes(final char recordType,
final IndexSelector selector) throws Exception {
try {
return selector.getIndexes();
} catch (IOException e) {
throw new TeiidDesignerException(
e,
TransformationPlugin.Util.getString("TransformationMetadata.Error_trying_to_obtain_index_file_using_IndexSelector_1", selector)); //$NON-NLS-1$
}
}
/**
* Return the pattern match string that could be used to match a UUID in a datatype index record. The RECORD_TYPE.DATATYPE
* records contain a header portion of the form: recordType|datatypeID|basetypeID|fullName|objectID|nameInSource|...
*
* @param uuid The UUID for which the pattern match string is to be constructed.
* @return The pattern match string of the form: recordType|*|*|*|uuid|*
*/
protected String getDatatypeUUIDMatchPattern(final String uuid) {
CoreArgCheck.isNotNull(uuid);
String uuidString = uuid;
if (CoreStringUtil.startsWithIgnoreCase(uuid, UUID.PROTOCOL)) {
uuidString = uuid.toLowerCase();
}
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ IndexConstants.RECORD_TYPE.DATATYPE // recordType
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER + IndexConstants.RECORD_STRING.MATCH_CHAR // datatypeID
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER + IndexConstants.RECORD_STRING.MATCH_CHAR // basetypeID
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER + IndexConstants.RECORD_STRING.MATCH_CHAR // fullName
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER + uuidString // objectID
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER + IndexConstants.RECORD_STRING.MATCH_CHAR;
return patternStr;
}
/**
* Return the pattern match string that could be used to match a UUID in an index record. All index records contain a header
* portion of the form: recordType|pathInModel|UUID|nameInSource|parentObjectID|
*
* @param uuid The UUID for which the pattern match string is to be constructed.
* @return The pattern match string of the form: recordType|*|uuid|*
*/
protected String getUUIDMatchPattern(final char recordType,
final String uuid) {
CoreArgCheck.isNotNull(uuid);
String uuidString = uuid;
if (CoreStringUtil.startsWithIgnoreCase(uuid, UUID.PROTOCOL)) {
uuidString = uuid.toLowerCase();
}
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ recordType
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER
+ IndexConstants.RECORD_STRING.MATCH_CHAR
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER
+ uuidString
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER
+ IndexConstants.RECORD_STRING.MATCH_CHAR;
return patternStr;
}
/**
* Return the pattern match string that could be used to match a partially/fully qualified entity name in an index record. All
* index records contain a header portion of the form: recordType|pathInModel|UUID|nameInSource|parentObjectID|
*
* @param name The partially/fully qualified name for which the pattern match string is to be constructed.
* @return The pattern match string of the form: recordType|*name|*
*/
protected String getMatchPattern(final char recordType,
final String name) {
CoreArgCheck.isNotNull(name);
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ recordType
+ IndexConstants.RECORD_STRING.RECORD_DELIMITER
+ IndexConstants.RECORD_STRING.MATCH_CHAR;
if (name != null) {
patternStr = patternStr + name.trim().toUpperCase() + IndexConstants.RECORD_STRING.RECORD_DELIMITER
+ IndexConstants.RECORD_STRING.MATCH_CHAR;
}
return patternStr;
}
/**
* Return the prefix match string that could be used to exactly match a fully qualified entity name in an index record. All
* index records contain a header portion of the form: recordType|pathInModel|UUID|nameInSource|parentObjectID|
*
* @param name The fully qualified name for which the prefix match string is to be constructed.
* @return The pattern match string of the form: recordType|name|
*/
protected String getPrefixPattern(final char recordType,
final String name) {
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ recordType + IndexConstants.RECORD_STRING.RECORD_DELIMITER;
if (name != null) {
patternStr = patternStr + name.trim().toUpperCase() + IndexConstants.RECORD_STRING.RECORD_DELIMITER;
}
return patternStr;
}
/**
* Return the prefix match string that could be used to exactly match a uuid in an index record.
*
* @param uuid The uuid for which the prefix match string is to be constructed.
* @return The pattern match string of the form: recordType|uuid|
*/
protected String getUUIDPrefixPattern(final char recordType,
final String uuid) {
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ recordType + IndexConstants.RECORD_STRING.RECORD_DELIMITER;
if (uuid != null) {
patternStr = patternStr + uuid.trim() + IndexConstants.RECORD_STRING.RECORD_DELIMITER;
}
return patternStr;
}
/**
* Return the prefix match string that could be used to exactly match a fully qualified entity name in an index record. All
* index records contain a header portion of the form: recordType|pathInModel|UUID|nameInSource|parentObjectID|
*
* @param name The fully qualified name for which the prefix match string is to be constructed.
* @return The pattern match string of the form: recordType|name|
*/
protected String getParentPrefixPattern(final char recordType,
final String name) {
// construct the pattern string
String patternStr = "" //$NON-NLS-1$
+ recordType + IndexConstants.RECORD_STRING.RECORD_DELIMITER;
if (name != null) {
patternStr = patternStr + name.trim().toUpperCase() + DELIMITER_CHAR;
}
return patternStr;
}
/**
* Get a MetadataRecord object given a entityName/UUID.
*
* @param entityName String representing an entity, may be null(vdbs)
* @param recordType The record type for the entity
* @return A MetadataRecord object for a given entityName/UUID
* @throws Exception
*/
protected MetadataRecord getRecordByType(final String entityName,
final char recordType) throws Exception {
// Query the index files
Collection results = findMetadataRecords(recordType, entityName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the columnset record for this result
return (MetadataRecord)results.iterator().next();
} else if (resultSize == 0) {
// there should be only one for the UUID
throw new Exception(entityName + NOT_EXISTS_MESSAGE);
} else {
// there should be only one for the UUID
throw new Exception(TransformationPlugin.Util.getString("TransformationMetadata.0", entityName)); //$NON-NLS-1$
}
}
/**
* Remove any MetadataRecord instances that do not match the specified uuid Due to the pattern matching used to query index
* files if an index record matched the specified uuid string anywhere in that record it would be returned in the results (for
* example, if the parent ObjectID in the index record matched the specified uuid).
*
* @param uuid
* @param records
* @since 4.2
*/
protected void filterMetadataRecordForUUID(final String uuid,
Collection records) {
if (uuid != null && records != null) {
for (final Iterator iter = records.iterator(); iter.hasNext();) {
final MetadataRecord record = (MetadataRecord)iter.next();
if (record == null || !uuid.equals(record.getUUID())) {
iter.remove();
}
}
}
}
/**
* Return the IndexSelector reference
*
* @return
*/
protected IndexSelector getIndexSelector() {
return this.context.getIndexSelector();
}
/**
* Return the QueryMetadataContext reference
*
* @return
*/
protected QueryMetadataContext getContext() {
return this.context;
}
/**
* Return all index file records that match the specified entity name, filtering by matching on parent uuid
*
* @param parentRecord
* @param childRecordType the type of the child to seek uuids
* @param uuids to filter just the objects we want
* @return columnRecords
* @throws Exception
*/
protected Collection findChildRecords(final MetadataRecord parentRecord,
final char childRecordType) throws Exception {
IEntryResult[] results = queryIndexByParentPath(childRecordType, parentRecord.getFullName());
Collection records = findMetadataRecords(results);
// if uniquekey records are being returned, also return primary key records,
// as primary keys are unique keys
if (childRecordType == IndexConstants.RECORD_TYPE.UNIQUE_KEY) {
Collection primarKeyRecords = findMetadataRecords(queryIndexByParentPath(IndexConstants.RECORD_TYPE.PRIMARY_KEY,
parentRecord.getFullName()));
records.addAll(primarKeyRecords);
}
// jh Case 5092: Moved filtering down into this method (findChildRecords()).
// (It used to be done in each method that calls this one.)
// filtering records (records with different parents could begin with same name)
final String groupUUID = parentRecord.getUUID();
List filteredRecords = new ArrayList(records.size());
for (Iterator resultsIter = records.iterator(); resultsIter.hasNext();) {
MetadataRecord record = (MetadataRecord)resultsIter.next();
String parentUUID = record.getParentUUID();
if (parentUUID != null && parentUUID.equalsIgnoreCase(groupUUID)) {
filteredRecords.add(record);
}
}
return filteredRecords;
}
/**
* Return all index file records that match the specified entity name, filtering by matching on the child uuids
*
* @param parentRecord
* @param childRecordType the type of the child to seek uuids
* @param uuids to filter just the objects we want
* @return columnRecords
* @throws Exception
*/
protected Collection findChildRecordsForColumns(final MetadataRecord parentRecord,
final char childRecordType,
final List uuids) throws Exception {
IEntryResult[] results = queryIndexByParentPath(childRecordType, parentRecord.getFullName());
Collection records = findMetadataRecords(results);
// if uniquekey records are being returned, also return primary key records,
// as primary keys are unique keys
if (childRecordType == IndexConstants.RECORD_TYPE.UNIQUE_KEY) {
Collection primarKeyRecords = findMetadataRecords(queryIndexByParentPath(IndexConstants.RECORD_TYPE.PRIMARY_KEY,
parentRecord.getFullName()));
records.addAll(primarKeyRecords);
}
List columnRecords = new ArrayList(uuids.size());
// filtering recods (records with differrent parents could
// begin with same name)
for (Iterator resultIter = records.iterator(); resultIter.hasNext();) {
MetadataRecord record = (MetadataRecord)resultIter.next();
if (record != null && uuids.contains(record.getUUID())) {
columnRecords.add(record);
}
}
return columnRecords;
}
/**
* Return all index file records that match the specified entity name, without filtering
*
* @param parentRecord
* @param childRecordType the type of the child to seek
* @return records
* @throws Exception
*/
protected Collection findChildRecordsWithoutFiltering(final MetadataRecord parentRecord,
final char childRecordType) throws Exception {
IEntryResult[] results = queryIndexByParentPath(childRecordType, parentRecord.getFullName());
Collection records = findMetadataRecords(results);
// if uniquekey records are being returned, also return primary key records,
// as primary keys are unique keys
if (childRecordType == IndexConstants.RECORD_TYPE.UNIQUE_KEY) {
Collection primarKeyRecords = findMetadataRecords(queryIndexByParentPath(IndexConstants.RECORD_TYPE.PRIMARY_KEY,
parentRecord.getFullName()));
records.addAll(primarKeyRecords);
}
return records;
}
/**
* Return all index file records that match the specified entity name
*
* @param indexName
* @param entityName the name to match
* @param isPartialName true if the entity name is a partially qualified
* @return results
* @throws Exception
*/
protected Collection findMetadataRecords(final IEntryResult[] results) {
return RecordFactory.getMetadataRecord(results, null);
}
protected MetadataRecord findMetadataRecord(final IEntryResult result) {
return RecordFactory.getMetadataRecord(result, null);
}
/**
* Return all index file records that match the specified entity name
*
* @param indexName
* @param entityName the name to match
* @param isPartialName true if the entity name is a partially qualified
* @return results
* @throws Exception
*/
protected Collection findMetadataRecords(final char recordType,
final String entityName,
final boolean isPartialName) throws Exception {
IEntryResult[] results = queryIndex(recordType, entityName, isPartialName);
Collection records = findMetadataRecords(results);
if (CoreStringUtil.startsWithIgnoreCase(entityName, UUID.PROTOCOL)) {
// Filter out ColumnRecord instances that do not match the specified uuid.
// Due to the pattern matching used to query index files if an index record
// matched the specified uuid string anywhere in that record it would be returned
// in the results (for example, if the parent ObjectID in the index record
// matched the specified uuid).
this.filterMetadataRecordForUUID(entityName, records);
}
return records;
}
/**
* Return all index file records that match the specified entity name
*
* @param indexName
* @param entityName the name to match
* @param isPartialName true if the entity name is a partially qualified
* @return results
* @throws Exception
*/
protected IEntryResult[] queryIndex(final char recordType,
final String entityName,
final boolean isPartialName) throws Exception {
IEntryResult[] results = null;
Index[] indexes = getIndexes(recordType, getIndexSelector());
// Query based on UUID
if (CoreStringUtil.startsWithIgnoreCase(entityName, UUID.PROTOCOL)) {
String patternString = null;
if (recordType == IndexConstants.RECORD_TYPE.DATATYPE) {
patternString = getDatatypeUUIDMatchPattern(entityName);
} else {
patternString = getUUIDMatchPattern(recordType, entityName);
}
results = queryIndex(indexes, patternString.toCharArray(), false, true);
}
// Query based on partially qualified name
else if (isPartialName) {
String patternString = getMatchPattern(recordType, entityName);
results = queryIndex(indexes, patternString.toCharArray(), false, false);
}
// Query based on fully qualified name
else {
String prefixString = getPrefixPattern(recordType, entityName);
results = queryIndex(indexes, prefixString.toCharArray(), true, true);
}
return results;
}
/**
* Return all index file records that match the specified record pattern.
*
* @param indexes the array of MtkIndex instances to query
* @param pattern
* @return results
* @throws Exception
*/
protected IEntryResult[] queryIndex(final Index[] indexes,
final char[] pattern,
boolean isPrefix,
boolean returnFirstMatch) throws Exception {
return SimpleIndexUtil.queryIndex(indexes, pattern, isPrefix, returnFirstMatch);
}
/**
* Return all index file records that match the specified record pattern.
*
* @param indexes the array of MtkIndex instances to query
* @param pattern
* @return results
* @throws Exception
*/
protected IEntryResult[] queryIndex(final Index[] indexes,
final char[] pattern,
boolean isPrefix,
boolean isCaseSensitive,
boolean returnFirstMatch) throws Exception {
return SimpleIndexUtil.queryIndex(null, indexes, pattern, isPrefix, isCaseSensitive, returnFirstMatch);
}
// ==================================================================================
// P A C K A G E M E T H O D S
// ==================================================================================
// ==================================================================================
// P R I V A T E M E T H O D S
// ==================================================================================
/**
* Looks up procedure plan in the transformations index for a given procedure.
*/
private String getProcedurePlan(final String procedureName) throws Exception {
CoreArgCheck.isNotEmpty(procedureName);
// Query the index files
Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.PROC_TRANSFORM, procedureName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the transform record for this result
final TransformationRecord transformRecord = (TransformationRecord)results.iterator().next();
return transformRecord.getTransformation();
}
// there should be only one result entry for a fully qualified name
if (resultSize > 1) {
throw new Exception(
TransformationPlugin.Util.getString("TransformationMetadata.Procedure_ambiguous_there_are_multiple_procedure_plans_available_for_this_name___4") + procedureName); //$NON-NLS-1$
}
// no transfomation available, this may not be a virtual procedure
return null;
}
/**
* Helper method to get back an array of ColumnRecords given a list of UUIDs.
*/
private ColumnRecord[] getColumnRecordsForUUIDs(final List uuids)
throws Exception {
ColumnRecord[] columnRecords = new ColumnRecord[uuids.size()];
for (int i = 0; i < uuids.size(); i++) {
String colUUID = (String)uuids.get(i);
columnRecords[i] = (ColumnRecord)this.getElementID(colUUID);
}
return columnRecords;
}
/**
* Return all index file records that match the specified entity name
*
* @param indexName
* @param entityName the name to match
* @param isPartialName true if the entity name is a partially qualified
* @return results
* @throws Exception
*/
private IEntryResult[] queryIndexByParentPath(final char recordType,
final String parentFullName) throws Exception {
// Query based on fully qualified name
String prefixString = getParentPrefixPattern(recordType, parentFullName);
// Query the model index files
Index[] indexes = getIndexes(recordType, getIndexSelector());
IEntryResult[] results = queryIndex(indexes, prefixString.toCharArray(), true, false);
return results;
}
private IStoredProcedureInfo getStoredProcInfoDirect(final String procedureName) throws Exception {
CoreArgCheck.isNotEmpty(procedureName);
ProcedureRecord procRecord = null;
IDataTypeManagerService dataTypeService = ModelerCore.getTeiidDataTypeManagerService();
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
// procedure full names always contain atlest 2 segments(modelname.procedureName)
if (CoreStringUtil.startsWithIgnoreCase(procedureName, UUID.PROTOCOL)
|| CoreStringUtil.getTokens(procedureName, DELIMITER_STRING).size() >= 2) {
final Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.CALLABLE, procedureName, false);
int resultSize = results.size();
if (resultSize == 1) {
// get the columnset record for this result
procRecord = (ProcedureRecord)results.iterator().next();
} else if (resultSize == 0) {
if (CoreStringUtil.startsWithIgnoreCase(procedureName, UUID.PROTOCOL)) {
return null;
}
} else {
// there should be only one for the full name
throw new Exception(TransformationPlugin.Util.getString("TransformationMetadata.0", procedureName)); //$NON-NLS-1$
}
}
if (procRecord == null) {
String partialName = DELIMITER_CHAR + procedureName;
final Collection results = findMetadataRecords(IndexConstants.RECORD_TYPE.CALLABLE, partialName, true);
int resultSize = results.size();
if (resultSize == 1) {
// get the columnset record for this result
procRecord = (ProcedureRecord)results.iterator().next();
} else if (resultSize == 0) {
throw new Exception(procedureName + NOT_EXISTS_MESSAGE);
} else {
// there should be only one for the UUID
throw new Exception(TransformationPlugin.Util.getString("TransformationMetadata.0", procedureName)); //$NON-NLS-1$
}
}
String procedureFullName = procRecord.getFullName();
// create the storedProcedure info object that would hold procedure's metadata
IStoredProcedureInfo procInfo = factory.createStoredProcedureInfo();
procInfo.setProcedureCallableName(procedureFullName);
procInfo.setProcedureID(procRecord);
// modelID for the procedure
MetadataRecord modelRecord = (MetadataRecord)this.getModelID(procRecord);
procInfo.setModelID(modelRecord);
// get the parameter metadata info
for (Iterator paramIter = procRecord.getParameterIDs().iterator(); paramIter.hasNext();) {
String paramID = (String)paramIter.next();
ProcedureParameterRecord paramRecord = (ProcedureParameterRecord)this.getRecordByType(paramID,
IndexConstants.RECORD_TYPE.CALLABLE_PARAMETER);
String runtimeType = paramRecord.getRuntimeType();
ISPParameter.ParameterInfo direction = this.convertParamRecordTypeToStoredProcedureType(paramRecord.getType());
// create a parameter and add it to the procedure object
ISPParameter spParam = factory.createSPParameter(paramRecord.getPosition(), direction, paramRecord.getFullName());
spParam.setMetadataID(paramRecord);
spParam.setClassType(dataTypeService.getDataTypeClass(runtimeType));
procInfo.addParameter(spParam);
}
// if the procedure returns a resultSet, obtain resultSet metadata
String resultID = (String)procRecord.getResultSetID();
if (resultID != null) {
try {
ColumnSetRecord resultRecord = (ColumnSetRecord)this.getRecordByType(resultID,
IndexConstants.RECORD_TYPE.RESULT_SET);
// resultSet is the last parameter in the procedure
int lastParamIndex = procInfo.getParameters().size() + 1;
ISPParameter param = factory.createSPParameter(lastParamIndex,
ISPParameter.ParameterInfo.RESULT_SET,
resultRecord.getFullName());
param.setClassType(java.sql.ResultSet.class);
param.setMetadataID(resultRecord);
ColumnRecord[] columnRecords = getColumnRecordsForUUIDs(resultRecord.getColumnIDs());
for (int i = 0; i < columnRecords.length; i++) {
String colType = columnRecords[i].getRuntimeType();
param.addResultSetColumn(columnRecords[i].getFullName(),
dataTypeService.getDataTypeClass(colType),
columnRecords[i]);
}
procInfo.addParameter(param);
} catch (Exception e) {
// it is ok to fail here. it will happen when a
// virtual stored procedure is created from a
// physical stored procedrue without a result set
// TODO: find a better fix for this
}
}
// if this is a virtual procedure get the procedure plan
if (procRecord.isVirtual()) {
String procedurePlan = getProcedurePlan(procedureFullName);
if (procedurePlan != null) {
IQueryNode queryNode = factory.createQueryNode(procedurePlan);
procInfo.setQueryPlan(queryNode);
}
}
// subtract 1, to match up with the server
procInfo.setUpdateCount(procRecord.getUpdateCount() - 1);
return procInfo;
}
/***
* Methods added below to support implementation in teiid runtime client
*/
@Override
public boolean isTemporaryTable(Object groupID) throws Exception {
return false;
}
@Override
public Object addToMetadataCache(Object metadataID, String key, Object value) throws Exception {
return null;
}
@Override
public Object getFromMetadataCache(Object metadataID, String key) throws Exception {
return null;
}
@Override
public boolean isScalarGroup(Object groupID) throws Exception {
return false;
}
@Override
public boolean isMultiSource(Object modelId) throws Exception {
return false;
}
@Override
public boolean isMultiSourceElement(Object elementId) throws Exception {
return false;
}
@Override
public IQueryMetadataInterface getDesignTimeMetadata() {
return this;
}
@Override
public IQueryMetadataInterface getSessionMetadata() {
return null;
}
@Override
public Set getImportedModels() {
return Collections.emptySet();
}
@Override
public ScriptEngine getScriptEngine(String langauge) throws Exception {
return null;
}
@Override
public boolean isVariadic(Object metadataID) {
return false;
}
@Override
public Map getFunctionBasedExpressions(Object metadataID) {
return null;
}
@Override
public boolean isPseudo(Object elementId) {
return false;
}
@Override
public Object getModelID(String modelName) throws Exception {
return null;
}
@Override
public String getExtensionProperty(Object metadataID, String key, boolean checkUnqualified) {
return null;
}
@Override
public boolean findShortName() {
return false;
}
@Override
public boolean useOutputName() {
return false;
}
@Override
public boolean widenComparisonToString() {
return false;
}
}