[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