/* * 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.validation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.core.designer.TeiidDesignerRuntimeException; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.container.Container; import org.teiid.designer.core.index.IndexSelector; import org.teiid.designer.core.index.ModelResourceIndexSelector; import org.teiid.designer.core.index.NullIndexSelector; import org.teiid.designer.core.index.TargetLocationIndexSelector; import org.teiid.designer.core.metamodel.aspect.AspectManager; import org.teiid.designer.core.metamodel.aspect.sql.SqlAspect; import org.teiid.designer.core.metamodel.aspect.sql.SqlColumnAspect; import org.teiid.designer.core.metamodel.aspect.sql.SqlColumnSetAspect; import org.teiid.designer.core.metamodel.aspect.sql.SqlDatatypeAspect; import org.teiid.designer.core.metamodel.aspect.sql.SqlProcedureAspect; import org.teiid.designer.core.metamodel.aspect.sql.SqlTableAspect; import org.teiid.designer.core.query.QueryValidationResult; import org.teiid.designer.core.query.QueryValidator; import org.teiid.designer.core.resource.EmfResource; import org.teiid.designer.core.validation.ValidationContext; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.metamodels.relational.Procedure; import org.teiid.designer.metamodels.relational.Table; import org.teiid.designer.metamodels.transformation.MappingClass; import org.teiid.designer.metamodels.transformation.SqlTransformation; import org.teiid.designer.metamodels.transformation.SqlTransformationMappingRoot; import org.teiid.designer.metamodels.webservice.Operation; import org.teiid.designer.query.IQueryFactory; import org.teiid.designer.query.IQueryParser; import org.teiid.designer.query.IQueryResolver; import org.teiid.designer.query.IQueryService; import org.teiid.designer.query.metadata.DelegatingQueryMetadataInterface; import org.teiid.designer.query.metadata.IQueryMetadataInterface; import org.teiid.designer.query.sql.IReferenceCollectorVisitor; import org.teiid.designer.query.sql.IResolverVisitor; import org.teiid.designer.query.sql.lang.ICommand; import org.teiid.designer.query.sql.lang.IExpression; import org.teiid.designer.query.sql.lang.util.CommandHelper; import org.teiid.designer.query.sql.symbol.IElementSymbol; import org.teiid.designer.query.sql.symbol.IGroupSymbol; import org.teiid.designer.transformation.TransformationPlugin; import org.teiid.designer.transformation.metadata.QueryMetadataContext; import org.teiid.designer.transformation.metadata.TransformationMetadataFacade; import org.teiid.designer.transformation.metadata.TransformationMetadataFactory; import org.teiid.designer.transformation.metadata.VdbMetadata; import org.teiid.designer.transformation.util.SqlMappingRootCache; import org.teiid.designer.transformation.util.TransformationHelper; import org.teiid.designer.transformation.util.TransformationSqlHelper; import org.teiid.designer.type.IDataTypeManagerService; import org.teiid.designer.validator.IUpdateValidator; import org.teiid.designer.validator.IUpdateValidator.TransformUpdateType; import org.teiid.designer.validator.IValidator; import org.teiid.designer.validator.IValidator.IValidatorFailure; import org.teiid.designer.validator.IValidator.IValidatorReport; /** * TransformationValidator Static methods for doing Validation on the transformation. * * @since 8.0 */ public class TransformationValidator implements QueryValidator { private static final boolean DEFAULT_USE_CACHING = true; /** Key used when setting the metadata context in the validation context. */ public static final String QUERY_METADATA_INTERFACE = TransformationValidator.class.getSimpleName() + ".QUERY_METADATA_INTERFACE"; //$NON-NLS-1$ // toolkit metadata private IQueryMetadataInterface metadata; private final boolean restrictSearch; private final SqlTransformationMappingRoot mappingRoot; private final EObject targetGroup; private final ValidationContext validationContext; // private String indexFilePath = IndexUtil.INDEX_PATH; private static final String XML_URI = "http://www.metamatrix.com/metamodels/XmlDocument"; //$NON-NLS-1$ private ElementSymbolOptimization elementSymbolOptimization = ElementSymbolOptimization.UNMODIFIED; // ================================================================================== // C O N S T R U C T O R S // ================================================================================== /** * Constructor for TransformationValidator * * @param eObject The EObject used to contruct metadata instance used by the validator */ public TransformationValidator( final SqlTransformationMappingRoot eObject ) { this(eObject, null, DEFAULT_USE_CACHING, false); } /** * Constructor for TransformationValidator * * @param eObject The EObject used to contruct metadata instance used by the validator * @param useCaching A boolean indicating if metadata caching should be enabled. */ public TransformationValidator( final SqlTransformationMappingRoot eObject, final boolean useCaching ) { this(eObject, null, useCaching, false); } /** * Constructor for TransformationValidator * * @param eObject The EObject used to contruct metadata instance used by the validator * @param useCaching A boolean indicating if metadata caching should be enabled. * @param restrictSearch A boolean indicating if the search needs to be restricted to model imports or if the whole workspace * needs to be searched */ public TransformationValidator( final SqlTransformationMappingRoot eObject, final boolean useCaching, final boolean restrictSearch ) { this(eObject, null, useCaching, restrictSearch); } /** * Constructor for TransformationValidator * * @param eObject The EObject used to contruct metadata instance used by the validator * @param context The ValidationContext to use * @param useCaching A boolean indicating if metadata caching should be enabled. * @param restrictSearch A boolean indicating if the search needs to be restricted to model imports or if the whole workspace * needs to be searched */ public TransformationValidator( final SqlTransformationMappingRoot eObject, final ValidationContext context, final boolean useCaching, final boolean restrictSearch ) { this.restrictSearch = restrictSearch; this.mappingRoot = eObject; this.targetGroup = eObject.getTarget(); this.validationContext = context; } // ================================================================================== // P U B L I C M E T H O D S // ================================================================================== /** * This method does a validation on all of the transformatin SQL Strings (SELECT, INSERT, UPDATE, DELETE). The result of the * validation is returned as a TransformationValidationResult object. * * @param transformation the SqlTransformation * @return the TransformationValidationResult object */ public TransformationValidationResult validateTransformation() { // If this is a XML Document SOURCE model, return EmfResource emfResource = (EmfResource)this.mappingRoot.eResource(); if (emfResource.getModelAnnotation() != null) { ModelType type = emfResource.getModelAnnotation().getModelType(); String stringURI = emfResource.getModelAnnotation().getPrimaryMetamodelUri(); if (type.equals(ModelType.LOGICAL_LITERAL) && XML_URI.equals(stringURI)) { return new TransformationValidationResult(); } } // User defined functions do not need SQL and shouldn't be validated if( !shouldValidate() ) return new TransformationValidationResult(); // validate the SQLTransformation on the mapping root // get the UUID form of the SqlTransformation SqlTransformation sqlTrans = (SqlTransformation)this.mappingRoot.getHelper(); // get the path to the virtual group String objectPath = TransformationHelper.getSqlEObjectPath(this.targetGroup); // If no sql transform exists ... if (sqlTrans == null) { String msg = TransformationPlugin.Util.getString("TransformationValidator.Error_in_the_Sql_tranformation_for_1", objectPath); //$NON-NLS-1$ List statuses = new ArrayList(1); statuses.add(new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null)); return new TransformationValidationResult(statuses); } // create a transformation result and update it with various command results TransformationValidationResult transformResult = new TransformationValidationResult(); // validate the select sql on the mapping root SqlTransformationResult selectStatus = SqlMappingRootCache.getSqlTransformationStatus(this.mappingRoot, QueryValidator.SELECT_TRNS, restrictSearch, this.validationContext); if (selectStatus != null) { transformResult.setSelectResult(selectStatus); } else { String msg = TransformationPlugin.Util.getString("TransformationValidator.Found_problems_validating_transformation_defining_{0},_re-validate_in_the_transformation_editor._1", objectPath); //$NON-NLS-1$ List statuses = new ArrayList(1); statuses.add(new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null)); return new TransformationValidationResult(statuses); } /* Defect 11415 * Workspace query validation would try to resolve UUID versions of the queries initially. * We need to use UUID queries because they reflect the most current picture of the objects * in the modeler(Example: If an entity is renamed the user query on the MappingRoot would * point to the older entity name but the UUID of the entity would remain the same.), also * UUID queries get updated with changes in the worspace. * * Defect: 11627 * Whenever a UUID query is not resolvable, we convert the UUID query to user form and then * try to validate it. There are cases where a UUID query would be not be resolvable but a * user form of it would be, example: In a query that has one of its groups defined by a sub query * If the user types in the query below in the TransforMationEditor and validate * Select a, b, c from (select a, b, c from table) as x * The element on the outer query get resolved to TempMetadataID and do not have UUIDs defined, * so the UUID version of the query that gets saved on the MappingRoot would look like * Select a, b, c from (select UUID, UUID, UUID from UUID) as x * This query would not be resolvable, since the TempGroup x has 3 elements whose names are UUID's * but do not have a,b,c defined on it, when this query is converted back to user version the query * would be resolvable again. * * We do the same thing when validating Insert, Update and Delete transformations. * This logic is in the SqlMappingRootCache */ // ----------------------------------------------------------- // SELECT is Valid - check the remaining SQL (if allowed) // Update, Insert and Delete transforms exist only for Tables. // The transforma need to be validated only if the target is updatable. // ----------------------------------------------------------- if (transformResult.isValid() && org.teiid.designer.core.metamodel.aspect.sql.SqlAspectHelper.isUpdatableGroup(this.targetGroup)) { // ----------------------------------------------------------- // Validate the INSERT String // ----------------------------------------------------------- if (sqlTrans.isInsertAllowed()) { transformResult.setInsertResult(SqlMappingRootCache.getSqlTransformationStatus(mappingRoot, QueryValidator.INSERT_TRNS, restrictSearch, this.validationContext)); transformResult.setInsertAllowed(true); } // ----------------------------------------------------------- // Validate the UPDATE String // ----------------------------------------------------------- if (sqlTrans.isUpdateAllowed()) { transformResult.setUpdateResult(SqlMappingRootCache.getSqlTransformationStatus(mappingRoot, QueryValidator.UPDATE_TRNS, restrictSearch, this.validationContext)); transformResult.setUpdateAllowed(true); } // ----------------------------------------------------------- // Validate the DELETE String // ----------------------------------------------------------- if (sqlTrans.isDeleteAllowed()) { transformResult.setDeleteResult(SqlMappingRootCache.getSqlTransformationStatus(mappingRoot, QueryValidator.DELETE_TRNS, restrictSearch, this.validationContext)); transformResult.setDeleteAllowed(true); } } // Return results return transformResult; } @Override public boolean isValidRoot() { return mappingRoot != null && mappingRoot.eResource() != null; } /** * This method attempts a complete validation on the supplied SQL string. The result of the validation is returned as a * CommandValidationResult object. * * @param sql the SQL String * @return the CommandValidationResult object */ @Override public QueryValidationResult validateSql( final String sql, final int transformType, final boolean cacheResult ) { if (!isValidRoot()) { return null; } // User defined functions do not need SQL and shouldn't be validated if( !shouldValidate()) return null; SqlTransformationResult commandValidationResult = null; switch( transformType ) { case QueryValidator.INSERT_TRNS: { if( ((SqlTransformation)this.mappingRoot.getHelper()).isInsertSqlDefault()) { IStatus status = new Status(IStatus.OK, TransformationPlugin.PLUGIN_ID, 0, "\"Use Default\" option is turned on for Insert procedure.", null); //$NON-NLS-1$ ICommand command = null; if( sql != null && sql.length() > 0 ) { SqlTransformationResult parsedResult = parseSQL(sql); command = parsedResult.getCommand(); } commandValidationResult = new SqlTransformationResult(command, status); commandValidationResult.setSqlString(sql); return commandValidationResult; } } break; case QueryValidator.UPDATE_TRNS: { if( ((SqlTransformation)this.mappingRoot.getHelper()).isUpdateSqlDefault()) { IStatus status = new Status(IStatus.OK, TransformationPlugin.PLUGIN_ID, 0, "\"Use Default\" option is turned on for UPDATE procedure.", null); //$NON-NLS-1$ ICommand command = null; if( sql != null && sql.length() > 0 ) { SqlTransformationResult parsedResult = parseSQL(sql); command = parsedResult.getCommand(); } commandValidationResult = new SqlTransformationResult(command, status); commandValidationResult.setSqlString(sql); return commandValidationResult; } } break; case QueryValidator.DELETE_TRNS: { if( ((SqlTransformation)this.mappingRoot.getHelper()).isDeleteSqlDefault()) { IStatus status = new Status(IStatus.OK, TransformationPlugin.PLUGIN_ID, 0, "\"Use Default\" option is turned on for DELETE procedure.", null); //$NON-NLS-1$ ICommand command = null; if( sql != null && sql.length() > 0 ) { SqlTransformationResult parsedResult = parseSQL(sql); command = parsedResult.getCommand(); } commandValidationResult = new SqlTransformationResult(command, status); commandValidationResult.setSqlString(sql); return commandValidationResult; } } break; } commandValidationResult = parseSQL(sql); if (commandValidationResult.isParsable() && transformType != UNKNOWN_TRNS) { ICommand command = commandValidationResult.getCommand(); // resolve command commandValidationResult = resolveCommand(command, transformType); // aTODO commandValidationResult = resolveCommand(command, transformType, this.targetGroup); if (commandValidationResult.isResolvable()) { if (this.elementSymbolOptimization == ElementSymbolOptimization.DEOPTIMIZED) { IQueryService queryService = ModelerCore.getTeiidQueryService(); queryService.fullyQualifyElements(command); } // validate command commandValidationResult = validateCommand(command, transformType); commandValidationResult.setResolvable(true); } } // set other info commandValidationResult.setSqlString(sql); commandValidationResult.setSourceGroups(this.mappingRoot.getInputs()); // cache the result if needed if (cacheResult) { SqlMappingRootCache.setStatus(this.mappingRoot, transformType, commandValidationResult); } return commandValidationResult; } /** * This method attempts to parse the supplied SQL string. The result is returned as a SqlTransformationResult object. * * @param sqlString the SQL to parse * @return the SqlTransformationResult object */ public static SqlTransformationResult parseSQL( final String sqlString ) { ICommand command = null; IStatus status = null; if (sqlString == null || sqlString.trim().isEmpty()) { String msg = TransformationPlugin.Util.getString("TransformationValidator.emptySQLMessage"); //$NON-NLS-1$ status = new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } else { try { // QueryParser is not thread-safe, get new parser each time IQueryParser parser = ModelerCore.getTeiidQueryService().getQueryParser(); command = parser.parseDesignerCommand(sqlString); } catch (Throwable e) { status = new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, e.getMessage(), e); } } final SqlTransformationResult result = new SqlTransformationResult(command, status); if (status != null && !status.isOK()) { result.setParsable(false); } return result; } /** * This method attempts to resolve the supplied Command language object. The result is returned as a SqlTransformationResult * object. * * @param command the Command languageObject * @param externalMetadata the externalMetadata required to resolve the command * @return the SqlTransformationResult object */ public SqlTransformationResult resolveCommand( final ICommand command, final int transformType ) { IStatus status = checkCommandType(command, transformType, this.targetGroup); if (status != null && status.getSeverity() != IStatus.OK) { SqlTransformationResult resolverResult = new SqlTransformationResult(command, status); resolverResult.setTargetValidStatus(status); return resolverResult; } // resolve the command CoreArgCheck.isNotNull(command); String commandSQL = command.toString(); // ------------------------------------------------------------ // Resolve the Command // ------------------------------------------------------------ IQueryService queryService = ModelerCore.getTeiidQueryService(); IResolverVisitor resolverVisitor = queryService.getResolverVisitor(); IQueryFactory factory = queryService.createQueryFactory(); IQueryResolver queryResolver = queryService.getQueryResolver(); try { // Attempt to resolve the command /* * Wrap the metadata so the findShortName flag can be overridden */ final IQueryMetadataInterface metadata = new DelegatingQueryMetadataInterface(getQueryMetadata()) { @Override public boolean findShortName() { return elementSymbolOptimization == ElementSymbolOptimization.OPTIMIZED; } }; String targetFullName = TransformationHelper.getSqlEObjectFullName(this.targetGroup); IGroupSymbol gSymbol = factory.createGroupSymbol(targetFullName); if (this.elementSymbolOptimization == ElementSymbolOptimization.OPTIMIZED) { /* To be removed on removal of deprecated teiid client plugins */ resolverVisitor.setProperty(IResolverVisitor.SHORT_NAME, true); } int teiidCommandType = getTeiidCommandType(transformType); queryResolver.resolveCommand(command, gSymbol, teiidCommandType, metadata); // If unsuccessful, an exception is thrown queryResolver.postResolveCommand(command, gSymbol, teiidCommandType, metadata, getProjectedSymbols()); } catch (Exception e) { // create status status = new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, e.getMessage(), e); } finally { /* To be removed on removal of deprecated teiid client plugins */ resolverVisitor.setProperty(IResolverVisitor.SHORT_NAME, false); } if (status != null && status.getSeverity() == IStatus.ERROR) { return new SqlTransformationResult(parseSQL(commandSQL).getCommand(), status); } SqlTransformationResult resolverResult = new SqlTransformationResult(command, status); // set resolvable if (status == null) { resolverResult.setResolvable(true); } return resolverResult; } private int getTeiidCommandType(int cmdtype) { switch (cmdtype) { case QueryValidator.SELECT_TRNS: if (this.targetGroup instanceof Table || this.targetGroup instanceof MappingClass) { return ICommand.TYPE_QUERY; } return ICommand.TYPE_STORED_PROCEDURE; case QueryValidator.INSERT_TRNS: return ICommand.TYPE_INSERT; case QueryValidator.UPDATE_TRNS: return ICommand.TYPE_UPDATE; case QueryValidator.DELETE_TRNS: return ICommand.TYPE_DELETE; } return ICommand.TYPE_UNKNOWN; } /** * This method does a validation on the supplied Command language object. The result is returned as a SqlTransformationResult * object. * * @param command the Command languageObject * @return the SqlTransformationResult object */ public SqlTransformationResult validateCommand( final ICommand command, final int cmdType) { CoreArgCheck.isNotNull(command); Collection<IStatus> statusList = null; Collection<IStatus> updateStatusList = null; IQueryService queryService = ModelerCore.getTeiidQueryService(); try { // Validate IValidator validator = queryService.getValidator(); IValidatorReport report = validator.validate(command, getQueryMetadata()); // If Validation report has nothing, command is valid if (!report.hasItems()) { // If no report items - validation is successful // Check sources (target can't be a source) statusList = validateSources(command, statusList); // validate references statusList = validateReferences(command, statusList); } else { statusList = createStatusList(report); } // handle exception } catch (Exception e) { // Add exception to the problems list statusList = new ArrayList<IStatus>(1); statusList.add(new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, e.getMessage(), e)); } boolean validateAndResolve = false; if (statusList == null || statusList.isEmpty()) { statusList = Collections.EMPTY_LIST; validateAndResolve = true; } // If Select SQL is OK, now we need to check if any of the update SQL are using "Default" and run // additional validation boolean allowsUpdates = TransformationHelper.tableSupportsUpdate(targetGroup); if( validateAndResolve && cmdType == QueryValidator.SELECT_TRNS && allowsUpdates) { updateStatusList = new ArrayList(); TransformUpdateType insertType = TransformUpdateType.INSTEAD_OF; TransformUpdateType updateType = TransformUpdateType.INSTEAD_OF; TransformUpdateType deleteType = TransformUpdateType.INSTEAD_OF; SqlTransformation transformation = (SqlTransformation)mappingRoot.getHelper(); boolean doUpdateValidation = false; if( transformation.isInsertSqlDefault() ) { insertType = TransformUpdateType.INHERENT; doUpdateValidation = true; } if( transformation.isUpdateSqlDefault() ) { updateType = TransformUpdateType.INHERENT; doUpdateValidation = true; } if( transformation.isDeleteSqlDefault() ) { deleteType = TransformUpdateType.INHERENT; doUpdateValidation = true; } if( doUpdateValidation ) { IUpdateValidator updateValidator = queryService.getUpdateValidator(metadata, insertType, updateType, deleteType ); List<IElementSymbol> elemSymbols = null; try { elemSymbols = getProjectedSymbols(); List<IExpression> theSymbols = CommandHelper.getProjectedSymbols(command); List<IExpression> symbols = new ArrayList(theSymbols.size()); symbols.addAll(theSymbols); if( !elemSymbols.isEmpty() && elemSymbols.size() == symbols.size() ) { updateValidator.validate(command, elemSymbols); } } catch (Exception e) { // Add exception to the problems list updateStatusList = new ArrayList<IStatus>(1); updateStatusList.add(new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, e.getMessage(), e)); } if (updateStatusList.isEmpty()) { if( insertType == TransformUpdateType.INHERENT) { updateStatusList.addAll(getReportStatusList(updateValidator.getInsertReport(), INSERT_SQL_PROBLEM)); } if( updateType == TransformUpdateType.INHERENT) { updateStatusList.addAll(getReportStatusList(updateValidator.getUpdateReport(), UPDATE_SQL_PROBLEM)); } if( deleteType == TransformUpdateType.INHERENT) { updateStatusList.addAll(getReportStatusList(updateValidator.getDeleteReport(), DELETE_SQL_PROBLEM)); } updateStatusList.addAll(getReportStatusList(updateValidator.getReport(), ALL_UPDATE_SQL_PROBLEM)); } } } SqlTransformationResult result = new SqlTransformationResult(command, statusList, updateStatusList); result.setValidatable(validateAndResolve); result.setResolvable(validateAndResolve); return result; } @Override public IQueryMetadataInterface getQueryMetadata() { return getDirectQueryMetadata(); } /** * Return the {@link org.teiid.query.metadata.QueryMetadataInterface} instance to use for query validation and * resolution. */ private IQueryMetadataInterface getDirectQueryMetadata() { if (this.metadata == null && this.mappingRoot.eResource() != null) { TransformationMetadataFactory factory = TransformationMetadataFactory.getInstance(); final boolean useServerMetadata = (this.validationContext != null && this.validationContext.useServerIndexes()); // Validating within the modeler workspace if (!useServerMetadata) { // create a indexSelector for this resource and instantiate transformation validator ModelResourceIndexSelector selector = new ModelResourceIndexSelector(this.mappingRoot.eResource()); QueryMetadataContext queryContext = new QueryMetadataContext(selector); queryContext.setRestrictedSearch(this.restrictSearch); Container container = null; if (this.validationContext != null) { container = this.validationContext.getResourceContainer(); if (!this.validationContext.useIndexesToResolve()) { queryContext.setIndexSelector(new NullIndexSelector()); } queryContext.setResources(Arrays.asList(this.validationContext.getResourcesInScope())); } else { try { container = ModelerCore.getModelContainer(); // set the resource scope (all model resources in open model projects) try { ModelResource mr = ModelUtil.getModel(this.mappingRoot); IProject proj = mr.getCorrespondingResource().getProject(); Collection<Resource> projectResources = new ArrayList<Resource>(0); ModelUtil.collectResources(proj, projectResources); queryContext.setResources(projectResources); queryContext.setRestrictedSearch(true); // ModelWorkspace workspace = ModelerCore.getModelWorkspace(); // queryContext.setResources(Arrays.asList(workspace.getEmfResources())); } catch (RuntimeException e) { // If we are running in a non-workspace environement, just use the resource from the given // mappingRoot's resource set queryContext.setResources(this.mappingRoot.eResource().getResourceSet().getResources()); } } catch (CoreException e) { TransformationPlugin.Util.log(e); } } // defect 16567 - we cannot use caching since it prevents the MetadataRecords // from being updated with new values based on model changes. // this.metadata = factory.createCachingModelerMetadata(this.mappingRoot, this.restrictSearch); this.metadata = factory.getModelerMetadata(queryContext, container); // Validating within the vdb(server) context } else { CoreArgCheck.isNotNull(this.validationContext); IndexSelector selector = null; if (this.validationContext.useIndexesToResolve()) { if (this.validationContext.getData(QUERY_METADATA_INTERFACE) != null) { this.metadata = (IQueryMetadataInterface)this.validationContext.getData(QUERY_METADATA_INTERFACE); // make sure the type is what we think it should be. if (!(this.metadata instanceof VdbMetadata) && ((this.metadata instanceof TransformationMetadataFacade) && !(((TransformationMetadataFacade)this.metadata).getDelegate() instanceof VdbMetadata))) { throw new TeiidDesignerRuntimeException( TransformationPlugin.Util.getString("TransformationValidator.QMI_of_unexpected_type")); //$NON-NLS-1$ } } else { // The TargetLocationIndexSelector will gather all index files under a specified directory location. selector = new TargetLocationIndexSelector(this.validationContext.getIndexLocation()); } } else { selector = new NullIndexSelector(); } if (this.metadata == null) { QueryMetadataContext queryContext = new QueryMetadataContext(selector); queryContext.setResources(Arrays.asList(this.validationContext.getResourcesInScope())); queryContext.setRestrictedSearch(this.restrictSearch); this.metadata = factory.getVdbMetadata(queryContext, this.validationContext.getResourceContainer()); this.validationContext.setData(QUERY_METADATA_INTERFACE, this.metadata); } } } return this.metadata; } /** * Check if the command could be used to define the given target in the transformation. * * @param command the Command languageObject * @param transformType The type of transformation. * @param targetGroup The traget virtual group or procedure. * @return The status indicating if the command is valid. */ private IStatus checkCommandType( final ICommand command, final int transformType, final Object targetGroup ) { CoreArgCheck.isNotNull(command); CoreArgCheck.isNotNull(targetGroup); int cmdType = command.getType(); switch (transformType) { case QueryValidator.SELECT_TRNS: if (targetGroup instanceof Table) { if (cmdType != ICommand.TYPE_QUERY && cmdType != ICommand.TYPE_STORED_PROCEDURE) { // create validation problem and addition to the results String msg = TransformationPlugin.Util.getString("TransformationValidator.Query_defining_a_virtual_group_can_only_be_of_type_Select_or_Exec._1"); //$NON-NLS-1$ return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } } else if (targetGroup instanceof Procedure) { if (!(cmdType == ICommand.TYPE_UPDATE_PROCEDURE )) { // create validation problem and addition to the results String msg = TransformationPlugin.Util.getString("TransformationValidator.Query_defining_a_virtual_procedure_can_only_be_of_type_Virtual_procedure._2"); //$NON-NLS-1$ return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } } else if (targetGroup instanceof Operation) { if (cmdType != ICommand.TYPE_UPDATE_PROCEDURE) { // create validation problem and addition to the results String msg = TransformationPlugin.Util.getString("TransformationValidator.Query_defining_an_operation_can_only_be_of_type_Virtual_procedure._1"); //$NON-NLS-1$ return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } } break; case QueryValidator.INSERT_TRNS: case QueryValidator.UPDATE_TRNS: case QueryValidator.DELETE_TRNS: if (cmdType != ICommand.TYPE_TRIGGER_ACTION && !(cmdType == ICommand.TYPE_UPDATE_PROCEDURE )) { // create validation problem and addition to the results String msg = TransformationPlugin.Util.getString("TransformationValidator.Only_update_procedures_are_allowed_in_the_INSERT/UPDATE/DELETE_tabs._1"); //$NON-NLS-1$ return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } break; default: // create validation problem transformation type is illegal String msg = TransformationPlugin.Util.getString("TransformationValidator.Invalid_transformation_type,_only_allowed_transformations_are_select,_insert,_update,_delete_transforms._1"); //$NON-NLS-1$ return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, msg, null); } return null; } // ================================================================================== // P R O T E C T E D M E T H O D S // ================================================================================== /** * Private method for creating a List of Status objects from a ValidatorReport * * @param report the ValidatorReport * @return the List of Status */ private List<IStatus> createStatusList( final IValidatorReport report ) { if (report != null && report.hasItems()) { Collection<? extends IValidatorFailure> items = report.getItems(); List<IStatus> statusList = new ArrayList<IStatus>(items.size()); for (IValidatorFailure item : items) { int statusVal = IStatus.OK; switch (item.getStatus()) { case ERROR: statusVal = IStatus.ERROR; break; case WARNING: statusVal = IStatus.WARNING; } IStatus status = new Status(statusVal, TransformationPlugin.PLUGIN_ID, item.toString(), null); statusList.add(status); } return statusList; } return Collections.emptyList(); } /** * Get the output columns for the target of the given transformation mapping root. * * @param transRoot The mappingroot objects wholse targets columns are returned * @return The list of columns on the target. * @since 4.3 */ public static List getOutputColumns( final SqlTransformationMappingRoot transRoot ) { EObject target = transRoot.getTarget(); List outputColumns = null; SqlAspect sqlAspect = AspectManager.getSqlAspect(target); if (sqlAspect instanceof SqlTableAspect) { outputColumns = ((SqlTableAspect)sqlAspect).getColumns(target); } else if (sqlAspect instanceof SqlProcedureAspect) { SqlProcedureAspect procAspect = (SqlProcedureAspect)sqlAspect; EObject resultSet = (EObject)procAspect.getResult(target); if (resultSet != null) { SqlColumnSetAspect resultAspect = (SqlColumnSetAspect)AspectManager.getSqlAspect(resultSet); outputColumns = resultAspect.getColumns(resultSet); } else { outputColumns = Collections.EMPTY_LIST; } } return outputColumns; } private List<IElementSymbol> getProjectedSymbols() throws Exception { List<EObject> outputColumns = getOutputColumns(this.mappingRoot); List<IElementSymbol> projectedSymbols = new ArrayList<IElementSymbol>(outputColumns.size()); for (EObject outputColumn : outputColumns) { String outputColumnName = TransformationHelper.getSqlColumnName(outputColumn); SqlColumnAspect columnAspect = (SqlColumnAspect)AspectManager.getSqlAspect(outputColumn); EObject datatype = columnAspect.getDatatype(outputColumn); SqlDatatypeAspect typeAspect = datatype != null ? (SqlDatatypeAspect)AspectManager.getSqlAspect(datatype) : null; Class<?> targetType = null; if (typeAspect != null) { IDataTypeManagerService service = ModelerCore.getTeiidDataTypeManagerService(); targetType = service.getDataTypeClass(typeAspect.getRuntimeTypeName(datatype)); } IQueryService queryService = ModelerCore.getTeiidQueryService(); IQueryFactory factory = queryService.createQueryFactory(); IElementSymbol column = factory.createElementSymbol(outputColumnName); column.setType(targetType); column.setMetadataID(getQueryMetadata().getElementID(columnAspect.getFullName(outputColumn))); projectedSymbols.add(column); } return projectedSymbols; } private List<IStatus> getReportStatusList(IValidatorReport report, int errorCode) { Collection<? extends IValidatorFailure> items = report.getItems(); List<IStatus> statusList = new ArrayList<IStatus>(items.size()); for( IValidatorFailure item : items ) { int statusVal = IStatus.OK; switch (item.getStatus()) { case ERROR: statusVal = IStatus.ERROR; break; case WARNING: statusVal = IStatus.WARNING; } IStatus status = new Status(statusVal, TransformationPlugin.PLUGIN_ID, errorCode, item.getStatus().toString(), null); statusList.add(status); } return statusList; } /** * Check if the command has any references or ?. If so error out. * * @param command the Command languageObject * @param statuslist to which an error can be added * @return statuslist The updated status list */ private Collection<IStatus> validateReferences( final ICommand command, Collection<IStatus> statusList ) { IQueryService queryService = ModelerCore.getTeiidQueryService(); IReferenceCollectorVisitor referenceCollectorVisitor = queryService.getReferenceCollectorVisitor(); Collection references = referenceCollectorVisitor.findReferences(command); if (!references.isEmpty()) { statusList = statusList != null ? statusList : new ArrayList<IStatus>(1); IStatus status = new Status( IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, TransformationPlugin.Util.getString("TransformationValidator.The_transformation_contains_Reference_Symbols_(_),_which_is_not_allowed,_replace___with_a_constant,_element,_or_parameter_name._1"), null); //$NON-NLS-1$ statusList.add(status); } return statusList; } /** * Check if the command has any references or ?. If so error out. * * @param command the Command languageObject * @param statuslist to which an error can be added * @return statuslist The updated status list */ private Collection<IStatus> validateSources( final ICommand command, Collection<IStatus> statusList ) { if (isTargetASourceInCommand(command)) { statusList = statusList != null ? statusList : new ArrayList<IStatus>(1); String message = TransformationPlugin.Util.getString("TransformationValidator.errorTargetIsSourceMsg", ModelerCore.getModelEditor().getName(targetGroup)); //$NON-NLS-1$ IStatus status = new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID, 0, message, null); statusList.add(status); } return statusList; } protected boolean isTargetASourceInCommand( ICommand command ) { boolean result = false; Collection sourceSymbols = TransformationSqlHelper.getGroupSymbols(command); List sourceEObjects = TransformationSqlHelper.getGroupSymbolEObjects(sourceSymbols); if (!sourceEObjects.isEmpty() && mappingRoot != null) { EObject tRootTarget = mappingRoot.getTarget(); Iterator iter = sourceEObjects.iterator(); Object nextObj = null; while (iter.hasNext() && !result) { nextObj = iter.next(); if (nextObj.equals(tRootTarget)) result = true; } } return result; } @Override public EObject getTransformationRoot() { // TODO Auto-generated method stub return this.mappingRoot; } @Override public void setElementSymbolOptimization( ElementSymbolOptimization status ) { this.elementSymbolOptimization = status; } @Override public boolean shouldValidate() { // User defined functions do not need SQL and shouldn't be validated if( this.targetGroup instanceof Procedure ) { if( ((Procedure)this.targetGroup).isFunction()) return false; } return true; } }