/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.resolver.util; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.teiid.api.exception.query.InvalidFunctionException; import org.teiid.api.exception.query.QueryMetadataException; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.api.exception.query.UnresolvedSymbolDescription; import org.teiid.core.CoreConstants; import org.teiid.core.TeiidComponentException; import org.teiid.core.types.DataTypeManager; import org.teiid.core.types.DataTypeManager.DefaultDataClasses; import org.teiid.core.util.StringUtil; import org.teiid.query.QueryPlugin; import org.teiid.query.function.FunctionDescriptor; import org.teiid.query.function.FunctionLibrary; import org.teiid.query.function.FunctionLibrary.ConversionResult; import org.teiid.query.metadata.GroupInfo; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.TempMetadataID; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.LanguageVisitor; import org.teiid.query.sql.lang.*; import org.teiid.query.sql.navigator.PostOrderNavigator; import org.teiid.query.sql.proc.ExceptionExpression; import org.teiid.query.sql.symbol.*; import org.teiid.query.sql.symbol.ElementSymbol.DisplayMode; public class ResolverVisitor extends LanguageVisitor { public static final String TEIID_PASS_THROUGH_TYPE = "teiid:pass-through-type"; //$NON-NLS-1$ private static final String SYS_PREFIX = CoreConstants.SYSTEM_MODEL + '.'; private Collection<GroupSymbol> groups; private GroupContext externalContext; protected QueryMetadataInterface metadata; private TeiidComponentException componentException; private QueryResolverException resolverException; private Map<Function, QueryResolverException> unresolvedFunctions; private boolean findShortName; private List<ElementSymbol> matches = new ArrayList<ElementSymbol>(2); private List<GroupSymbol> groupMatches = new ArrayList<GroupSymbol>(2); private boolean hasUserDefinedAggregate; /** * Constructor for ResolveElementsVisitor. * * External groups are ordered from inner to outer most */ public ResolverVisitor(QueryMetadataInterface metadata, Collection<GroupSymbol> internalGroups, GroupContext externalContext) { this.groups = internalGroups; this.externalContext = externalContext; this.metadata = metadata; this.findShortName = metadata.findShortName(); } public void setGroups(Collection<GroupSymbol> groups) { this.groups = groups; } public void visit(ElementSymbol obj) { try { resolveElementSymbol(obj); } catch(QueryMetadataException e) { handleException(handleUnresolvedElement(obj, e.getMessage())); } catch(TeiidComponentException e) { handleException(e); } catch (QueryResolverException e) { handleException(e); } } private QueryResolverException handleUnresolvedElement(ElementSymbol symbol, String description) { UnresolvedSymbolDescription usd = new UnresolvedSymbolDescription(symbol.toString(), description); QueryResolverException e = new QueryResolverException(usd.getDescription()); e.setUnresolvedSymbols(Arrays.asList(usd)); return e; } private void resolveElementSymbol(ElementSymbol elementSymbol) throws TeiidComponentException, QueryResolverException { // already resolved if(elementSymbol.getMetadataID() != null) { return; } // look up group and element parts of the potentialID String groupContext = null; if (elementSymbol.getGroupSymbol() != null) { groupContext = elementSymbol.getGroupSymbol().getName(); } String elementShortName = elementSymbol.getShortName(); if (groupContext != null) { groupContext = elementSymbol.getGroupSymbol().getName(); try { if (findShortName && internalResolveElementSymbol(elementSymbol, null, elementShortName, groupContext)) { elementSymbol.setDisplayMode(DisplayMode.SHORT_OUTPUT_NAME); return; } } catch (QueryResolverException e) { //ignore } catch (QueryMetadataException e) { //ignore } } internalResolveElementSymbol(elementSymbol, groupContext, elementShortName, null); } private boolean internalResolveElementSymbol(ElementSymbol elementSymbol, String groupContext, String shortCanonicalName, String expectedGroupContext) throws TeiidComponentException, QueryResolverException { boolean isExternal = false; boolean groupMatched = false; GroupContext root = null; if (groups != null || externalContext != null) { if (groups != null) { root = new GroupContext(externalContext, groups); } if (root == null) { isExternal = true; root = externalContext; } } else { try { LinkedList<GroupSymbol> matchedGroups = new LinkedList<GroupSymbol>(); if (groupContext != null) { //assume that this is fully qualified Object groupID = this.metadata.getGroupID(groupContext); // No groups specified, so any group is valid GroupSymbol groupSymbol = new GroupSymbol(groupContext); groupSymbol.setMetadataID(groupID); matchedGroups.add(groupSymbol); } root = new GroupContext(null, matchedGroups); } catch(QueryMetadataException e) { // ignore } } matches.clear(); groupMatches.clear(); while (root != null) { Collection<GroupSymbol> matchedGroups = ResolverUtil.findMatchingGroups(groupContext, root.getGroups(), metadata); if (matchedGroups != null && !matchedGroups.isEmpty()) { groupMatched = true; resolveAgainstGroups(shortCanonicalName, matchedGroups); if (matches.size() > 1) { throw handleUnresolvedElement(elementSymbol, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31117, elementSymbol, groupMatches)); } if (matches.size() == 1) { break; } } root = root.getParent(); isExternal = true; } if (matches.isEmpty()) { if (groupMatched) { throw handleUnresolvedElement(elementSymbol, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31118, elementSymbol)); } throw handleUnresolvedElement(elementSymbol, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31119, elementSymbol)); } //copy the match information ElementSymbol resolvedSymbol = matches.get(0); GroupSymbol resolvedGroup = groupMatches.get(0); String oldName = elementSymbol.getOutputName(); if (expectedGroupContext != null && !ResolverUtil.nameMatchesGroup(expectedGroupContext, resolvedGroup.getName())) { return false; } elementSymbol.setIsExternalReference(isExternal); elementSymbol.setType(resolvedSymbol.getType()); elementSymbol.setMetadataID(resolvedSymbol.getMetadataID()); elementSymbol.setGroupSymbol(resolvedGroup); elementSymbol.setShortName(resolvedSymbol.getShortName()); if (metadata.useOutputName()) { elementSymbol.setOutputName(oldName); } return true; } private void resolveAgainstGroups(String elementShortName, Collection<GroupSymbol> matchedGroups) throws QueryMetadataException, TeiidComponentException { for (GroupSymbol group : matchedGroups) { GroupInfo groupInfo = ResolverUtil.getGroupInfo(group, metadata); ElementSymbol result = groupInfo.getSymbol(elementShortName); if (result != null) { matches.add(result); groupMatches.add(group); } } } public void visit(BetweenCriteria obj) { try { resolveBetweenCriteria(obj); } catch(QueryResolverException e) { handleException(e); } catch(TeiidComponentException e) { handleException(e); } } public void visit(CompareCriteria obj) { try { resolveCompareCriteria(obj); } catch(QueryResolverException e) { handleException(e); } } public void visit(MatchCriteria obj) { try { resolveMatchCriteria(obj); } catch(QueryResolverException e) { handleException(e); } } public void visit(SetCriteria obj) { try { resolveSetCriteria(obj); } catch(QueryResolverException e) { handleException(e); } } public void visit(SubqueryCompareCriteria obj) { if (obj.getCommand() != null) { try { obj.setLeftExpression(ResolverUtil.resolveSubqueryPredicateCriteria(obj.getLeftExpression(), obj, metadata)); } catch(QueryResolverException e) { handleException(e); } catch (TeiidComponentException e) { handleException(e); } } else { try { resolveQuantifiedCompareArray(obj); } catch (QueryResolverException e) { handleException(e); } } } private void resolveQuantifiedCompareArray(SubqueryCompareCriteria obj) throws QueryResolverException, AssertionError { Class<?> expressionType = obj.getArrayExpression().getType(); if (expressionType == null || !expressionType.isArray()) { throw new QueryResolverException(QueryPlugin.Event.TEIID31175, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31175, new Object[] { obj.getArrayExpression(), expressionType })); } Class<?> rightType = expressionType.getComponentType(); Expression leftExpression = obj.getLeftExpression(); setDesiredType(leftExpression, rightType, obj); if(leftExpression.getType().equals(rightType) ) { return; } // Try to apply an implicit conversion from one side to the other String leftTypeName = DataTypeManager.getDataTypeName(leftExpression.getType()); String rightTypeName = DataTypeManager.getDataTypeName(rightType); if (leftExpression.getType() == DataTypeManager.DefaultDataClasses.NULL) { obj.setLeftExpression(ResolverUtil.convertExpression(leftExpression, rightTypeName, metadata) ); return; } boolean leftChar = isCharacter(leftExpression, true); boolean rightChar = isCharacter(rightType, true); // Special cases when left expression is a constant if(leftExpression instanceof Constant && (!rightChar || leftChar)) { // Auto-convert constant string on left to expected type on right try { obj.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName, metadata)); return; } catch (QueryResolverException qre) { if (leftChar && !metadata.widenComparisonToString()) { throw qre; } } } // Try to apply a conversion generically if ((rightChar ^ leftChar) && !metadata.widenComparisonToString()) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, obj)); } if(ResolverUtil.canImplicitlyConvert(leftTypeName, rightTypeName)) { obj.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName, metadata) ); return; } throw new QueryResolverException(QueryPlugin.Event.TEIID30072, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30072, new Object[] { leftTypeName, rightTypeName, obj })); } public void visit(SubquerySetCriteria obj) { try { obj.setExpression(ResolverUtil.resolveSubqueryPredicateCriteria(obj.getExpression(), obj, metadata)); } catch(QueryResolverException e) { handleException(e); } catch (TeiidComponentException e) { handleException(e); } } public void visit(IsNullCriteria obj) { try { setDesiredType(obj.getExpression(), DefaultDataClasses.OBJECT, obj); } catch(QueryResolverException e) { handleException(e); } } @Override public void visit(IsDistinctCriteria isDistinctCriteria) { try { ResolverUtil.resolveGroup(isDistinctCriteria.getLeftRowValue(), metadata); ResolverUtil.resolveGroup(isDistinctCriteria.getRightRowValue(), metadata); } catch (QueryResolverException e) { handleException(e); } catch (TeiidComponentException e) { handleException(e); } } public void visit(Function obj) { try { resolveFunction(obj, this.metadata.getFunctionLibrary()); if (obj.isAggregate()) { hasUserDefinedAggregate = true; } } catch(QueryResolverException e) { if (QueryPlugin.Event.TEIID30069.name().equals(e.getCode()) || QueryPlugin.Event.TEIID30067.name().equals(e.getCode())) { if (unresolvedFunctions == null) { unresolvedFunctions = new LinkedHashMap<Function, QueryResolverException>(); } unresolvedFunctions.put(obj, e); } else { handleException(e); } } catch(TeiidComponentException e) { handleException(e); } } @Override public void visit(Array array) { try { if (array.getComponentType() != null) { String type = DataTypeManager.getDataTypeName(array.getComponentType()); for (int i = 0; i < array.getExpressions().size(); i++) { Expression expr = array.getExpressions().get(i); setDesiredType(expr, array.getComponentType(), array); if (array.getComponentType() != DefaultDataClasses.OBJECT) { array.getExpressions().set(i, ResolverUtil.convertExpression(expr, type, metadata)); } } } else { resolveComponentType(array); } } catch (QueryResolverException e) { handleException(e); } } public static void resolveComponentType(Array array) { Class<?> type = null; for (int i = 0; i < array.getExpressions().size(); i++) { Expression expr = array.getExpressions().get(i); Class<?> baseType = expr.getType(); while (baseType != null && baseType.isArray()) { baseType = baseType.getComponentType(); } if (baseType != DefaultDataClasses.NULL) { if (type == null) { type = expr.getType(); } else if (type != expr.getType()) { type = DataTypeManager.DefaultDataClasses.OBJECT; } } } if (type == null) { type = DataTypeManager.DefaultDataClasses.NULL; } array.setComponentType(type); } public void visit(CaseExpression obj) { try { resolveCaseExpression(obj); } catch(QueryResolverException e) { handleException(e); } } public void visit(SearchedCaseExpression obj) { try { resolveSearchedCaseExpression(obj); } catch(QueryResolverException e) { handleException(e); } } public void visit(SetClause obj) { String type = DataTypeManager.getDataTypeName(obj.getSymbol().getType()); try { setDesiredType(obj.getValue(), obj.getSymbol().getType(), obj); obj.setValue(ResolverUtil.convertExpression(obj.getValue(), type, metadata)); } catch(QueryResolverException e) { handleException(new QueryResolverException(e, QueryPlugin.Util.getString("SetClause.resolvingError", new Object[] {obj.getValue(), obj.getSymbol(), type}))); //$NON-NLS-1$ } } @Override public void visit(XMLSerialize obj) { try { obj.setExpression(ResolverUtil.convertExpression(obj.getExpression(), DataTypeManager.DefaultDataTypes.XML, metadata)); } catch (QueryResolverException e) { handleException(new QueryResolverException(e, QueryPlugin.Util.getString("XMLSerialize.resolvingError", obj))); //$NON-NLS-1$ } } @Override public void visit(XMLQuery obj) { try { ResolverUtil.setDesiredType(obj.getPassing(), obj); obj.compileXqueryExpression(); } catch (QueryResolverException e) { handleException(e); } } @Override public void visit(XMLExists obj) { visit(obj.getXmlQuery()); } @Override public void visit(XMLCast xmlCast) { String typeName = xmlCast.getTypeName(); try { xmlCast.setType(this.metadata.getDataTypeClass(typeName)); } catch (QueryMetadataException e) { handleException(e); } } @Override public void visit(QueryString obj) { try { obj.setPath(ResolverUtil.convertExpression(obj.getPath(), DataTypeManager.DefaultDataTypes.STRING, metadata)); for (DerivedColumn col : obj.getArgs()) { col.setExpression(ResolverUtil.convertExpression(col.getExpression(), DataTypeManager.DefaultDataTypes.STRING, metadata)); } } catch (QueryResolverException e) { handleException(new QueryResolverException(e, QueryPlugin.Util.getString("XMLQuery.resolvingError", obj))); //$NON-NLS-1$ } } @Override public void visit(ExpressionCriteria obj) { try { obj.setExpression(ResolverUtil.convertExpression(obj.getExpression(), DataTypeManager.DefaultDataTypes.BOOLEAN, metadata)); } catch (QueryResolverException e) { handleException(e); } } @Override public void visit(ExceptionExpression obj) { try { if (obj.getErrorCode() != null) { obj.setErrorCode(ResolverUtil.convertExpression(obj.getErrorCode(), DataTypeManager.DefaultDataTypes.INTEGER, metadata)); } obj.setMessage(ResolverUtil.convertExpression(obj.getMessage(), DataTypeManager.DefaultDataTypes.STRING, metadata)); if (obj.getSqlState() != null) { obj.setSqlState(ResolverUtil.convertExpression(obj.getSqlState(), DataTypeManager.DefaultDataTypes.STRING, metadata)); } checkException(obj.getParent()); } catch (QueryResolverException e) { handleException(e); } } public static void checkException(Expression obj) throws QueryResolverException { if (obj == null || obj instanceof ExceptionExpression) { return; } if (obj instanceof ElementSymbol) { ElementSymbol es = (ElementSymbol)obj; if (!(es.getMetadataID() instanceof TempMetadataID)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31120, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31120, obj)); } TempMetadataID tid = (TempMetadataID)es.getMetadataID(); if (tid.getType() != Exception.class) { throw new QueryResolverException(QueryPlugin.Event.TEIID31120, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31120, obj)); } } else if (obj instanceof Constant) { Constant c = (Constant)obj; if (!(c.getValue() instanceof Exception)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31120, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31120, obj)); } } else { throw new QueryResolverException(QueryPlugin.Event.TEIID31120, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31120, obj)); } } @Override public void visit(AggregateSymbol obj) { if (obj.getCondition() != null) { try { obj.setCondition(ResolverUtil.convertExpression(obj.getCondition(), DataTypeManager.DefaultDataTypes.BOOLEAN, metadata)); } catch (QueryResolverException e) { handleException(e); } } switch (obj.getAggregateFunction()) { case USER_DEFINED: visit((Function)obj); break; case LEAD: case LAG: if (obj.getArgs().length > 1) { //second arg must be an integer try { obj.getArgs()[1] = ResolverUtil.convertExpression(obj.getArg(1), DataTypeManager.DefaultDataTypes.INTEGER, metadata); } catch (QueryResolverException e) { handleException(e); } //first and third args must match type if (obj.getArgs().length > 2) { try { if (obj.getArgs()[0].getType() == DataTypeManager.DefaultDataClasses.NULL) { obj.getArgs()[0] = ResolverUtil.convertExpression(obj.getArg(0), DataTypeManager.getDataTypeName(obj.getArg(2).getType()), metadata); } else { obj.getArgs()[2] = ResolverUtil.convertExpression(obj.getArg(2), DataTypeManager.getDataTypeName(obj.getArg(0).getType()), metadata); } } catch (QueryResolverException e) { handleException(e); } } } break; case STRING_AGG: try { if (obj.getArgs().length != 2) { throw new QueryResolverException(QueryPlugin.Event.TEIID31140, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31140, obj)); } if (obj.getType() == null) { Expression arg = obj.getArg(0); Expression arg1 = obj.getArg(1); Class<?> type = null; if (isBinary(arg)) { setDesiredType(arg1, DataTypeManager.DefaultDataClasses.BLOB, obj); if (isBinary(arg1)) { type = DataTypeManager.DefaultDataClasses.BLOB; } } else if (isCharacter(arg, false)) { setDesiredType(arg1, DataTypeManager.DefaultDataClasses.CLOB, obj); if (isCharacter(arg1, false)) { type = DataTypeManager.DefaultDataClasses.CLOB; } } else if (arg.getType() == null) { if (isBinary(arg1)) { setDesiredType(arg, DataTypeManager.DefaultDataClasses.BLOB, obj); if (isBinary(arg)) { type = DataTypeManager.DefaultDataClasses.BLOB; } } else if (isCharacter(arg1, false)) { setDesiredType(arg, DataTypeManager.DefaultDataClasses.CLOB, obj); if (isCharacter(arg, false)) { type = DataTypeManager.DefaultDataClasses.CLOB; } } } if (type == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID31141, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31141, obj)); } obj.setType(type); } } catch (QueryResolverException e) { handleException(e); } break; } } static boolean isCharacter(Expression arg, boolean includeChar) { Class<?> type = arg.getType(); return isCharacter(type, includeChar); } static boolean isCharacter(Class<?> type, boolean includeChar) { return type == DataTypeManager.DefaultDataClasses.STRING || type == DataTypeManager.DefaultDataClasses.CLOB || (includeChar && type == DataTypeManager.DefaultDataClasses.CHAR); } private boolean isBinary(Expression arg) { return arg.getType() == DataTypeManager.DefaultDataClasses.VARBINARY || arg.getType() == DataTypeManager.DefaultDataClasses.BLOB; } public TeiidComponentException getComponentException() { return this.componentException; } public QueryResolverException getResolverException() { return this.resolverException; } void handleException(TeiidComponentException e) { this.componentException = e; // Abort the validation process setAbort(true); } void handleException(QueryResolverException e) { this.resolverException = e; // Abort the validation process setAbort(true); } public void throwException(boolean includeUnresolvedFunctions) throws TeiidComponentException, QueryResolverException { if(getComponentException() != null) { throw getComponentException(); } if(getResolverException() != null) { throw getResolverException(); } if (includeUnresolvedFunctions && unresolvedFunctions != null && !unresolvedFunctions.isEmpty()) { throw unresolvedFunctions.values().iterator().next(); } } /** * Resolve function such that all functions are resolved and type-safe. */ void resolveFunction(Function function, FunctionLibrary library) throws QueryResolverException, TeiidComponentException { // Check whether this function is already resolved if(function.getFunctionDescriptor() != null) { return; } // Look up types for all args boolean hasArgWithoutType = false; Expression[] args = function.getArgs(); Class<?>[] types = new Class[args.length]; for(int i=0; i<args.length; i++) { types[i] = args[i].getType(); if(types[i] == null) { if(!(args[i] instanceof Reference)){ throw new QueryResolverException(QueryPlugin.Event.TEIID30067, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30067, new Object[] {args[i], function})); } hasArgWithoutType = true; } } //special case handling for convert of an untyped reference if (FunctionLibrary.isConvert(function) && hasArgWithoutType) { Constant constant = (Constant)function.getArg(1); Class<?> type = metadata.getDataTypeClass((String)constant.getValue()); setDesiredType(function.getArg(0), type, function); types[0] = type; hasArgWithoutType = false; } // Attempt to get exact match of function for this signature List<FunctionDescriptor> fds; try { fds = findWithImplicitConversions(library, function, args, types, hasArgWithoutType); if(fds.isEmpty()) { if(!library.hasFunctionMethod(function.getName(), args.length)) { // Unknown function form throw new QueryResolverException(QueryPlugin.Event.TEIID30068, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30068, function)); } // Known function form - but without type information if (hasArgWithoutType) { throw new QueryResolverException(QueryPlugin.Event.TEIID30069, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30069, function)); } // Known function form - unable to find implicit conversions throw new QueryResolverException(QueryPlugin.Event.TEIID30070, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30070, function)); } if (fds.size() > 1) { throw new QueryResolverException(QueryPlugin.Event.TEIID31150, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31150, function)); } } catch (InvalidFunctionException e) { // Known function form - but without type information if (hasArgWithoutType) { throw new QueryResolverException(QueryPlugin.Event.TEIID30069, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30069, function)); } throw new QueryResolverException(QueryPlugin.Event.TEIID31150, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31150, function)); } FunctionDescriptor fd = fds.get(0); if (fd.getMethod().isVarArgs() && fd.getTypes().length == types.length && library.isVarArgArrayParam(fd.getMethod(), types, types.length - 1, fd.getTypes()[types.length - 1])) { fd = fd.clone(); fd.setCalledWithVarArgArrayParam(true); } if(fd.isSystemFunction(FunctionLibrary.CONVERT) || fd.isSystemFunction(FunctionLibrary.CAST)) { String dataType = (String) ((Constant)args[1]).getValue(); Class<?> dataTypeClass = metadata.getDataTypeClass(dataType); fd = library.findTypedConversionFunction(args[0].getType(), dataTypeClass); // Verify that the type conversion from src to type is even valid Class<?> srcTypeClass = args[0].getType(); if(srcTypeClass != null && dataTypeClass != null && !srcTypeClass.equals(dataTypeClass) && !DataTypeManager.isTransformable(srcTypeClass, dataTypeClass)) { throw new QueryResolverException(QueryPlugin.Event.TEIID30071, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30071, new Object[] {DataTypeManager.getDataTypeName(srcTypeClass), dataType})); } } else if(fd.isSystemFunction(FunctionLibrary.LOOKUP)) { ResolverUtil.ResolvedLookup lookup = ResolverUtil.resolveLookup(function, metadata); fd = library.copyFunctionChangeReturnType(fd, lookup.getReturnElement().getType()); } else if (fd.isSystemFunction(FunctionLibrary.ARRAY_GET)) { if (args[0].getType() != null && args[0].getType().isArray()) { fd = library.copyFunctionChangeReturnType(fd, args[0].getType().getComponentType()); } else { if (function.getType() != null) { setDesiredType(args[0], function.getType(), function); } if (args[0].getType() != DataTypeManager.DefaultDataClasses.OBJECT) { throw new QueryResolverException(QueryPlugin.Event.TEIID31145, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31145, DataTypeManager.getDataTypeName(args[0].getType()), function)); } } } else if (Boolean.valueOf(fd.getMethod().getProperty(TEIID_PASS_THROUGH_TYPE, false))) { //hack largely to support pg fd = library.copyFunctionChangeReturnType(fd, args[0].getType()); } function.setFunctionDescriptor(fd); function.setType(fd.getReturnType()); if (CoreConstants.SYSTEM_MODEL.equals(fd.getSchema())) { if (StringUtil.startsWithIgnoreCase(function.getName(), SYS_PREFIX)) { function.setName(function.getName().substring(SYS_PREFIX.length())); } } else if (library.getSystemFunctions().hasFunctionWithName(function.getName()) && !StringUtil.startsWithIgnoreCase(function.getName(), function.getFunctionDescriptor().getSchema() + ElementSymbol.SEPARATOR)) { function.setName(function.getFunctionDescriptor().getSchema() + ElementSymbol.SEPARATOR + function.getName()); } } /** * Find possible matches based on implicit conversions of the arguments. * NOTE: This method has the side-effect of explicitly inserting conversions into the function arguments, * and thereby changing the structure of the function call. * @param library * @param function * @param types * @return * @throws TeiidComponentException * @throws InvalidFunctionException * @since 4.3 */ private List<FunctionDescriptor> findWithImplicitConversions(FunctionLibrary library, Function function, Expression[] args, Class<?>[] types, boolean hasArgWithoutType) throws QueryResolverException, TeiidComponentException, InvalidFunctionException { // Try to find implicit conversion path to still perform this function ConversionResult cr = library.determineNecessaryConversions(function.getName(), function.getType(), args, types, hasArgWithoutType); if (cr.method == null) { return Collections.emptyList(); } Class<?>[] newSignature = types; if (cr.needsConverion) { FunctionDescriptor[] conversions = library.getConverts(cr.method, types); newSignature = new Class[conversions.length]; // Insert new conversion functions as necessary, while building new signature for(int i=0; i<conversions.length; i++) { Class<?> newType = types[i]; if(conversions[i] != null) { newType = conversions[i].getReturnType(); setDesiredType(args[i], newType, function); //only currently typed expressions need conversions if (types[i] != null && newType != DataTypeManager.DefaultDataClasses.OBJECT) { //directly resolve constants if (args[i] instanceof Constant && newType == DataTypeManager.DefaultDataClasses.TIMESTAMP) { args[i] = ResolverUtil.getProperlyTypedConstant(((Constant)args[i]).getValue(), newType); } else { function.insertConversion(i, conversions[i]); } } } newSignature[i] = newType; } } String name = cr.method.getFullName(); // Now resolve using the new signature to get the function's descriptor return library.findAllFunctions(name, newSignature); } /** * Resolves criteria "a BETWEEN b AND c". If type conversions are necessary, * this method attempts the following implicit conversions: * <br/> * <ol type="1" start="1"> * <li>convert the lower and upper expressions to the criteria expression's type, or</li> * <li>convert the criteria and upper expressions to the lower expression's type, or</li> * <li>convert the criteria and lower expressions to the upper expression's type, or</li> * <li>convert all expressions to a common type to which all three expressions' types can be implicitly converted.</li> * </ol> * @param criteria * @throws QueryResolverException * @throws TeiidComponentException * @throws TeiidComponentException */ void resolveBetweenCriteria(BetweenCriteria criteria) throws QueryResolverException, TeiidComponentException { Expression exp = criteria.getExpression(); Expression lower = criteria.getLowerExpression(); Expression upper = criteria.getUpperExpression(); // invariants: none of the expressions is an aggregate symbol setDesiredType(exp, (lower.getType() == null) ? upper.getType() : lower.getType(), criteria); // invariants: exp.getType() != null setDesiredType(lower, exp.getType(), criteria); setDesiredType(upper, exp.getType(), criteria); // invariants: none of the types is null if (exp.getType() == lower.getType() && exp.getType() == upper.getType()) { return; } String expTypeName = DataTypeManager.getDataTypeName(exp.getType()); String lowerTypeName = DataTypeManager.getDataTypeName(lower.getType()); String upperTypeName = DataTypeManager.getDataTypeName(upper.getType()); //check if all types are the same, or if there is a common type String[] types = new String[2]; types[0] = lowerTypeName; types[1] = upperTypeName; Class<?> type = null; String commonType = ResolverUtil.getCommonRuntimeType(types); if (commonType != null) { type = DataTypeManager.getDataTypeClass(commonType); } boolean exprChar = isCharacter(exp, true); if (exp.getType() != DataTypeManager.DefaultDataClasses.NULL) { boolean success = true; // try to apply cast // Apply cast and replace current value if (!exprChar || metadata.widenComparisonToString() || isCharacter(lower, true)) { try { criteria.setLowerExpression(ResolverUtil.convertExpression(lower, lowerTypeName, expTypeName, metadata) ); lower = criteria.getLowerExpression(); lowerTypeName = DataTypeManager.getDataTypeName(lower.getType()); } catch (QueryResolverException e) { if (lower instanceof Constant && isCharacter(lower, true) && !metadata.widenComparisonToString()) { throw e; } if (type == null) { type = lower.getType(); } success = false; } } else { success = false; } // try to apply cast // Apply cast and replace current value if (!exprChar || metadata.widenComparisonToString() || isCharacter(upper, true)) { try { criteria.setUpperExpression(ResolverUtil.convertExpression(upper, upperTypeName, expTypeName, metadata) ); upper = criteria.getUpperExpression(); upperTypeName = DataTypeManager.getDataTypeName(upper.getType()); } catch (QueryResolverException e) { if (lower instanceof Constant && isCharacter(lower, true) && !metadata.widenComparisonToString()) { throw e; } if (type == null) { type = upper.getType(); } success = false; } } else { success = false; } if (success) { return; } } // If no convert found for first element, check whether everything in the // set is the same and the convert can be placed on the left side if (type == null) { // Couldn't find a common type to implicitly convert to throw new QueryResolverException(QueryPlugin.Event.TEIID30072, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30077, criteria)); } // Is there a possible conversion from left to right? String typeName = DataTypeManager.getDataTypeName(type); if (!isCharacter(type, true) || metadata.widenComparisonToString() || exp.getType() == DataTypeManager.DefaultDataClasses.NULL) { criteria.setExpression(ResolverUtil.convertExpression(exp, expTypeName, typeName, metadata)); } else if (type != exp.getType()) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, criteria)); } if(lower.getType() != type) { if (!metadata.widenComparisonToString() && exprChar ^ isCharacter(lower, true)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, criteria)); } criteria.setLowerExpression(ResolverUtil.convertExpression(lower, lowerTypeName, typeName, metadata)); } if(upper.getType() != type) { if (!metadata.widenComparisonToString() && exprChar ^ isCharacter(lower, true)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, criteria)); } criteria.setUpperExpression(ResolverUtil.convertExpression(upper, upperTypeName, typeName, metadata)); } // invariants: exp.getType() == lower.getType() == upper.getType() } void resolveCompareCriteria(CompareCriteria ccrit) throws QueryResolverException { Expression leftExpression = ccrit.getLeftExpression(); Expression rightExpression = ccrit.getRightExpression(); // Check typing between expressions setDesiredType(leftExpression, rightExpression.getType(), ccrit); setDesiredType(rightExpression, leftExpression.getType(), ccrit); if(leftExpression.getType() == rightExpression.getType()) { return; } // Try to apply an implicit conversion from one side to the other String leftTypeName = DataTypeManager.getDataTypeName(leftExpression.getType()); String rightTypeName = DataTypeManager.getDataTypeName(rightExpression.getType()); if (leftExpression.getType() == DataTypeManager.DefaultDataClasses.NULL) { ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName, metadata) ); return; } if (rightExpression.getType() == DataTypeManager.DefaultDataClasses.NULL) { ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName, metadata) ); return; } boolean leftChar = isCharacter(leftExpression, true); boolean rightChar = isCharacter(rightExpression, true); // Special cases when right expression is a constant if(rightExpression instanceof Constant && (!leftChar || rightChar)) { // Auto-convert constant string on right to expected type on left try { ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName, metadata)); return; } catch (QueryResolverException qre) { if (rightChar && !metadata.widenComparisonToString()) { throw qre; } } } // Special cases when left expression is a constant if(leftExpression instanceof Constant && (!rightChar || leftChar)) { // Auto-convert constant string on left to expected type on right try { ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName, metadata)); return; } catch (QueryResolverException qre) { if (leftChar && !metadata.widenComparisonToString()) { throw qre; } } } // Try to apply a conversion generically if ((rightChar ^ leftChar) && !metadata.widenComparisonToString()) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, ccrit)); } if(ResolverUtil.canImplicitlyConvert(leftTypeName, rightTypeName)) { ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName, metadata) ); return; } if(ResolverUtil.canImplicitlyConvert(rightTypeName, leftTypeName)) { ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName, metadata) ); return; } String commonType = ResolverUtil.getCommonRuntimeType(new String[] {leftTypeName, rightTypeName}); if (commonType == null) { // Neither are aggs, but types can't be reconciled throw new QueryResolverException(QueryPlugin.Event.TEIID30072, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30072, new Object[] { leftTypeName, rightTypeName, ccrit })); } ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, commonType, metadata) ); ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, commonType, metadata) ); } void resolveMatchCriteria(MatchCriteria mcrit) throws QueryResolverException { setDesiredType(mcrit.getLeftExpression(), mcrit.getRightExpression().getType(), mcrit); mcrit.setLeftExpression(resolveMatchCriteriaExpression(mcrit, mcrit.getLeftExpression())); setDesiredType(mcrit.getRightExpression(), mcrit.getLeftExpression().getType(), mcrit); mcrit.setRightExpression(resolveMatchCriteriaExpression(mcrit, mcrit.getRightExpression())); } /** * Checks one side of a LIKE Criteria; implicitly converts to a String or CLOB if necessary. * @param mcrit the Match Criteria * @param expr either left or right expression * @return either 'expr' itself, or a new implicit type conversion wrapping expr * @throws QueryResolverException if no implicit type conversion is available */ Expression resolveMatchCriteriaExpression(MatchCriteria mcrit, Expression expr) throws QueryResolverException { // Check left expression == string or CLOB String type = DataTypeManager.getDataTypeName(expr.getType()); Expression result = expr; if(type != null) { if (!isCharacter(expr, false)) { if(ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.STRING)) { result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.STRING, metadata); } else if (ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.CLOB)){ result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.CLOB, metadata); } else { throw new QueryResolverException(QueryPlugin.Event.TEIID30074, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30074, mcrit)); } } } return result; } void resolveSetCriteria(SetCriteria scrit) throws QueryResolverException { // Check that each of the values are the same type as expression Class<?> exprType = scrit.getExpression().getType(); if(exprType == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30075, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30075, scrit.getExpression())); } //check if all types are the same, or if there is a common type boolean same = true; Iterator valIter = scrit.getValues().iterator(); String[] types = new String[scrit.getValues().size()]; int i = 0; Class<?> type = null; while(valIter.hasNext()) { Expression value = (Expression) valIter.next(); if (value.getType() != exprType) { same = false; } types[i++] = DataTypeManager.getDataTypeName(value.getType()); type = value.getType(); } if (same && type == exprType) { return; } if (!same) { String commonType = ResolverUtil.getCommonRuntimeType(types); if (commonType != null) { type = DataTypeManager.getDataTypeClass(commonType); } else { type = null; } } String exprTypeName = DataTypeManager.getDataTypeName(exprType); boolean attemptConvert = !isCharacter(exprType, true) || metadata.widenComparisonToString(); List<Expression> newVals = new ArrayList<Expression>(scrit.getValues().size()); if (scrit.getExpression().getType() != DataTypeManager.DefaultDataClasses.NULL) { valIter = scrit.getValues().iterator(); while(valIter.hasNext()) { Expression value = (Expression) valIter.next(); setDesiredType(value, exprType, scrit); if(value.getType() != exprType) { String valTypeName = DataTypeManager.getDataTypeName(value.getType()); // try to apply cast // Apply cast and replace current value if (attemptConvert || isCharacter(value.getType(), true)) { try { newVals.add(ResolverUtil.convertExpression(value, valTypeName, exprTypeName, metadata) ); } catch (QueryResolverException e) { if (value instanceof Constant && isCharacter(value, true) && !metadata.widenComparisonToString()) { throw e; } if (type == null) { type = value.getType(); } break; } } } else { newVals.add(value); } } if (newVals.size() == scrit.getValues().size()) { scrit.setValues(newVals); return; } } // If no convert found for first element, check whether everything in the // set is the same and the convert can be placed on the left side if (type == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30077, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30077, scrit)); } // Is there a possible conversion from left to right? String setTypeName = DataTypeManager.getDataTypeName(type); if (!isCharacter(type, true) || metadata.widenComparisonToString() || scrit.getExpression().getType() == DataTypeManager.DefaultDataClasses.NULL) { scrit.setExpression(ResolverUtil.convertExpression(scrit.getExpression(), exprTypeName, setTypeName, metadata)); } else if (type != scrit.getExpression().getType()) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, scrit)); } boolean exprChar = isCharacter(scrit.getExpression(), true); newVals.clear(); valIter = scrit.getValues().iterator(); while(valIter.hasNext()) { Expression value = (Expression) valIter.next(); if(value.getType() == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30075, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30075, value)); } else if(value.getType() != type) { if (!metadata.widenComparisonToString() && exprChar ^ isCharacter(value, true)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, scrit)); } value = ResolverUtil.convertExpression(value, setTypeName, metadata); } newVals.add(value); } scrit.setValues(newVals); } void resolveCaseExpression(CaseExpression obj) throws QueryResolverException { // If already resolved, do nothing if (obj.getType() != null) { return; } final int whenCount = obj.getWhenCount(); Expression expr = obj.getExpression(); Class<?> whenType = null; Class<?> thenType = null; // Get the WHEN and THEN types, and get a candidate type for each (for the next step) for (int i = 0; i < whenCount; i++) { if (whenType == null) { whenType = obj.getWhenExpression(i).getType(); } if (thenType == null) { thenType = obj.getThenExpression(i).getType(); } } Expression elseExpr = obj.getElseExpression(); if (elseExpr != null) { if (thenType == null) { thenType = elseExpr.getType(); } } // Invariant: All the expressions contained in the obj are resolved (except References) // 2. Attempt to set the target types of all contained expressions, // and collect their type names for the next step ArrayList<String> whenTypeNames = new ArrayList<String>(whenCount + 1); ArrayList<String> thenTypeNames = new ArrayList<String>(whenCount + 1); setDesiredType(expr, whenType, obj); // Add the expression's type to the WHEN types whenTypeNames.add(DataTypeManager.getDataTypeName(expr.getType())); Expression when = null; Expression then = null; // Set the types of the WHEN and THEN parts boolean whenNotChar = false; for (int i = 0; i < whenCount; i++) { when = obj.getWhenExpression(i); then = obj.getThenExpression(i); setDesiredType(when, expr.getType(), obj); setDesiredType(then, thenType, obj); if (!whenTypeNames.contains(DataTypeManager.getDataTypeName(when.getType()))) { whenTypeNames.add(DataTypeManager.getDataTypeName(when.getType())); } if (!isCharacter(when.getType(), true)) { whenNotChar = true; } if (!thenTypeNames.contains(DataTypeManager.getDataTypeName(then.getType()))) { thenTypeNames.add(DataTypeManager.getDataTypeName(then.getType())); } } // Set the type of the else expression if (elseExpr != null) { setDesiredType(elseExpr, thenType, obj); if (!thenTypeNames.contains(DataTypeManager.getDataTypeName(elseExpr.getType()))) { thenTypeNames.add(DataTypeManager.getDataTypeName(elseExpr.getType())); } } // Invariants: all the expressions' types are non-null // 3. Perform implicit type conversions String whenTypeName = ResolverUtil.getCommonRuntimeType(whenTypeNames.toArray(new String[whenTypeNames.size()])); if (whenTypeName == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30079, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30079, "WHEN", obj));//$NON-NLS-1$ } if (!metadata.widenComparisonToString() && whenNotChar && isCharacter(DataTypeManager.getDataTypeClass(whenTypeName), true)) { throw new QueryResolverException(QueryPlugin.Event.TEIID31172, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31172, obj)); } String thenTypeName = ResolverUtil.getCommonRuntimeType(thenTypeNames.toArray(new String[thenTypeNames.size()])); if (thenTypeName == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30079, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30079, "THEN/ELSE", obj));//$NON-NLS-1$ } obj.setExpression(ResolverUtil.convertExpression(obj.getExpression(), whenTypeName, metadata)); ArrayList<Expression> whens = new ArrayList<Expression>(whenCount); ArrayList<Expression> thens = new ArrayList<Expression>(whenCount); for (int i = 0; i < whenCount; i++) { whens.add(ResolverUtil.convertExpression(obj.getWhenExpression(i), whenTypeName, metadata)); thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName, metadata)); } obj.setWhen(whens, thens); if (elseExpr != null) { obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName, metadata)); } // Set this CASE expression's type to the common THEN type, and we're done. obj.setType(DataTypeManager.getDataTypeClass(thenTypeName)); } private void setDesiredType(Expression obj, Class<?> type, LanguageObject surrounding) throws QueryResolverException { ResolverUtil.setDesiredType(obj, type, surrounding); //second pass resolving for functions if (!(obj instanceof Function)) { return; } if (unresolvedFunctions != null) { Function f = (Function)obj; if (f.getFunctionDescriptor() != null) { return; } unresolvedFunctions.remove(obj); obj.acceptVisitor(this); QueryResolverException e = unresolvedFunctions.get(obj); if (e != null) { throw e; } } } void resolveSearchedCaseExpression(SearchedCaseExpression obj) throws QueryResolverException { // If already resolved, do nothing if (obj.getType() != null) { return; } final int whenCount = obj.getWhenCount(); // 1. Call recursively to resolve any contained CASE expressions Class<?> thenType = null; // Get the WHEN and THEN types, and get a candidate type for each (for the next step) for (int i = 0; i < whenCount; i++) { if (thenType == null) { thenType = obj.getThenExpression(i).getType(); } } Expression elseExpr = obj.getElseExpression(); if (elseExpr != null) { if (thenType == null) { thenType = elseExpr.getType(); } } // Invariant: All the expressions contained in the obj are resolved (except References) // 2. Attempt to set the target types of all contained expressions, // and collect their type names for the next step ArrayList<String> thenTypeNames = new ArrayList<String>(whenCount + 1); Expression then = null; // Set the types of the WHEN and THEN parts for (int i = 0; i < whenCount; i++) { then = obj.getThenExpression(i); setDesiredType(then, thenType, obj); thenTypeNames.add(DataTypeManager.getDataTypeName(then.getType())); } // Set the type of the else expression if (elseExpr != null) { setDesiredType(elseExpr, thenType, obj); thenTypeNames.add(DataTypeManager.getDataTypeName(elseExpr.getType())); } // Invariants: all the expressions' types are non-null // 3. Perform implicit type conversions String thenTypeName = ResolverUtil.getCommonRuntimeType(thenTypeNames.toArray(new String[thenTypeNames.size()])); if (thenTypeName == null) { throw new QueryResolverException(QueryPlugin.Event.TEIID30079, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30079, "THEN/ELSE", obj)); //$NON-NLS-1$ } ArrayList<Expression> thens = new ArrayList<Expression>(whenCount); for (int i = 0; i < whenCount; i++) { thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName, metadata)); } obj.setWhen(obj.getWhen(), thens); if (elseExpr != null) { obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName, metadata)); } // Set this CASE expression's type to the common THEN type, and we're done. obj.setType(DataTypeManager.getDataTypeClass(thenTypeName)); } public static void resolveLanguageObject(LanguageObject obj, QueryMetadataInterface metadata) throws TeiidComponentException, QueryResolverException { ResolverVisitor.resolveLanguageObject(obj, null, metadata); } public static void resolveLanguageObject(LanguageObject obj, Collection<GroupSymbol> groups, QueryMetadataInterface metadata) throws TeiidComponentException, QueryResolverException { ResolverVisitor.resolveLanguageObject(obj, groups, null, metadata); } public static void resolveLanguageObject(LanguageObject obj, Collection<GroupSymbol> groups, GroupContext externalContext, QueryMetadataInterface metadata) throws TeiidComponentException, QueryResolverException { if(obj == null) { return; } // Resolve elements, deal with errors ResolverVisitor elementsVisitor = new ResolverVisitor(metadata, groups, externalContext); PostOrderNavigator.doVisit(obj, elementsVisitor); elementsVisitor.throwException(true); } public boolean hasUserDefinedAggregate() { return hasUserDefinedAggregate; } }