[teiid-commits] teiid SVN: r815 - in trunk/engine/src: main/java/com/metamatrix/query/optimizer/relational/rules and 5 other directories.
teiid-commits at lists.jboss.org
teiid-commits at lists.jboss.org
Tue Apr 21 14:38:34 EDT 2009
Author: shawkins
Date: 2009-04-21 14:38:33 -0400 (Tue, 21 Apr 2009)
New Revision: 815
Modified:
trunk/engine/src/main/java/com/metamatrix/query/function/FunctionLibrary.java
trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushAggregates.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/command/ExecResolver.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/command/InsertResolver.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/command/SimpleQueryResolver.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverUtil.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitor.java
trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitorUtil.java
trunk/engine/src/main/java/com/metamatrix/query/sql/navigator/AbstractNavigator.java
trunk/engine/src/test/java/com/metamatrix/query/function/TestFunctionLibrary.java
trunk/engine/src/test/java/com/metamatrix/query/resolver/TestFunctionResolving.java
trunk/engine/src/test/java/com/metamatrix/query/resolver/TestResolver.java
Log:
TEIID-235 improved resolution of functions when a parameter type is unknown.
Modified: trunk/engine/src/main/java/com/metamatrix/query/function/FunctionLibrary.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/function/FunctionLibrary.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/function/FunctionLibrary.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -201,25 +201,26 @@
* In other cases the list will contain one or more non-null values where the value
* is a conversion function that can be used to convert to the proper types for
* executing the function.
- * @param name Name of function
- * @param types Existing types passed to the function
+ * @param name Name of function
+ * @param returnType
+ * @param types Existing types passed to the function
* @return Null if no conversion could be found, otherwise an array of conversions
* to apply to each argument. The list should match 1-to-1 with the parameters.
* Parameters that do not need a conversion are null; parameters that do are
* FunctionDescriptors.
* @throws QueryResolverException
*/
- public FunctionDescriptor[] determineNecessaryConversions(String name, Class[] types, boolean hasUnknownType) {
+ public FunctionDescriptor[] determineNecessaryConversions(String name, Class<?> returnType, Class<?>[] types, boolean hasUnknownType) {
// Check for no args - no conversion necessary
if(types.length == 0) {
return new FunctionDescriptor[0];
}
// Construct results array
- FunctionDescriptor[] results = null;
+ FunctionDescriptor[] results = null;
//First find existing functions with same name and same number of parameters
- final Collection functionMethods = new LinkedList();
+ final Collection<FunctionMethod> functionMethods = new LinkedList<FunctionMethod>();
functionMethods.addAll( this.systemFunctions.findFunctionMethods(name , types.length) );
functionMethods.addAll( this.userFunctions.findFunctionMethods(name , types.length) );
@@ -227,11 +228,10 @@
//Score of current method (lower score means better match with less converts
//Current best score (lower score is best. Higher score results in more implicit conversions
int bestScore = Integer.MAX_VALUE;
- boolean ambiguous = false;
-
- for (Iterator matchingMethods = functionMethods.iterator(); matchingMethods.hasNext(); ) {
+ boolean ambiguous = false;
+
+ for (FunctionMethod nextMethod : functionMethods) {
int currentScore = 0;
- final FunctionMethod nextMethod = (FunctionMethod)matchingMethods.next();
final FunctionParameter[] methodTypes = nextMethod.getInputParameters();
//Holder for current signature with converts where required
FunctionDescriptor[] currentSignature = new FunctionDescriptor[types.length];
@@ -242,9 +242,9 @@
for(; i < types.length; i++) {
//treat all varags as the same type
final String tmpTypeName = methodTypes[Math.min(i, methodTypes.length - 1)].getType();
- Class targetType = DataTypeManager.getDataTypeClass(tmpTypeName);
+ Class<?> targetType = DataTypeManager.getDataTypeClass(tmpTypeName);
- Class sourceType = types[i];
+ Class<?> sourceType = types[i];
if (sourceType == null) {
FunctionDescriptor fd = findTypedConversionFunction(DataTypeManager.DefaultDataClasses.NULL, targetType);
currentSignature[i] = fd;
@@ -252,32 +252,37 @@
continue;
}
- final String sourceTypeName = DataTypeManager.getDataTypeName(sourceType);
- final String targetTypeName = DataTypeManager.getDataTypeName(targetType);
- //If exact match no conversion neccessary
- if(sourceTypeName.equals(targetTypeName)) {
- continue;
- }
- //Else see if an implicit conversion is possible.
- if(!DataTypeManager.isImplicitConversion(sourceTypeName, targetTypeName)){
- break;
- }
- //Else no conversion is available and the current method is not a valid match
- final FunctionDescriptor fd = findTypedConversionFunction(sourceType, targetType);
- if(fd == null) {
- break;
- }
- currentScore++;
- currentSignature[i] = fd;
+ try {
+ FunctionDescriptor fd = getConvertFunctionDescriptor(sourceType, targetType);
+ if (fd != null) {
+ currentScore++;
+ currentSignature[i] = fd;
+ }
+ } catch (InvalidFunctionException e) {
+ break;
+ }
}
//If the method is valid match and it is the current best score, capture those values as current best match
if (i != types.length || currentScore > bestScore) {
continue;
- }
+ }
+
+ if (hasUnknownType) {
+ if (returnType != null) {
+ try {
+ FunctionDescriptor fd = getConvertFunctionDescriptor(DataTypeManager.getDataTypeClass(nextMethod.getOutputParameter().getType()), returnType);
+ if (fd != null) {
+ currentScore++;
+ }
+ } catch (InvalidFunctionException e) {
+ //there still may be a common type, but use any other valid conversion over this one
+ currentScore += (types.length + 1);
+ }
+ }
+ ambiguous = currentScore == bestScore;
+ }
- ambiguous = hasUnknownType && currentScore == bestScore;
-
if (currentScore < bestScore) {
if (currentScore == 0) {
@@ -286,7 +291,7 @@
}
bestScore = currentScore;
- results = currentSignature;
+ results = currentSignature;
}
}
@@ -295,6 +300,25 @@
}
return results;
+ }
+
+ private FunctionDescriptor getConvertFunctionDescriptor(Class<?> sourceType, Class<?> targetType) throws InvalidFunctionException {
+ final String sourceTypeName = DataTypeManager.getDataTypeName(sourceType);
+ final String targetTypeName = DataTypeManager.getDataTypeName(targetType);
+ //If exact match no conversion necessary
+ if(sourceTypeName.equals(targetTypeName)) {
+ return null;
+ }
+ //Else see if an implicit conversion is possible.
+ if(!DataTypeManager.isImplicitConversion(sourceTypeName, targetTypeName)){
+ throw new InvalidFunctionException();
+ }
+ //Else no conversion is available and the current method is not a valid match
+ final FunctionDescriptor fd = findTypedConversionFunction(sourceType, targetType);
+ if(fd == null) {
+ throw new InvalidFunctionException();
+ }
+ return fd;
}
/**
Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushAggregates.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushAggregates.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushAggregates.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -48,6 +48,7 @@
import com.metamatrix.query.optimizer.relational.plantree.NodeEditor;
import com.metamatrix.query.optimizer.relational.plantree.NodeFactory;
import com.metamatrix.query.optimizer.relational.plantree.PlanNode;
+import com.metamatrix.query.resolver.util.ResolverVisitor;
import com.metamatrix.query.resolver.util.ResolverVisitorUtil;
import com.metamatrix.query.sql.ReservedWords;
import com.metamatrix.query.sql.lang.CompareCriteria;
@@ -384,7 +385,7 @@
Function convertFunc = new Function(FunctionLibrary.CONVERT, new Expression[] {
newAgg, convertTargetType
});
- ResolverVisitorUtil.resolveFunction(convertFunc, metadata);
+ ResolverVisitor.resolveLanguageObject(convertFunc, metadata);
newExpression = convertFunc;
nestedAggregates.add(partitionAgg);
@@ -397,7 +398,7 @@
AggregateSymbol sumCountAgg = new AggregateSymbol("stagedAgg", ReservedWords.SUM, false, countAgg); //$NON-NLS-1$
Function divideFunc = new Function("/", new Expression[] {sumSumAgg, sumCountAgg}); //$NON-NLS-1$
- ResolverVisitorUtil.resolveFunction(divideFunc, metadata);
+ ResolverVisitor.resolveLanguageObject(divideFunc, metadata);
newExpression = divideFunc;
nestedAggregates.add(countAgg);
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/command/ExecResolver.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/command/ExecResolver.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/command/ExecResolver.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -289,7 +289,7 @@
ResolverVisitor.resolveLanguageObject(expr, null, externalGroups, metadata);
Class paramType = param.getClassType();
- ResolverUtil.setTypeIfReference(expr, paramType, storedProcedureCommand);
+ ResolverUtil.setDesiredType(expr, paramType, storedProcedureCommand);
// Compare type of parameter expression against parameter type
// and add implicit conversion if necessary
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/command/InsertResolver.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/command/InsertResolver.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/command/InsertResolver.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -166,7 +166,7 @@
ElementSymbol element = (ElementSymbol) varIter.next();
if (!usingQuery) {
- ResolverUtil.setTypeIfReference(expression, element.getType(), insert);
+ ResolverUtil.setDesiredType(expression, element.getType(), insert);
}
if(element.getType() != null && expression.getType() != null) {
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/command/SimpleQueryResolver.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/command/SimpleQueryResolver.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/command/SimpleQueryResolver.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -298,6 +298,8 @@
try {
QueryResolverVisitor qrv = new QueryResolverVisitor(query, metadata, useMetadataCommands, analysis);
qrv.visit(query);
+ ResolverVisitor visitor = (ResolverVisitor)qrv.getVisitor();
+ visitor.throwException(true);
} catch (MetaMatrixRuntimeException e) {
if (e.getChild() instanceof QueryMetadataException) {
throw (QueryMetadataException)e.getChild();
@@ -362,12 +364,13 @@
protected void postVisitVisitor(LanguageObject obj) {
super.postVisitVisitor(obj);
ResolverVisitor visitor = (ResolverVisitor)getVisitor();
- if (visitor.getComponentException() != null) {
- throw new MetaMatrixRuntimeException(visitor.getComponentException());
- }
- if (visitor.getResolverException() != null) {
- throw new MetaMatrixRuntimeException(visitor.getResolverException());
- }
+ try {
+ visitor.throwException(false);
+ } catch (QueryResolverException e) {
+ throw new MetaMatrixRuntimeException(e);
+ } catch (MetaMatrixComponentException e) {
+ throw new MetaMatrixRuntimeException(e);
+ }
}
/**
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverUtil.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverUtil.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverUtil.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -213,10 +213,10 @@
private static Expression getConversion(Expression sourceExpression,
String sourceTypeName,
String targetTypeName) {
- Class srcType = DataTypeManager.getDataTypeClass(sourceTypeName);
+ Class<?> srcType = DataTypeManager.getDataTypeClass(sourceTypeName);
FunctionLibrary library = FunctionLibraryManager.getFunctionLibrary();
- FunctionDescriptor fd = library.findFunction(FunctionLibrary.CONVERT, new Class[] { srcType, DataTypeManager.DefaultDataClasses.STRING });
+ FunctionDescriptor fd = library.findTypedConversionFunction(srcType, DataTypeManager.getDataTypeClass(targetTypeName));
Function conversion = new Function(fd.getName(), new Expression[] { sourceExpression, new Constant(targetTypeName) });
conversion.setType(DataTypeManager.getDataTypeClass(targetTypeName));
@@ -232,12 +232,20 @@
* @param targetType the target type, if the expression's type is null.
* @throws QueryResolverException if unable to set the reference type to the target type.
*/
- public static void setTypeIfReference(Expression expression, Class<?> targetType, LanguageObject surroundingExpression) throws QueryResolverException {
- if ((expression instanceof Reference) && ((Reference)expression).isPositional()) {
- if (targetType == null) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0026, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0026, surroundingExpression));
+ public static void setDesiredType(Expression expression, Class<?> targetType, LanguageObject surroundingExpression) throws QueryResolverException {
+ if (expression instanceof Reference) {
+ Reference ref = (Reference)expression;
+ if (ref.isPositional() && ref.getType() == null) {
+ if (targetType == null) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0026, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0026, surroundingExpression));
+ }
+ ref.setType(targetType);
}
- ((Reference)expression).setType(targetType);
+ } else if (expression instanceof Function) {
+ Function f = (Function)expression;
+ if (f.getType() == null) {
+ f.setType(targetType);
+ }
}
}
@@ -550,9 +558,9 @@
public static void resolveLimit(Limit limit) throws QueryResolverException {
if (limit.getOffset() != null) {
- setTypeIfReference(limit.getOffset(), DataTypeManager.DefaultDataClasses.INTEGER, limit);
+ setDesiredType(limit.getOffset(), DataTypeManager.DefaultDataClasses.INTEGER, limit);
}
- setTypeIfReference(limit.getRowLimit(), DataTypeManager.DefaultDataClasses.INTEGER, limit);
+ setDesiredType(limit.getRowLimit(), DataTypeManager.DefaultDataClasses.INTEGER, limit);
}
public static void resolveImplicitTempGroup(TempMetadataAdapter metadata, GroupSymbol symbol, List symbols)
@@ -629,7 +637,7 @@
((ScalarSubquery)expr).setType(replacement);
} else {
try {
- ResolverUtil.setTypeIfReference(expr, replacement, symbol);
+ ResolverUtil.setDesiredType(expr, replacement, symbol);
} catch (QueryResolverException e) {
//cannot happen
}
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitor.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitor.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitor.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -22,18 +22,30 @@
package com.metamatrix.query.resolver.util;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import net.sf.saxon.trans.XPathException;
+
import com.metamatrix.api.exception.MetaMatrixComponentException;
import com.metamatrix.api.exception.query.QueryMetadataException;
import com.metamatrix.api.exception.query.QueryResolverException;
import com.metamatrix.api.exception.query.UnresolvedSymbolDescription;
import com.metamatrix.common.types.DataTypeManager;
+import com.metamatrix.common.types.DataTypeManager.DefaultDataClasses;
import com.metamatrix.core.util.Assertion;
+import com.metamatrix.internal.core.xml.XPathHelper;
import com.metamatrix.query.QueryPlugin;
+import com.metamatrix.query.function.FunctionDescriptor;
+import com.metamatrix.query.function.FunctionForm;
+import com.metamatrix.query.function.FunctionLibrary;
+import com.metamatrix.query.function.FunctionLibraryManager;
import com.metamatrix.query.metadata.GroupInfo;
import com.metamatrix.query.metadata.QueryMetadataInterface;
import com.metamatrix.query.sql.LanguageObject;
@@ -48,10 +60,14 @@
import com.metamatrix.query.sql.lang.SubqueryCompareCriteria;
import com.metamatrix.query.sql.lang.SubquerySetCriteria;
import com.metamatrix.query.sql.navigator.PostOrderNavigator;
+import com.metamatrix.query.sql.symbol.AggregateSymbol;
import com.metamatrix.query.sql.symbol.CaseExpression;
+import com.metamatrix.query.sql.symbol.Constant;
import com.metamatrix.query.sql.symbol.ElementSymbol;
+import com.metamatrix.query.sql.symbol.Expression;
import com.metamatrix.query.sql.symbol.Function;
import com.metamatrix.query.sql.symbol.GroupSymbol;
+import com.metamatrix.query.sql.symbol.Reference;
import com.metamatrix.query.sql.symbol.SearchedCaseExpression;
import com.metamatrix.query.util.ErrorMessageKeys;
@@ -67,31 +83,25 @@
}
}
- private Collection groups;
+ private Collection<GroupSymbol> groups;
private GroupContext externalContext;
protected QueryMetadataInterface metadata;
private MetaMatrixComponentException componentException;
private QueryResolverException resolverException;
+ private Map<Function, QueryResolverException> unresolvedFunctions;
/**
* Constructor for ResolveElementsVisitor.
*
* External groups are ordered from inner to outer most
*/
- public ResolverVisitor(QueryMetadataInterface metadata, Collection internalGroups, GroupContext externalContext) {
+ public ResolverVisitor(QueryMetadataInterface metadata, Collection<GroupSymbol> internalGroups, GroupContext externalContext) {
this.groups = internalGroups;
this.externalContext = externalContext;
this.metadata = metadata;
}
- /**
- * Constructor for ResolveElementsVisitor.
- */
- public ResolverVisitor(QueryMetadataInterface metadata, Collection internalGroups) {
- this(metadata, internalGroups, null);
- }
-
- public void setGroups(Collection groups) {
+ public void setGroups(Collection<GroupSymbol> groups) {
this.groups = groups;
}
@@ -149,7 +159,7 @@
}
} else {
try {
- LinkedList matchedGroups = new LinkedList();
+ LinkedList<GroupSymbol> matchedGroups = new LinkedList<GroupSymbol>();
if (groupContext != null) {
//assume that this is fully qualified
@@ -169,7 +179,7 @@
LinkedList<ElementMatch> matches = new LinkedList<ElementMatch>();
String shortCanonicalName = elementShortName.toUpperCase();
while (root != null) {
- Collection matchedGroups = ResolverUtil.findMatchingGroups(groupContext, root.getGroups(), metadata);
+ Collection<GroupSymbol> matchedGroups = ResolverUtil.findMatchingGroups(groupContext, root.getGroups(), metadata);
if (matchedGroups != null && !matchedGroups.isEmpty()) {
groupMatched = true;
@@ -217,10 +227,10 @@
private void resolveUsingUUID(String potentialID,
String elementShortName,
LinkedList<ElementMatch> matches,
- Collection matchedGroups) throws MetaMatrixComponentException,
+ Collection<GroupSymbol> matchedGroups) throws MetaMatrixComponentException,
QueryMetadataException {
Assertion.assertTrue(matchedGroups.size() == 1);
- GroupSymbol group = (GroupSymbol)matchedGroups.iterator().next();
+ GroupSymbol group = matchedGroups.iterator().next();
ElementSymbol resolvedSymbol = new ElementSymbol(potentialID);
resolvedSymbol.setGroupSymbol(group);
String elementID = metadata.getFullElementName( metadata.getFullName(group.getMetadataID()), elementShortName );
@@ -230,11 +240,9 @@
}
private void resolveAgainstGroups(String elementShortName,
- Collection matchedGroups, LinkedList<ElementMatch> matches) throws QueryMetadataException,
+ Collection<GroupSymbol> matchedGroups, LinkedList<ElementMatch> matches) throws QueryMetadataException,
MetaMatrixComponentException {
- for (Iterator i = matchedGroups.iterator(); i.hasNext();) {
- GroupSymbol group = (GroupSymbol)i.next();
-
+ for (GroupSymbol group : matchedGroups) {
GroupInfo groupInfo = ResolverUtil.getGroupInfo(group, metadata);
ElementSymbol result = groupInfo.getSymbol(elementShortName);
@@ -246,15 +254,17 @@
public void visit(BetweenCriteria obj) {
try {
- ResolverVisitorUtil.resolveBetweenCriteria(obj);
+ resolveBetweenCriteria(obj);
} catch(QueryResolverException e) {
handleException(e);
+ } catch(MetaMatrixComponentException e) {
+ handleException(e);
}
}
public void visit(CompareCriteria obj) {
try {
- ResolverVisitorUtil.resolveCompareCriteria(obj);
+ resolveCompareCriteria(obj);
} catch(QueryResolverException e) {
handleException(e);
}
@@ -262,7 +272,7 @@
public void visit(MatchCriteria obj) {
try {
- ResolverVisitorUtil.resolveMatchCriteria(obj);
+ resolveMatchCriteria(obj);
} catch(QueryResolverException e) {
handleException(e);
}
@@ -270,7 +280,7 @@
public void visit(SetCriteria obj) {
try {
- ResolverVisitorUtil.resolveSetCriteria(obj);
+ resolveSetCriteria(obj);
} catch(QueryResolverException e) {
handleException(e);
}
@@ -295,7 +305,7 @@
public void visit(IsNullCriteria obj) {
try {
- ResolverVisitorUtil.resolveIsNullCriteria(obj);
+ setDesiredType(obj.getExpression(), DefaultDataClasses.OBJECT, obj);
} catch(QueryResolverException e) {
handleException(e);
}
@@ -303,9 +313,12 @@
public void visit(Function obj) {
try {
- ResolverVisitorUtil.resolveFunction(obj, metadata);
+ resolveFunction(obj);
} catch(QueryResolverException e) {
- handleException(e);
+ if (unresolvedFunctions == null) {
+ unresolvedFunctions = new IdentityHashMap<Function, QueryResolverException>();
+ }
+ unresolvedFunctions.put(obj, e);
} catch(MetaMatrixComponentException e) {
handleException(e);
}
@@ -313,28 +326,27 @@
public void visit(CaseExpression obj) {
try {
- ResolverVisitorUtil.resolveCaseExpression(obj);
-
+ resolveCaseExpression(obj);
} catch(QueryResolverException e) {
handleException(e);
- }
+ }
}
public void visit(SearchedCaseExpression obj) {
try {
- ResolverVisitorUtil.resolveSearchedCaseExpression(obj);
+ resolveSearchedCaseExpression(obj);
} catch(QueryResolverException e) {
handleException(e);
- }
+ }
}
public void visit(SetClause obj) {
String type = DataTypeManager.getDataTypeName(obj.getSymbol().getType());
try {
- ResolverUtil.setTypeIfReference(obj.getValue(), obj.getSymbol().getType(), obj);
+ setDesiredType(obj.getValue(), obj.getSymbol().getType(), obj);
obj.setValue(ResolverUtil.convertExpression(obj.getValue(), type));
} catch(QueryResolverException e) {
- handleException(new QueryResolverException(e, QueryPlugin.Util.getString("SetClause.resolvingError", new Object[] {obj.getValue(), obj.getSymbol(), type})));
+ handleException(new QueryResolverException(e, QueryPlugin.Util.getString("SetClause.resolvingError", new Object[] {obj.getValue(), obj.getSymbol(), type}))); //$NON-NLS-1$
}
}
@@ -360,34 +372,576 @@
setAbort(true);
}
- public static void resolveLanguageObject(LanguageObject obj, QueryMetadataInterface metadata)
- throws MetaMatrixComponentException, QueryResolverException {
- ResolverVisitor.resolveLanguageObject(obj, null, metadata);
- }
+ public void throwException(boolean includeUnresolvedFunctions)
+ throws MetaMatrixComponentException, QueryResolverException {
+ if(getComponentException() != null) {
+ throw getComponentException();
+ }
- public static void resolveLanguageObject(LanguageObject obj, Collection groups, QueryMetadataInterface metadata)
- throws MetaMatrixComponentException, QueryResolverException {
- ResolverVisitor.resolveLanguageObject(obj, groups, null, metadata);
- }
+ if(getResolverException() != null) {
+ throw getResolverException();
+ }
+
+ if (includeUnresolvedFunctions
+ && unresolvedFunctions != null && !unresolvedFunctions.isEmpty()) {
+ throw unresolvedFunctions.values().iterator().next();
+ }
+ }
- public static void resolveLanguageObject(LanguageObject obj, Collection groups, GroupContext externalContext, QueryMetadataInterface metadata)
- throws MetaMatrixComponentException, QueryResolverException {
+ /**
+ * Resolve function such that all functions are resolved and type-safe.
+ */
+ void resolveFunction(Function function)
+ throws QueryResolverException, MetaMatrixComponentException {
+
+ // 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(ErrorMessageKeys.RESOLVER_0035, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0035, new Object[] {args[i], function}));
+ }
+ hasArgWithoutType = true;
+ }
+ }
+
+ FunctionLibrary library = FunctionLibraryManager.getFunctionLibrary();
+
+ //special case handling for convert of an untyped reference
+ if (FunctionLibrary.isConvert(function) && hasArgWithoutType) {
+ Constant constant = (Constant)function.getArg(1);
+ Class<?> type = DataTypeManager.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
+ FunctionDescriptor fd = findWithImplicitConversions(library, function, args, types, hasArgWithoutType);
+
+ // Function did not resolve - determine reason and throw exception
+ if(fd == null) {
+ FunctionForm form = library.findFunctionForm(function.getName(), args.length);
+ if(form == null) {
+ // Unknown function form
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0039, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0039, function));
+ }
+ // Known function form - but without type information
+ if (hasArgWithoutType) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0036, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0036, function));
+ }
+ // Known function form - unable to find implicit conversions
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0040, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0040, function));
+ }
+
+ if(fd.getName().equalsIgnoreCase(FunctionLibrary.CONVERT) || fd.getName().equalsIgnoreCase(FunctionLibrary.CAST)) {
+ String dataType = (String) ((Constant)args[1]).getValue();
+ Class dataTypeClass = DataTypeManager.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(ErrorMessageKeys.RESOLVER_0037, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0037, new Object[] {DataTypeManager.getDataTypeName(srcTypeClass), dataType}));
+ }
+ } else if(fd.getName().equalsIgnoreCase(FunctionLibrary.LOOKUP)) {
+ ResolverVisitorUtil.ResolvedLookup lookup = ResolverVisitorUtil.resolveLookup(function, metadata);
+ fd = library.copyFunctionChangeReturnType(fd, lookup.getReturnElement().getType());
+ } else if(fd.getName().equalsIgnoreCase(FunctionLibrary.XPATHVALUE)) {
+ // Validate the xpath value is valid
+ if(args[1] != null && args[1] instanceof Constant) {
+ Constant xpathConst = (Constant) args[1];
+ if(xpathConst.getType().equals(DataTypeManager.DefaultDataClasses.STRING)) {
+ String value = (String) xpathConst.getValue();
+ if(value == null) {
+ throw new QueryResolverException(QueryPlugin.Util.getString("QueryResolver.invalid_xpath", QueryPlugin.Util.getString("ResolveFunctionsVisitor.xpath_cant_be_null"))); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ try {
+ XPathHelper.validateXpath(value);
+ } catch(XPathException e) {
+ throw new QueryResolverException(QueryPlugin.Util.getString("QueryResolver.invalid_xpath", e.getMessage())); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ // Resolve the function
+ function.setFunctionDescriptor(fd);
+ function.setType(fd.getReturnType());
+ }
- if(obj == null) {
- return;
- }
+ /**
+ * 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 MetaMatrixComponentException
+ * @since 4.3
+ */
+ private FunctionDescriptor findWithImplicitConversions(FunctionLibrary library, Function function, Expression[] args, Class<?>[] types, boolean hasArgWithoutType) throws QueryResolverException, MetaMatrixComponentException {
+
+ // Try to find implicit conversion path to still perform this function
+ FunctionDescriptor[] conversions = library.determineNecessaryConversions(function.getName(), function.getType(), types, hasArgWithoutType);
+
+ if(conversions == null) {
+ return null;
+ }
+ // Insert new conversion functions as necessary, while building new signature
+ Class<?>[] newSignature = new Class[conversions.length];
+ 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) {
+ function.insertConversion(i, conversions[i]);
+ }
+ }
+
+ newSignature[i] = newType;
+ }
+
+ // Now resolve using the new signature to get the function's descriptor
+ return library.findFunction(function.getName(), newSignature);
+ }
- // Resolve elements, deal with errors
- ResolverVisitor elementsVisitor = new ResolverVisitor(metadata, groups, externalContext);
- PostOrderNavigator.doVisit(obj, elementsVisitor);
- if(elementsVisitor.getComponentException() != null) {
- throw elementsVisitor.getComponentException();
- }
+ /**
+ * 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 MetaMatrixComponentException
+ * @throws MetaMatrixComponentException
+ */
+ void resolveBetweenCriteria(BetweenCriteria criteria)
+ throws QueryResolverException, MetaMatrixComponentException {
+
+ 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
+
+ String expTypeName = DataTypeManager.getDataTypeName(exp.getType());
+ String lowerTypeName = DataTypeManager.getDataTypeName(lower.getType());
+ String upperTypeName = DataTypeManager.getDataTypeName(upper.getType());
+ if (exp.getType().equals(lower.getType()) && exp.getType().equals(upper.getType())) {
+ return;
+ }
+
+ String commonType = ResolverUtil.getCommonType(new String[] {expTypeName, lowerTypeName, upperTypeName});
+ if (commonType != null) {
+ criteria.setExpression(ResolverUtil.convertExpression(exp, expTypeName, commonType));
+ criteria.setLowerExpression(ResolverUtil.convertExpression(lower, lowerTypeName, commonType));
+ criteria.setUpperExpression(ResolverUtil.convertExpression(upper, upperTypeName, commonType));
+ } else {
+ // Couldn't find a common type to implicitly convert to
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0027, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0027, expTypeName, lowerTypeName, criteria));
+ }
+ // invariants: exp.getType() == lower.getType() == upper.getType()
+ }
- if(elementsVisitor.getResolverException() != null) {
- throw elementsVisitor.getResolverException();
- }
+ 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().equals(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());
+
+ // Special cases when right expression is a constant
+ if(rightExpression instanceof Constant) {
+ // Auto-convert constant string on right to expected type on left
+ try {
+ ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName));
+ return;
+ } catch (QueryResolverException qre) {
+ //ignore
+ }
+ }
+
+ // Special cases when left expression is a constant
+ if(leftExpression instanceof Constant) {
+ // Auto-convert constant string on left to expected type on right
+ try {
+ ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName));
+ return;
+ } catch (QueryResolverException qre) {
+ //ignore
+ }
+ }
+
+ // Try to apply a conversion generically
+
+ if(ResolverUtil.canImplicitlyConvert(leftTypeName, rightTypeName)) {
+ ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName) );
+ return;
+ }
+
+ if(ResolverUtil.canImplicitlyConvert(rightTypeName, leftTypeName)) {
+ ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName) );
+ return;
+ }
+
+ String commonType = ResolverUtil.getCommonType(new String[] {leftTypeName, rightTypeName});
+
+ if (commonType == null) {
+ // Neither are aggs, but types can't be reconciled
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0027, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0027, new Object[] { leftTypeName, rightTypeName, ccrit }));
+ }
+ ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, commonType) );
+ ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, commonType) );
+ }
- }
+ 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 (! type.equals(DataTypeManager.DefaultDataTypes.STRING) &&
+ ! type.equals(DataTypeManager.DefaultDataTypes.CLOB)) {
+
+ if(!(expr instanceof AggregateSymbol) &&
+ ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.STRING)) {
+
+ result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.STRING);
+
+ } else if (!(expr instanceof AggregateSymbol) &&
+ ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.CLOB)){
+
+ result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.CLOB);
+
+ } else {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0029, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0029, 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(ErrorMessageKeys.RESOLVER_0030, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0030, scrit.getExpression()));
+ }
+
+ String exprTypeName = DataTypeManager.getDataTypeName(exprType);
+ boolean changed = false;
+ List newVals = new ArrayList();
+
+ boolean convertLeft = false;
+ Class setType = null;
+
+ Iterator valIter = scrit.getValues().iterator();
+ while(valIter.hasNext()) {
+ Expression value = (Expression) valIter.next();
+ setDesiredType(value, exprType, scrit);
+ if(! value.getType().equals(exprType)) {
+ if(value instanceof AggregateSymbol) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
+ }
+ // try to apply cast
+ String valTypeName = DataTypeManager.getDataTypeName(value.getType());
+ if(ResolverUtil.canImplicitlyConvert(valTypeName, exprTypeName)) {
+ // Apply cast and replace current value
+ newVals.add(ResolverUtil.convertExpression(value, valTypeName, exprTypeName) );
+ changed = true;
+ } else {
+ convertLeft = true;
+ setType = value.getType();
+ break;
+ }
+ } else {
+ newVals.add(value);
+ }
+ }
+
+ // 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(convertLeft) {
+ // Is there a possible conversion from left to right?
+ String setTypeName = DataTypeManager.getDataTypeName(setType);
+ if(ResolverUtil.canImplicitlyConvert(exprTypeName, setTypeName)) {
+ valIter = scrit.getValues().iterator();
+ while(valIter.hasNext()) {
+ Expression value = (Expression) valIter.next();
+ if(value.getType() == null) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0030, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0030, value));
+ } else if(! value.getType().equals(setType)) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
+ }
+ }
+
+ // Convert left expression to type of values in the set
+ scrit.setExpression(ResolverUtil.convertExpression(scrit.getExpression(), exprTypeName, setTypeName ));
+
+ } else {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
+ }
+ }
+
+ if(changed) {
+ 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 whenTypeNames = new ArrayList(whenCount + 1);
+ ArrayList thenTypeNames = new ArrayList(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
+ 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 (!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.getCommonType((String[])whenTypeNames.toArray(new String[whenTypeNames.size()]));
+ if (whenTypeName == null) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "WHEN", obj)); //$NON-NLS-1$
+ }
+ String thenTypeName = ResolverUtil.getCommonType((String[])thenTypeNames.toArray(new String[thenTypeNames.size()]));
+ if (thenTypeName == null) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "THEN/ELSE", obj)); //$NON-NLS-1$
+ }
+ obj.setExpression(ResolverUtil.convertExpression(obj.getExpression(), whenTypeName));
+ ArrayList whens = new ArrayList(whenCount);
+ ArrayList thens = new ArrayList(whenCount);
+ for (int i = 0; i < whenCount; i++) {
+ whens.add(ResolverUtil.convertExpression(obj.getWhenExpression(i), whenTypeName));
+ thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName));
+ }
+ obj.setWhen(whens, thens);
+ if (elseExpr != null) {
+ obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName));
+ }
+ // 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 thenTypeNames = new ArrayList(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);
+ 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 thenTypeName = ResolverUtil.getCommonType((String[])thenTypeNames.toArray(new String[thenTypeNames.size()]));
+ if (thenTypeName == null) {
+ throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "THEN/ELSE", obj)); //$NON-NLS-1$
+ }
+ ArrayList thens = new ArrayList(whenCount);
+ for (int i = 0; i < whenCount; i++) {
+ thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName));
+ }
+ obj.setWhen(obj.getWhen(), thens);
+ if (elseExpr != null) {
+ obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName));
+ }
+ // 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 MetaMatrixComponentException, QueryResolverException {
+ ResolverVisitor.resolveLanguageObject(obj, null, metadata);
+ }
+
+ public static void resolveLanguageObject(LanguageObject obj, Collection<GroupSymbol> groups, QueryMetadataInterface metadata)
+ throws MetaMatrixComponentException, QueryResolverException {
+ ResolverVisitor.resolveLanguageObject(obj, groups, null, metadata);
+ }
+
+ public static void resolveLanguageObject(LanguageObject obj, Collection<GroupSymbol> groups, GroupContext externalContext, QueryMetadataInterface metadata)
+ throws MetaMatrixComponentException, QueryResolverException {
+
+ if(obj == null) {
+ return;
+ }
+
+ // Resolve elements, deal with errors
+ ResolverVisitor elementsVisitor = new ResolverVisitor(metadata, groups, externalContext);
+ PostOrderNavigator.doVisit(obj, elementsVisitor);
+ elementsVisitor.throwException(true);
+ }
}
Modified: trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitorUtil.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitorUtil.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/resolver/util/ResolverVisitorUtil.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -22,44 +22,26 @@
package com.metamatrix.query.resolver.util;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
-import net.sf.saxon.trans.XPathException;
import com.metamatrix.api.exception.MetaMatrixComponentException;
import com.metamatrix.api.exception.query.QueryMetadataException;
import com.metamatrix.api.exception.query.QueryResolverException;
import com.metamatrix.api.exception.query.UnresolvedSymbolDescription;
import com.metamatrix.common.types.DataTypeManager;
-import com.metamatrix.common.types.DataTypeManager.DefaultDataClasses;
-import com.metamatrix.internal.core.xml.XPathHelper;
import com.metamatrix.query.QueryPlugin;
-import com.metamatrix.query.function.FunctionDescriptor;
-import com.metamatrix.query.function.FunctionForm;
-import com.metamatrix.query.function.FunctionLibrary;
-import com.metamatrix.query.function.FunctionLibraryManager;
import com.metamatrix.query.metadata.QueryMetadataInterface;
import com.metamatrix.query.metadata.StoredProcedureInfo;
import com.metamatrix.query.metadata.TempMetadataID;
-import com.metamatrix.query.sql.lang.BetweenCriteria;
-import com.metamatrix.query.sql.lang.CompareCriteria;
-import com.metamatrix.query.sql.lang.IsNullCriteria;
-import com.metamatrix.query.sql.lang.MatchCriteria;
-import com.metamatrix.query.sql.lang.SetCriteria;
import com.metamatrix.query.sql.lang.SubqueryContainer;
-import com.metamatrix.query.sql.symbol.AggregateSymbol;
-import com.metamatrix.query.sql.symbol.CaseExpression;
import com.metamatrix.query.sql.symbol.Constant;
import com.metamatrix.query.sql.symbol.ElementSymbol;
import com.metamatrix.query.sql.symbol.Expression;
import com.metamatrix.query.sql.symbol.Function;
import com.metamatrix.query.sql.symbol.GroupSymbol;
-import com.metamatrix.query.sql.symbol.Reference;
-import com.metamatrix.query.sql.symbol.SearchedCaseExpression;
import com.metamatrix.query.util.ErrorMessageKeys;
public class ResolverVisitorUtil {
@@ -90,231 +72,6 @@
}
/**
- * 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 MetaMatrixComponentException
- */
- static void resolveBetweenCriteria(BetweenCriteria criteria)
- throws QueryResolverException {
-
- Expression exp = criteria.getExpression();
- Expression lower = criteria.getLowerExpression();
- Expression upper = criteria.getUpperExpression();
-
- // invariants: none of the expressions is an aggregate symbol
- ResolverUtil.setTypeIfReference(exp,
- (lower.getType() == null)
- ? upper.getType()
- : lower.getType(), criteria);
- // invariants: exp.getType() != null
- ResolverUtil.setTypeIfReference(lower, exp.getType(), criteria);
- ResolverUtil.setTypeIfReference(upper, exp.getType(), criteria);
- // invariants: none of the types is null
-
- String expTypeName = DataTypeManager.getDataTypeName(exp.getType());
- String lowerTypeName = DataTypeManager.getDataTypeName(lower.getType());
- String upperTypeName = DataTypeManager.getDataTypeName(upper.getType());
- if (exp.getType().equals(lower.getType()) && exp.getType().equals(upper.getType())) {
- return;
- }
-
- String commonType = ResolverUtil.getCommonType(new String[] {expTypeName, lowerTypeName, upperTypeName});
- if (commonType != null) {
- criteria.setExpression(ResolverUtil.convertExpression(exp, expTypeName, commonType));
- criteria.setLowerExpression(ResolverUtil.convertExpression(lower, lowerTypeName, commonType));
- criteria.setUpperExpression(ResolverUtil.convertExpression(upper, upperTypeName, commonType));
- } else {
- // Couldn't find a common type to implicitly convert to
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0027, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0027, expTypeName, lowerTypeName, criteria));
- }
- // invariants: exp.getType() == lower.getType() == upper.getType()
- }
-
- static void resolveCompareCriteria(CompareCriteria ccrit)
- throws QueryResolverException {
-
- Expression leftExpression = ccrit.getLeftExpression();
- Expression rightExpression = ccrit.getRightExpression();
-
- // Check typing between expressions
- ResolverUtil.setTypeIfReference(leftExpression, rightExpression.getType(), ccrit);
- ResolverUtil.setTypeIfReference(rightExpression, leftExpression.getType(), ccrit);
-
- if(leftExpression.getType().equals(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());
-
- // Special cases when right expression is a constant
- if(rightExpression instanceof Constant) {
- // Auto-convert constant string on right to expected type on left
- try {
- ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName));
- return;
- } catch (QueryResolverException qre) {
- //ignore
- }
- }
-
- // Special cases when left expression is a constant
- if(leftExpression instanceof Constant) {
- // Auto-convert constant string on left to expected type on right
- try {
- ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName));
- return;
- } catch (QueryResolverException qre) {
- //ignore
- }
- }
-
- // Try to apply a conversion generically
-
- if(ResolverUtil.canImplicitlyConvert(leftTypeName, rightTypeName)) {
- ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, rightTypeName) );
- return;
- }
-
- if(ResolverUtil.canImplicitlyConvert(rightTypeName, leftTypeName)) {
- ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, leftTypeName) );
- return;
- }
-
- String commonType = ResolverUtil.getCommonType(new String[] {leftTypeName, rightTypeName});
-
- if (commonType == null) {
- // Neither are aggs, but types can't be reconciled
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0027, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0027, new Object[] { leftTypeName, rightTypeName, ccrit }));
- }
- ccrit.setLeftExpression(ResolverUtil.convertExpression(leftExpression, leftTypeName, commonType) );
- ccrit.setRightExpression(ResolverUtil.convertExpression(rightExpression, rightTypeName, commonType) );
- }
-
- static void resolveMatchCriteria(MatchCriteria mcrit)
- throws QueryResolverException {
-
- ResolverUtil.setTypeIfReference(mcrit.getLeftExpression(), mcrit.getRightExpression().getType(), mcrit);
- mcrit.setLeftExpression(resolveMatchCriteriaExpression(mcrit, mcrit.getLeftExpression()));
-
- ResolverUtil.setTypeIfReference(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
- */
- static 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 (! type.equals(DataTypeManager.DefaultDataTypes.STRING) &&
- ! type.equals(DataTypeManager.DefaultDataTypes.CLOB)) {
-
- if(!(expr instanceof AggregateSymbol) &&
- ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.STRING)) {
-
- result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.STRING);
-
- } else if (!(expr instanceof AggregateSymbol) &&
- ResolverUtil.canImplicitlyConvert(type, DataTypeManager.DefaultDataTypes.CLOB)){
-
- result = ResolverUtil.convertExpression(expr, type, DataTypeManager.DefaultDataTypes.CLOB);
-
- } else {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0029, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0029, mcrit));
- }
- }
- }
- return result;
- }
-
- static 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(ErrorMessageKeys.RESOLVER_0030, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0030, scrit.getExpression()));
- }
-
- String exprTypeName = DataTypeManager.getDataTypeName(exprType);
- boolean changed = false;
- List newVals = new ArrayList();
-
- boolean convertLeft = false;
- Class setType = null;
-
- Iterator valIter = scrit.getValues().iterator();
- while(valIter.hasNext()) {
- Expression value = (Expression) valIter.next();
- ResolverUtil.setTypeIfReference(value, exprType, scrit);
- if(! value.getType().equals(exprType)) {
- if(value instanceof AggregateSymbol) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
- }
- // try to apply cast
- String valTypeName = DataTypeManager.getDataTypeName(value.getType());
- if(ResolverUtil.canImplicitlyConvert(valTypeName, exprTypeName)) {
- // Apply cast and replace current value
- newVals.add(ResolverUtil.convertExpression(value, valTypeName, exprTypeName) );
- changed = true;
- } else {
- convertLeft = true;
- setType = value.getType();
- break;
- }
- } else {
- newVals.add(value);
- }
- }
-
- // 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(convertLeft) {
- // Is there a possible conversion from left to right?
- String setTypeName = DataTypeManager.getDataTypeName(setType);
- if(ResolverUtil.canImplicitlyConvert(exprTypeName, setTypeName)) {
- valIter = scrit.getValues().iterator();
- while(valIter.hasNext()) {
- Expression value = (Expression) valIter.next();
- if(value.getType() == null) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0030, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0030, value));
- } else if(! value.getType().equals(setType)) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
- }
- }
-
- // Convert left expression to type of values in the set
- scrit.setExpression(ResolverUtil.convertExpression(scrit.getExpression(), exprTypeName, setTypeName ));
-
- } else {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0031, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0031, scrit));
- }
- }
-
- if(changed) {
- scrit.setValues(newVals);
- }
- }
-
- /**
* Check the type of the (left) expression and the type of the single
* projected symbol of the subquery. If they are not the same, try to find
* an implicit conversion from the former type to the latter type, and wrap
@@ -353,257 +110,6 @@
return result;
}
- static void resolveIsNullCriteria(IsNullCriteria crit)
- throws QueryResolverException {
-
- ResolverUtil.setTypeIfReference(crit.getExpression(), DefaultDataClasses.OBJECT, crit);
- }
-
- static 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 whenTypeNames = new ArrayList(whenCount + 1);
- ArrayList thenTypeNames = new ArrayList(whenCount + 1);
- // Set the type of the expression
- ResolverUtil.setTypeIfReference(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
- for (int i = 0; i < whenCount; i++) {
- when = obj.getWhenExpression(i);
- then = obj.getThenExpression(i);
-
- ResolverUtil.setTypeIfReference(when, expr.getType(), obj);
- ResolverUtil.setTypeIfReference(then, thenType, obj);
-
- if (!whenTypeNames.contains(DataTypeManager.getDataTypeName(when.getType()))) {
- whenTypeNames.add(DataTypeManager.getDataTypeName(when.getType()));
- }
- if (!thenTypeNames.contains(DataTypeManager.getDataTypeName(then.getType()))) {
- thenTypeNames.add(DataTypeManager.getDataTypeName(then.getType()));
- }
- }
- // Set the type of the else expression
- if (elseExpr != null) {
- ResolverUtil.setTypeIfReference(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.getCommonType((String[])whenTypeNames.toArray(new String[whenTypeNames.size()]));
- if (whenTypeName == null) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "WHEN", obj)); //$NON-NLS-1$
- }
- String thenTypeName = ResolverUtil.getCommonType((String[])thenTypeNames.toArray(new String[thenTypeNames.size()]));
- if (thenTypeName == null) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "THEN/ELSE", obj)); //$NON-NLS-1$
- }
- obj.setExpression(ResolverUtil.convertExpression(obj.getExpression(), whenTypeName));
- ArrayList whens = new ArrayList(whenCount);
- ArrayList thens = new ArrayList(whenCount);
- for (int i = 0; i < whenCount; i++) {
- whens.add(ResolverUtil.convertExpression(obj.getWhenExpression(i), whenTypeName));
- thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName));
- }
- obj.setWhen(whens, thens);
- if (elseExpr != null) {
- obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName));
- }
- // Set this CASE expression's type to the common THEN type, and we're done.
- obj.setType(DataTypeManager.getDataTypeClass(thenTypeName));
- }
-
- static 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 thenTypeNames = new ArrayList(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);
- ResolverUtil.setTypeIfReference(then, thenType, obj);
- if (!thenTypeNames.contains(DataTypeManager.getDataTypeName(then.getType()))) {
- thenTypeNames.add(DataTypeManager.getDataTypeName(then.getType()));
- }
- }
- // Set the type of the else expression
- if (elseExpr != null) {
- ResolverUtil.setTypeIfReference(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 thenTypeName = ResolverUtil.getCommonType((String[])thenTypeNames.toArray(new String[thenTypeNames.size()]));
- if (thenTypeName == null) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0068, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0068, "THEN/ELSE", obj)); //$NON-NLS-1$
- }
- ArrayList thens = new ArrayList(whenCount);
- for (int i = 0; i < whenCount; i++) {
- thens.add(ResolverUtil.convertExpression(obj.getThenExpression(i), thenTypeName));
- }
- obj.setWhen(obj.getWhen(), thens);
- if (elseExpr != null) {
- obj.setElseExpression(ResolverUtil.convertExpression(elseExpr, thenTypeName));
- }
- // Set this CASE expression's type to the common THEN type, and we're done.
- obj.setType(DataTypeManager.getDataTypeClass(thenTypeName));
- }
-
- /**
- * Resolve function such that all functions are resolved and type-safe.
- */
- public static void resolveFunction(Function function, QueryMetadataInterface metadata)
- throws QueryResolverException, MetaMatrixComponentException {
-
- // 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(ErrorMessageKeys.RESOLVER_0035, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0035, new Object[] {args[i], function}));
- }
- hasArgWithoutType = true;
- }
- }
-
- FunctionLibrary library = FunctionLibraryManager.getFunctionLibrary();
-
- //special case handling for convert of an untyped reference
- if (FunctionLibrary.isConvert(function) && hasArgWithoutType) {
- Constant constant = (Constant)function.getArg(1);
- Class type = DataTypeManager.getDataTypeClass((String)constant.getValue());
-
- ResolverUtil.setTypeIfReference(function.getArg(0), type, function);
- types[0] = type;
- hasArgWithoutType = false;
- }
-
- // Attempt to get exact match of function for this signature
- FunctionDescriptor fd = findWithImplicitConversions(library, function, args, types, hasArgWithoutType);
-
- // Function did not resolve - determine reason and throw exception
- if(fd == null) {
- FunctionForm form = library.findFunctionForm(function.getName(), args.length);
- if(form == null) {
- // Unknown function form
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0039, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0039, function));
- }
- // Known function form - but without type information
- if (hasArgWithoutType) {
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0036, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0036, function));
- }
- // Known function form - unable to find implicit conversions
- throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0040, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0040, function));
- }
-
- if(fd.getName().equalsIgnoreCase(FunctionLibrary.CONVERT) || fd.getName().equalsIgnoreCase(FunctionLibrary.CAST)) {
- String dataType = (String) ((Constant)args[1]).getValue();
- Class dataTypeClass = DataTypeManager.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(ErrorMessageKeys.RESOLVER_0037, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0037, new Object[] {DataTypeManager.getDataTypeName(srcTypeClass), dataType}));
- }
- } else if(fd.getName().equalsIgnoreCase(FunctionLibrary.LOOKUP)) {
- ResolvedLookup lookup = resolveLookup(function, metadata);
- fd = library.copyFunctionChangeReturnType(fd, lookup.getReturnElement().getType());
- } else if(fd.getName().equalsIgnoreCase(FunctionLibrary.XPATHVALUE)) {
- // Validate the xpath value is valid
- if(args[1] != null && args[1] instanceof Constant) {
- Constant xpathConst = (Constant) args[1];
- if(xpathConst.getType().equals(DataTypeManager.DefaultDataClasses.STRING)) {
- String value = (String) xpathConst.getValue();
- if(value == null) {
- throw new QueryResolverException(QueryPlugin.Util.getString("QueryResolver.invalid_xpath", QueryPlugin.Util.getString("ResolveFunctionsVisitor.xpath_cant_be_null"))); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- try {
- XPathHelper.validateXpath(value);
- } catch(XPathException e) {
- throw new QueryResolverException(QueryPlugin.Util.getString("QueryResolver.invalid_xpath", e.getMessage())); //$NON-NLS-1$
- }
- }
- }
- }
-
- // Resolve the function
- function.setFunctionDescriptor(fd);
- function.setType(fd.getReturnType());
- }
-
public static ResolvedLookup resolveLookup(Function lookup, QueryMetadataInterface metadata) throws QueryResolverException, MetaMatrixComponentException {
Expression[] args = lookup.getArgs();
ResolvedLookup result = new ResolvedLookup();
@@ -645,49 +151,7 @@
throw new QueryResolverException(ErrorMessageKeys.RESOLVER_0063, QueryPlugin.Util.getString(ErrorMessageKeys.RESOLVER_0063));
}
- /**
- * 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
- * @since 4.3
- */
- static FunctionDescriptor findWithImplicitConversions(FunctionLibrary library, Function function, Expression[] args, Class[] types, boolean hasArgWithoutType) throws QueryResolverException {
-
- // Try to find implicit conversion path to still perform this function
- FunctionDescriptor[] conversions = library.determineNecessaryConversions(function.getName(), types, hasArgWithoutType);
-
- if(conversions == null) {
- return null;
- }
- // Insert new conversion functions as necessary, while building new signature
- Class[] newSignature = new Class[conversions.length];
- for(int i=0; i<conversions.length; i++) {
-
- Class newType = types[i];
-
- if(conversions[i] != null) {
- newType = conversions[i].getReturnType();
-
- ResolverUtil.setTypeIfReference(args[i], newType, function);
-
- //only currently typed expressions need conversions
- if (types[i] != null) {
- function.insertConversion(i, conversions[i]);
- }
- }
-
- newSignature[i] = newType;
- }
-
- // Now resolve using the new signature to get the function's descriptor
- return library.findFunction(function.getName(), newSignature);
- }
-
- private static QueryResolverException handleUnresolvedGroup(GroupSymbol symbol, String description) {
+ private static QueryResolverException handleUnresolvedGroup(GroupSymbol symbol, String description) {
UnresolvedSymbolDescription usd = new UnresolvedSymbolDescription(symbol.toString(), description);
QueryResolverException e = new QueryResolverException(usd.getDescription()+": "+usd.getSymbol()); //$NON-NLS-1$
e.setUnresolvedSymbols(Arrays.asList(usd));
Modified: trunk/engine/src/main/java/com/metamatrix/query/sql/navigator/AbstractNavigator.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/sql/navigator/AbstractNavigator.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/main/java/com/metamatrix/query/sql/navigator/AbstractNavigator.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -40,7 +40,7 @@
this.visitor = visitor;
}
- protected LanguageVisitor getVisitor() {
+ public LanguageVisitor getVisitor() {
return this.visitor;
}
Modified: trunk/engine/src/test/java/com/metamatrix/query/function/TestFunctionLibrary.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/function/TestFunctionLibrary.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/test/java/com/metamatrix/query/function/TestFunctionLibrary.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -130,7 +130,7 @@
private void helpFindConversions(String fname, Class[] types, FunctionDescriptor[] expected) {
- FunctionDescriptor[] actual = library.determineNecessaryConversions(fname, types, false);
+ FunctionDescriptor[] actual = library.determineNecessaryConversions(fname, null, types, false);
if(expected == null) {
if(actual != null) {
Modified: trunk/engine/src/test/java/com/metamatrix/query/resolver/TestFunctionResolving.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/resolver/TestFunctionResolving.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/test/java/com/metamatrix/query/resolver/TestFunctionResolving.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -30,7 +30,7 @@
import com.metamatrix.api.exception.query.QueryResolverException;
import com.metamatrix.common.types.DataTypeManager;
import com.metamatrix.query.parser.QueryParser;
-import com.metamatrix.query.resolver.util.ResolverVisitorUtil;
+import com.metamatrix.query.resolver.util.ResolverVisitor;
import com.metamatrix.query.sql.symbol.Constant;
import com.metamatrix.query.sql.symbol.ElementSymbol;
import com.metamatrix.query.sql.symbol.Expression;
@@ -44,7 +44,7 @@
Function function = new Function("convert", new Expression[] {new Constant(new Character('a')), new Constant(DataTypeManager.DefaultDataTypes.DATE)}); //$NON-NLS-1$
try {
- ResolverVisitorUtil.resolveFunction(function, FakeMetadataFactory.example1Cached());
+ ResolverVisitor.resolveLanguageObject(function, FakeMetadataFactory.example1Cached());
fail("excpetion expected"); //$NON-NLS-1$
} catch (QueryResolverException err) {
assertEquals("The conversion from char to date is not allowed.", err.getMessage()); //$NON-NLS-1$
@@ -53,10 +53,12 @@
@Test public void testResolvesClosestType() throws Exception {
ElementSymbol e1 = new ElementSymbol("pm1.g1.e1"); //$NON-NLS-1$
+ //dummy resolve to a byte
e1.setType(DataTypeManager.DefaultDataClasses.BYTE);
+ e1.setMetadataID(new Object());
Function function = new Function("abs", new Expression[] {e1}); //$NON-NLS-1$
- ResolverVisitorUtil.resolveFunction(function, FakeMetadataFactory.example1Cached());
+ ResolverVisitor.resolveLanguageObject(function, FakeMetadataFactory.example1Cached());
assertEquals(DataTypeManager.DefaultDataClasses.INTEGER, function.getType());
}
@@ -64,7 +66,7 @@
@Test public void testResolveConvertReference() throws Exception {
Function function = new Function("convert", new Expression[] {new Reference(0), new Constant(DataTypeManager.DefaultDataTypes.BOOLEAN)}); //$NON-NLS-1$
- ResolverVisitorUtil.resolveFunction(function, FakeMetadataFactory.example1Cached());
+ ResolverVisitor.resolveLanguageObject(function, FakeMetadataFactory.example1Cached());
assertEquals(DataTypeManager.DefaultDataClasses.BOOLEAN, function.getType());
assertEquals(DataTypeManager.DefaultDataClasses.BOOLEAN, function.getArgs()[0].getType());
@@ -74,7 +76,7 @@
Function function = new Function("LCASE", new Expression[] {new Reference(0)}); //$NON-NLS-1$
try {
- ResolverVisitorUtil.resolveFunction(function, FakeMetadataFactory.example1Cached());
+ ResolverVisitor.resolveLanguageObject(function, FakeMetadataFactory.example1Cached());
fail("excpetion expected"); //$NON-NLS-1$
} catch (QueryResolverException err) {
assertEquals("The function 'LCASE(?)' has more than one possible signature.", err.getMessage()); //$NON-NLS-1$
@@ -120,7 +122,7 @@
private Function helpResolveFunction(String sql) throws QueryParserException,
QueryResolverException, MetaMatrixComponentException {
Function func = (Function)QueryParser.getQueryParser().parseExpression(sql);
- ResolverVisitorUtil.resolveFunction(func, FakeMetadataFactory.example1Cached());
+ ResolverVisitor.resolveLanguageObject(func, FakeMetadataFactory.example1Cached());
assertEquals(DataTypeManager.DefaultDataClasses.STRING, func.getType());
return func;
}
Modified: trunk/engine/src/test/java/com/metamatrix/query/resolver/TestResolver.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/resolver/TestResolver.java 2009-04-21 16:32:23 UTC (rev 814)
+++ trunk/engine/src/test/java/com/metamatrix/query/resolver/TestResolver.java 2009-04-21 18:38:33 UTC (rev 815)
@@ -4653,4 +4653,8 @@
assertEquals(1, proc.getProjectedSymbols().size());
}
+ public void testSecondPassFunctionResolving() {
+ helpResolve("SELECT pm1.g1.e1 FROM pm1.g1 where lower(?) = e1 "); //$NON-NLS-1$
+ }
+
}
\ No newline at end of file
More information about the teiid-commits
mailing list