[teiid-commits] teiid SVN: r2734 - in trunk/engine/src: main/java/org/teiid/query/optimizer/relational/rules and 17 other directories.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Mon Nov 22 23:10:29 EST 2010


Author: shawkins
Date: 2010-11-22 23:10:27 -0500 (Mon, 22 Nov 2010)
New Revision: 2734

Added:
   trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidator.java
   trunk/engine/src/main/java/org/teiid/query/validator/ValidatorWarning.java
   trunk/engine/src/test/java/org/teiid/query/processor/TestInherintlyUpdatableViews.java
   trunk/engine/src/test/java/org/teiid/query/validator/TestUpdateValidator.java
Removed:
   trunk/engine/src/main/java/org/teiid/query/sql/util/UpdateProcedureGenerator.java
   trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidationVisitor.java
   trunk/engine/src/test/java/org/teiid/query/sql/util/TestUpdateProcedureGenerator.java
Modified:
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseJoinStrategy.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRaiseAccess.java
   trunk/engine/src/main/java/org/teiid/query/resolver/ProcedureContainerResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/QueryResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/command/ExecResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateProcedureResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java
   trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/Delete.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/Insert.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/ProcedureContainer.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/Query.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/Update.java
   trunk/engine/src/main/java/org/teiid/query/sql/proc/Block.java
   trunk/engine/src/main/java/org/teiid/query/sql/symbol/ElementSymbol.java
   trunk/engine/src/main/java/org/teiid/query/sql/visitor/ExpressionMappingVisitor.java
   trunk/engine/src/main/java/org/teiid/query/validator/ValidationVisitor.java
   trunk/engine/src/main/java/org/teiid/query/validator/ValidatorReport.java
   trunk/engine/src/main/resources/org/teiid/query/i18n.properties
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestJoinPushdownRestrictions.java
   trunk/engine/src/test/java/org/teiid/query/resolver/TestResolver.java
   trunk/engine/src/test/java/org/teiid/query/unittest/FakeMetadataFactory.java
   trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java
   trunk/engine/src/test/java/org/teiid/query/validator/TestValidator.java
Log:
TEIID-1349 TEIID-1368 adding better support for updatable views and ensuring that each is a parsing reserved word

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -165,7 +165,7 @@
 		if (command instanceof QueryCommand) {
 			QueryCommand queryCommand = (QueryCommand)command;
 			final HashSet<String> names = new HashSet<String>();
-			if (queryCommand.getWith() != null && !queryCommand.getWith().isEmpty()) {
+			if (queryCommand.getWith() != null) {
 	        	withList = queryCommand.getWith();
 	        	for (WithQueryCommand with : queryCommand.getWith()) {
 	        		Command subCommand = with.getCommand();
@@ -838,7 +838,7 @@
         	if (queryCommand.getLimit() == null) {
         		queryCommand.setOrderBy(null);
         	}
-        	if (merge && queryCommand.getWith() != null && !queryCommand.getWith().isEmpty()) {
+        	if (merge && queryCommand.getWith() != null) {
         		//TODO: should recontext with and merge
         		merge = false;
         	}

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -33,6 +33,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.teiid.api.exception.query.QueryMetadataException;
 import org.teiid.common.buffer.BufferManager;
@@ -776,7 +777,12 @@
      */
     public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, QueryMetadataInterface metadata)
         throws QueryMetadataException, TeiidComponentException {
+    	return usesKey(allElements, null, metadata);
+    }
     
+    public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, Set<GroupSymbol> groups, QueryMetadataInterface metadata)
+    throws QueryMetadataException, TeiidComponentException {
+        
         if(allElements == null || allElements.size() == 0) { 
             return false;
         }    
@@ -790,6 +796,9 @@
         	}
         	ElementSymbol element = (ElementSymbol)ex;
             GroupSymbol group = element.getGroupSymbol();
+            if (groups != null && !groups.contains(group)) {
+            	continue;
+            }
             List<Object> elements = groupMap.get(group);
             if(elements == null) { 
                 elements = new ArrayList<Object>();
@@ -818,7 +827,7 @@
             }                                    
         }
         
-        return false;    
+        return false; 
     }
     
     /**

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseJoinStrategy.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseJoinStrategy.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseJoinStrategy.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -118,7 +118,7 @@
         }
     }
 
-	static void separateCriteria(Collection<GroupSymbol> leftGroups,
+	public static void separateCriteria(Collection<GroupSymbol> leftGroups,
 			Collection<GroupSymbol> rightGroups,
 			List<Expression> leftExpressions,
 			List<Expression> rightExpressions, List<Criteria> crits,

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -49,6 +49,7 @@
 import org.teiid.query.processor.relational.RelationalPlan;
 import org.teiid.query.processor.relational.JoinNode.JoinStrategyType;
 import org.teiid.query.processor.relational.MergeJoinStrategy.SortOption;
+import org.teiid.query.resolver.util.ResolverUtil;
 import org.teiid.query.rewriter.QueryRewriter;
 import org.teiid.query.sql.LanguageObject;
 import org.teiid.query.sql.lang.CompareCriteria;
@@ -535,8 +536,12 @@
 				}
 			}
 		}
-		//TODO: a better check for distinct
-		return distinct || (query.getFrom().getGroups().size() == 1 && NewCalculateCostUtil.usesKey(expressions, metadata));
+		if (distinct) {
+			return true;
+		}
+		HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
+		ResolverUtil.findKeyPreserinvg((FromClause)query.getFrom().getClauses().get(0), keyPreservingGroups, metadata);
+		return NewCalculateCostUtil.usesKey(expressions, keyPreservingGroups, metadata);			
 	}
 
 	private boolean hasCorrelatedReferences(LanguageObject object, SymbolMap correlatedReferences) {

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRaiseAccess.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRaiseAccess.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRaiseAccess.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
@@ -608,52 +609,46 @@
         		/*
         		 * Key joins must be left linear
         		 */
-        		if (sjc == SupportedJoinCriteria.KEY && children.get(1).getGroups().size() != 1) {
+        		if (sjc == SupportedJoinCriteria.KEY && children.get(0).getGroups().size() != 1) {
         			return null;
         		}
         		
 				if(crits != null && !crits.isEmpty()) {
-					List<Object> leftIds = null;
-					List<Object> rightIds = null;
-					GroupSymbol leftGroup = null;
 					for (Criteria crit : crits) {
 				        if (!isSupportedJoinCriteria(sjc, crit, accessModelID, metadata, capFinder, record)) {
 				        	return null;
 				        }
-			        	if (sjc != SupportedJoinCriteria.KEY) {
-			        		continue;
-			        	}
-			        	//key join
-			        	//TODO: this would be much simpler if we could rely on rulechoosejoinstrategy running first
-			        	if (leftIds == null) {
-			        		leftIds = new ArrayList<Object>();
-			        		rightIds = new ArrayList<Object>();
-			        	}
-			        	ElementSymbol leftSymbol = (ElementSymbol)((CompareCriteria)crit).getLeftExpression();
-			        	ElementSymbol rightSymbol = (ElementSymbol)((CompareCriteria)crit).getRightExpression();
-			        	if ((children.get(0).getGroups().contains(leftSymbol.getGroupSymbol()) && children.get(1).getGroups().contains(rightSymbol.getGroupSymbol()))
-			        			|| (children.get(1).getGroups().contains(leftSymbol.getGroupSymbol()) && children.get(0).getGroups().contains(rightSymbol.getGroupSymbol()))) {
-			        		boolean left = children.get(0).getGroups().contains(leftSymbol.getGroupSymbol());
-			        		if (leftGroup == null) {
-			        			leftGroup = left?leftSymbol.getGroupSymbol():rightSymbol.getGroupSymbol();
-			        		} else if (!leftGroup.equals(left?leftSymbol.getGroupSymbol():rightSymbol.getGroupSymbol())) {
-			        			return null;
-			        		}
-			        		if (left) {
-				        		leftIds.add(leftSymbol.getMetadataID());
-				        		rightIds.add(rightSymbol.getMetadataID());
-			        		} else {
-			        			rightIds.add(leftSymbol.getMetadataID());
-				        		leftIds.add(rightSymbol.getMetadataID());
-			        		}
-			        	} else {
-			        		return null;
-			        	}
 					}
-					if (leftIds != null &&
-							!matchesForeignKey(metadata, leftIds, rightIds,	leftGroup) 
-							&& !matchesForeignKey(metadata, rightIds, leftIds, children.get(1).getGroups().iterator().next())) {
-						return null;
+					if (sjc == SupportedJoinCriteria.KEY) {
+						LinkedList<Expression> leftExpressions = new LinkedList<Expression>();
+						LinkedList<Expression> rightExpressions = new LinkedList<Expression>();
+						RuleChooseJoinStrategy.separateCriteria(children.get(0).getGroups(), children.get(1).getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
+						ArrayList<Object> leftIds = new ArrayList<Object>(leftExpressions.size());
+						ArrayList<Object> rightIds = new ArrayList<Object>(rightExpressions.size());
+						for (Expression expr : leftExpressions) {
+							if (expr instanceof ElementSymbol) {
+								leftIds.add(((ElementSymbol) expr).getMetadataID());
+							}
+						}
+						GroupSymbol rightGroup = null;
+						for (Expression expr : rightExpressions) {
+							if (expr instanceof ElementSymbol) {
+								ElementSymbol es = (ElementSymbol) expr; 
+								if (rightGroup == null) {
+									rightGroup = es.getGroupSymbol();
+								} else if (!rightGroup.equals(es.getGroupSymbol())) {
+									return null;
+								}
+								rightIds.add(es.getMetadataID());
+							}
+						}
+						if (rightGroup == null) {
+							return null;
+						}
+						if (!matchesForeignKey(metadata, leftIds, rightIds,	children.get(0).getGroups().iterator().next(), true) 
+								&& !matchesForeignKey(metadata, rightIds, leftIds, rightGroup, true)) {
+							return null;
+						}
 					}
                 } else if (sjc != SupportedJoinCriteria.ANY) {
                 	return null; //cross join not supported
@@ -708,21 +703,18 @@
 		return true;
     }
 
-    /**
-     * TODO: gracefully handle too much criteria
-     */
-	private static boolean matchesForeignKey(QueryMetadataInterface metadata,
-			List<Object> leftIds, List<Object> rightIds, GroupSymbol leftGroup)
+	public static boolean matchesForeignKey(QueryMetadataInterface metadata,
+			Collection<Object> leftIds, Collection<Object> rightIds, GroupSymbol leftGroup, boolean exact)
 			throws TeiidComponentException, QueryMetadataException {
 		Collection fks = metadata.getForeignKeysInGroup(leftGroup.getMetadataID());
 		for (Object fk : fks) {
 			List fkColumns = metadata.getElementIDsInKey(fk);
-			if (!leftIds.containsAll(fkColumns) || leftIds.size() != fkColumns.size()) {
+			if ((exact && leftIds.size() != fkColumns.size()) || !leftIds.containsAll(fkColumns)) {
 				continue;
 			}
 			Object pk = metadata.getPrimaryKeyIDForForeignKeyID(fk);
 			List pkColumns = metadata.getElementIDsInKey(pk);
-			if (rightIds.containsAll(pkColumns) && rightIds.size() == pkColumns.size()) {
+			if ((!exact || rightIds.size() == pkColumns.size()) && rightIds.containsAll(pkColumns)) {
 				return true;
 			}
 		}

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/ProcedureContainerResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/ProcedureContainerResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/ProcedureContainerResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -23,6 +23,7 @@
 package org.teiid.query.resolver;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -39,6 +40,8 @@
 import org.teiid.query.metadata.TempMetadataStore;
 import org.teiid.query.metadata.TempMetadataID.Type;
 import org.teiid.query.parser.QueryParser;
+import org.teiid.query.report.ReportItem;
+import org.teiid.query.resolver.command.UpdateProcedureResolver;
 import org.teiid.query.resolver.util.ResolverUtil;
 import org.teiid.query.sql.ProcedureReservedWords;
 import org.teiid.query.sql.lang.Command;
@@ -47,6 +50,9 @@
 import org.teiid.query.sql.proc.CreateUpdateProcedureCommand;
 import org.teiid.query.sql.symbol.ElementSymbol;
 import org.teiid.query.sql.symbol.GroupSymbol;
+import org.teiid.query.validator.UpdateValidator;
+import org.teiid.query.validator.ValidatorFailure;
+import org.teiid.query.validator.UpdateValidator.UpdateInfo;
 
 
 public abstract class ProcedureContainerResolver implements CommandResolver {
@@ -127,7 +133,7 @@
      */
     protected abstract String getPlan(QueryMetadataInterface metadata,
                            GroupSymbol group) throws TeiidComponentException,
-                                             QueryMetadataException;
+                                             QueryMetadataException, QueryResolverException;
         
     /**
      * Find all metadata defined by this command for it's children.  This metadata should be collected 
@@ -201,15 +207,43 @@
 			throws TeiidComponentException, QueryMetadataException,
 			QueryResolverException {
 		if(!procCommand.getGroup().isTempGroupSymbol() && metadata.isVirtualGroup(procCommand.getGroup().getMetadataID())) {
-            String plan = getPlan(metadata, procCommand.getGroup());
-            
-            if(plan == null) {
-                throw new QueryResolverException("ERR.015.008.0009", QueryPlugin.Util.getString("ERR.015.008.0009", procCommand.getGroup(), procCommand.getClass().getSimpleName())); //$NON-NLS-1$ //$NON-NLS-2$
-            }
-            return plan;
+            return getPlan(metadata, procCommand.getGroup());
         }
 		return null;
 	}
+	
+	public static UpdateInfo getUpdateInfo(GroupSymbol group, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException, QueryResolverException {
+		//if this is not inherently updatable, just return null
+		if(group.isTempGroupSymbol() || !metadata.isVirtualGroup(group.getMetadataID()) || (
+				metadata.getUpdatePlan(group.getMetadataID()) != null && 
+				metadata.getDeletePlan(group.getMetadataID()) != null && 
+				metadata.getInsertPlan(group.getMetadataID()) != null)) {
+			return null;
+		}
+    	UpdateInfo info = (UpdateInfo)metadata.getFromMetadataCache(group.getMetadataID(), "UpdateInfo"); //$NON-NLS-1$
+    	if (info == null) {
+    		
+            List<ElementSymbol> elements = ResolverUtil.resolveElementsInGroup(group, metadata);
+
+    		UpdateValidator validator = new UpdateValidator(metadata, 
+    				metadata.getUpdatePlan(group.getMetadataID()) == null, 
+    				metadata.getDeletePlan(group.getMetadataID()) == null, 
+    				metadata.getInsertPlan(group.getMetadataID()) == null);
+    		validator.validate(UpdateProcedureResolver.getQueryTransformCmd(group, metadata), elements);
+    		info = validator.getUpdateInfo();
+    		for (ReportItem item : (Collection<ReportItem>)validator.getReport().getItems()) {
+				if (item instanceof ValidatorFailure) {
+					info.setValidationError(item.getMessage());
+					break;
+				}
+    		}
+    		metadata.addToMetadataCache(group.getMetadataID(), "UpdateInfo", info); //$NON-NLS-1$
+    	}
+    	if (info.getValidationError() != null) {
+    		throw new QueryResolverException(info.getValidationError());
+    	}
+    	return info;
+	}
     
     /** 
      * @param metadata
@@ -223,6 +257,7 @@
         // Resolve group so we can tell whether it is an update procedure
         GroupSymbol group = procCommand.getGroup();
         ResolverUtil.resolveGroup(group, metadata);
+        procCommand.setUpdateInfo(ProcedureContainerResolver.getUpdateInfo(group, metadata));
     }
 
 	public static GroupSymbol addScalarGroup(String name, TempMetadataStore metadata, GroupContext externalGroups, List symbols) {

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/QueryResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/QueryResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/QueryResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -224,7 +224,7 @@
     public static boolean isXMLQuery(Query query, QueryMetadataInterface metadata)
      throws TeiidComponentException, QueryMetadataException, QueryResolverException {
 
-        if (query.getWith() != null && !query.getWith().isEmpty()) {
+        if (query.getWith() != null) {
         	return false;
         }
 

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/command/ExecResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/command/ExecResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/command/ExecResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -316,16 +316,21 @@
     }
 
     /** 
+     * @throws QueryResolverException 
      * @see org.teiid.query.resolver.ProcedureContainerResolver#getPlan(org.teiid.query.metadata.QueryMetadataInterface, org.teiid.query.sql.symbol.GroupSymbol)
      */
     protected String getPlan(QueryMetadataInterface metadata,
                              GroupSymbol group) throws TeiidComponentException,
-                                               QueryMetadataException {
+                                               QueryMetadataException, QueryResolverException {
         StoredProcedureInfo storedProcedureInfo = metadata.getStoredProcedureInfoForProcedure(group.getCanonicalName());
         
         //if there is a query plan associated with the procedure, get it.
         QueryNode plan = storedProcedureInfo.getQueryPlan();
         
+        if (plan.getQuery() == null) {
+            throw new QueryResolverException("ERR.015.008.0009", QueryPlugin.Util.getString("ERR.015.008.0009", group, "Stored Procedure")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        }
+        
         return plan.getQuery();
     }
 }

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -139,7 +139,7 @@
 
 	static void resolveWith(TempMetadataAdapter metadata,
 			AnalysisRecord analysis, QueryCommand query) throws QueryResolverException, TeiidComponentException {
-		if (query.getWith() == null || query.getWith().isEmpty()) {
+		if (query.getWith() == null) {
 			return;
 		}
 		LinkedHashSet<GroupSymbol> discoveredGroups = new LinkedHashSet<GroupSymbol>();

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateProcedureResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateProcedureResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateProcedureResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -123,7 +123,7 @@
     /**
 	 * Get the command for the transformation query that defines this virtual group.
 	 */
-    private Command getQueryTransformCmd(GroupSymbol virtualGroup, QueryMetadataInterface metadata)
+    public static Command getQueryTransformCmd(GroupSymbol virtualGroup, QueryMetadataInterface metadata)
     throws QueryMetadataException, QueryResolverException, TeiidComponentException {
     	Command transformCmd = (Command)metadata.getFromMetadataCache(virtualGroup.getMetadataID(), "transformation/select"); //$NON-NLS-1$
     	if (transformCmd != null) {

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/command/UpdateResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -45,6 +45,7 @@
 import org.teiid.query.resolver.util.ResolverVisitor;
 import org.teiid.query.sql.ProcedureReservedWords;
 import org.teiid.query.sql.lang.Command;
+import org.teiid.query.sql.lang.SetClause;
 import org.teiid.query.sql.lang.Update;
 import org.teiid.query.sql.symbol.Constant;
 import org.teiid.query.sql.symbol.ElementSymbol;
@@ -69,6 +70,9 @@
         // Resolve elements and functions
         Set<GroupSymbol> groups = new HashSet<GroupSymbol>();
         groups.add(update.getGroup());
+        for (SetClause clause : update.getChangeList().getClauses()) {
+        	ResolverVisitor.resolveLanguageObject(clause.getSymbol(), groups, null, metadata);
+		}
         ResolverVisitor.resolveLanguageObject(update, groups, update.getExternalGroupContexts(), metadata);
         QueryResolver.resolveSubqueries(command, metadata, analysis);
     }

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -26,12 +26,15 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.teiid.api.exception.query.QueryMetadataException;
 import org.teiid.api.exception.query.QueryResolverException;
@@ -50,13 +53,20 @@
 import org.teiid.query.metadata.TempMetadataAdapter;
 import org.teiid.query.metadata.TempMetadataID;
 import org.teiid.query.metadata.TempMetadataStore;
+import org.teiid.query.optimizer.relational.rules.RuleChooseJoinStrategy;
+import org.teiid.query.optimizer.relational.rules.RuleRaiseAccess;
 import org.teiid.query.sql.LanguageObject;
+import org.teiid.query.sql.lang.Criteria;
+import org.teiid.query.sql.lang.FromClause;
+import org.teiid.query.sql.lang.JoinPredicate;
+import org.teiid.query.sql.lang.JoinType;
 import org.teiid.query.sql.lang.Limit;
 import org.teiid.query.sql.lang.OrderBy;
 import org.teiid.query.sql.lang.Query;
 import org.teiid.query.sql.lang.QueryCommand;
 import org.teiid.query.sql.lang.SetQuery;
 import org.teiid.query.sql.lang.SubqueryContainer;
+import org.teiid.query.sql.lang.UnaryFromClause;
 import org.teiid.query.sql.symbol.AbstractCaseExpression;
 import org.teiid.query.sql.symbol.AggregateSymbol;
 import org.teiid.query.sql.symbol.AliasSymbol;
@@ -930,5 +940,81 @@
 	    symbol.setOutputDefinition(definition);
 	    symbol.setOutputName(name);
 	}
+	
+	public static void findKeyPreserinvg(FromClause clause, Set<GroupSymbol> keyPreservingGroups, QueryMetadataInterface metadata)
+	throws TeiidComponentException, QueryMetadataException {
+		if (clause instanceof UnaryFromClause) {
+			UnaryFromClause ufc = (UnaryFromClause)clause;
+		    
+			if (!metadata.getUniqueKeysInGroup(ufc.getGroup().getMetadataID()).isEmpty()) {
+				keyPreservingGroups.add(ufc.getGroup());
+			}
+		} 
+		if (clause instanceof JoinPredicate) {
+			JoinPredicate jp = (JoinPredicate)clause;
+			if (jp.getJoinType() == JoinType.JOIN_CROSS || jp.getJoinType() == JoinType.JOIN_FULL_OUTER) {
+				return;
+			}
+			HashSet<GroupSymbol> leftPk = new HashSet<GroupSymbol>();
+			findKeyPreserinvg(jp.getLeftClause(), leftPk, metadata);
+			HashSet<GroupSymbol> rightPk = new HashSet<GroupSymbol>();
+			findKeyPreserinvg(jp.getRightClause(), rightPk, metadata);
+			
+			if (leftPk.isEmpty() && rightPk.isEmpty()) {
+				return;
+			}
+			
+			HashSet<GroupSymbol> leftGroups = new HashSet<GroupSymbol>();
+			HashSet<GroupSymbol> rightGroups = new HashSet<GroupSymbol>();
+			jp.getLeftClause().collectGroups(leftGroups);
+			jp.getRightClause().collectGroups(rightGroups);
+			
+			LinkedList<Expression> leftExpressions = new LinkedList<Expression>();
+			LinkedList<Expression> rightExpressions = new LinkedList<Expression>();
+			RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, jp.getJoinCriteria(), new LinkedList<Criteria>());
+		    
+			HashMap<List<GroupSymbol>, List<HashSet<Object>>> crits = new HashMap<List<GroupSymbol>, List<HashSet<Object>>>();
+			
+			for (int i = 0; i < leftExpressions.size(); i++) {
+				Expression lexpr = leftExpressions.get(i);
+				Expression rexpr = rightExpressions.get(i);
+				if (!(lexpr instanceof ElementSymbol) || !(rexpr instanceof ElementSymbol)) {
+					continue;
+				}
+				ElementSymbol les = (ElementSymbol)lexpr;
+				ElementSymbol res = (ElementSymbol)rexpr;
+				List<GroupSymbol> tbls = Arrays.asList(les.getGroupSymbol(), res.getGroupSymbol());
+				List<HashSet<Object>> ids = crits.get(tbls);
+				if (ids == null) {
+					ids = Arrays.asList(new HashSet<Object>(), new HashSet<Object>());
+					crits.put(tbls, ids);
+				}
+				ids.get(0).add(les.getMetadataID());
+				ids.get(1).add(res.getMetadataID());
+			}
+			if (!leftPk.isEmpty() && (jp.getJoinType() == JoinType.JOIN_INNER || jp.getJoinType() == JoinType.JOIN_LEFT_OUTER)) {
+				findKeyPreserving(keyPreservingGroups, leftPk, crits, true, metadata);
+			} 
+			if (!rightPk.isEmpty() && (jp.getJoinType() == JoinType.JOIN_INNER || jp.getJoinType() == JoinType.JOIN_RIGHT_OUTER)) {
+				findKeyPreserving(keyPreservingGroups, rightPk, crits, false, metadata);
+			}
+		}
+	}
+
+	static private void findKeyPreserving(Set<GroupSymbol> keyPreservingGroups,
+		HashSet<GroupSymbol> pk,
+		HashMap<List<GroupSymbol>, List<HashSet<Object>>> crits, boolean left, QueryMetadataInterface metadata)
+		throws TeiidComponentException, QueryMetadataException {
+		for (GroupSymbol gs : pk) {
+			for (Map.Entry<List<GroupSymbol>, List<HashSet<Object>>> entry : crits.entrySet()) {
+				if (!entry.getKey().get(left?0:1).equals(gs)) {
+					continue;
+				}
+				if (RuleRaiseAccess.matchesForeignKey(metadata, entry.getValue().get(left?0:1), entry.getValue().get(left?1:0), gs, false)) {
+					keyPreservingGroups.add(gs);
+				}
+			}
+		}
+	}
     
 }

Modified: trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -68,6 +68,7 @@
 import org.teiid.query.optimizer.relational.rules.RulePlaceAccess;
 import org.teiid.query.optimizer.relational.rules.RuleMergeCriteria.PlannedResult;
 import org.teiid.query.processor.relational.DependentValueSource;
+import org.teiid.query.resolver.ProcedureContainerResolver;
 import org.teiid.query.resolver.QueryResolver;
 import org.teiid.query.resolver.util.ResolverUtil;
 import org.teiid.query.resolver.util.ResolverVisitor;
@@ -98,6 +99,7 @@
 import org.teiid.query.sql.lang.NotCriteria;
 import org.teiid.query.sql.lang.OrderBy;
 import org.teiid.query.sql.lang.OrderByItem;
+import org.teiid.query.sql.lang.ProcedureContainer;
 import org.teiid.query.sql.lang.Query;
 import org.teiid.query.sql.lang.QueryCommand;
 import org.teiid.query.sql.lang.SPParameter;
@@ -118,7 +120,9 @@
 import org.teiid.query.sql.lang.WithQueryCommand;
 import org.teiid.query.sql.lang.XMLTable;
 import org.teiid.query.sql.lang.PredicateCriteria.Negatable;
+import org.teiid.query.sql.navigator.DeepPostOrderNavigator;
 import org.teiid.query.sql.navigator.PostOrderNavigator;
+import org.teiid.query.sql.proc.AssignmentStatement;
 import org.teiid.query.sql.proc.Block;
 import org.teiid.query.sql.proc.CommandStatement;
 import org.teiid.query.sql.proc.CreateUpdateProcedureCommand;
@@ -155,6 +159,8 @@
 import org.teiid.query.sql.visitor.PredicateCollectorVisitor;
 import org.teiid.query.sql.visitor.EvaluatableVisitor.EvaluationLevel;
 import org.teiid.query.util.CommandContext;
+import org.teiid.query.validator.UpdateValidator.UpdateInfo;
+import org.teiid.query.validator.UpdateValidator.UpdateMapping;
 import org.teiid.translator.SourceSystemFunctions;
 
 
@@ -661,7 +667,7 @@
 		for (Iterator<Criteria> crits = current.iterator(); crits.hasNext();) {
 			PlannedResult plannedResult = rmc.findSubquery(crits.next());
 			if (plannedResult.not || plannedResult.query == null || plannedResult.query.getProcessorPlan() != null 
-					|| (plannedResult.query.getWith() != null && !plannedResult.query.getWith().isEmpty())) {
+					|| plannedResult.query.getWith() != null) {
 				continue;
 			}
 			if (plannedResult.query.getCorrelatedReferences() == null) {
@@ -678,10 +684,14 @@
 			if (!rmc.planQuery(groups, true, plannedResult)) {
 				continue;
 			}
-			if (query.getFrom().getGroups().size() != 1 || !NewCalculateCostUtil.usesKey(plannedResult.leftExpressions, metadata)) {
-				//TODO: check for grouping or distinct in the outer query
+			HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
+			if (query.getFrom().getClauses().size() > 1) {
 				continue;
 			}
+			ResolverUtil.findKeyPreserinvg((FromClause)query.getFrom().getClauses().get(0), keyPreservingGroups, metadata);
+			if (!NewCalculateCostUtil.usesKey(plannedResult.leftExpressions, keyPreservingGroups, metadata)) {
+				continue;
+			}
 			crits.remove();
 			
 			GroupSymbol viewName = RulePlaceAccess.recontextSymbol(new GroupSymbol("X"), names); //$NON-NLS-1$
@@ -937,14 +947,14 @@
      * @param query
      * @throws QueryValidatorException
      */
-    private Insert rewriteSelectInto(Query query) throws TeiidComponentException, TeiidProcessingException{
+    private Insert rewriteSelectInto(Query query) throws TeiidProcessingException{
         Into into = query.getInto();
         try {
             List<ElementSymbol> allIntoElements = Util.deepClone(ResolverUtil.resolveElementsInGroup(into.getGroup(), metadata), ElementSymbol.class);
             Insert insert = new Insert(into.getGroup(), allIntoElements, Collections.emptyList());
             query.setInto(null);
             insert.setQueryExpression(query);
-            return correctDatatypes(insert);
+            return rewriteInsert(correctDatatypes(insert));
         } catch (QueryMetadataException err) {
             throw new QueryValidatorException(err, err.getMessage());
         } catch (TeiidComponentException err) {
@@ -2414,7 +2424,24 @@
     }
 
 	private Insert rewriteInsert(Insert insert) throws TeiidComponentException, TeiidProcessingException{
-        
+		UpdateInfo info = insert.getUpdateInfo();
+		if (info != null && info.isInherentInsert()) {
+			//pass through
+			UpdateMapping mapping = info.findUpdateMapping(insert.getVariables(), false);
+			if (mapping == null) {
+				throw new QueryValidatorException(QueryPlugin.Util.getString("ValidationVisitor.nonUpdatable", insert.getVariables())); //$NON-NLS-1$
+			}
+			Map<ElementSymbol, ElementSymbol> symbolMap = mapping.getUpdatableViewSymbols();
+			List<ElementSymbol> mappedSymbols = new ArrayList<ElementSymbol>(insert.getVariables().size());
+			for (ElementSymbol symbol : (List<ElementSymbol>)insert.getVariables()) {
+				mappedSymbols.add(symbolMap.get(symbol));
+			}
+			insert.setVariables(mappedSymbols);
+			insert.setGroup(mapping.getGroup().clone());
+			insert.setUpdateInfo(ProcedureContainerResolver.getUpdateInfo(insert.getGroup(), metadata));
+			return rewriteInsert(insert);
+		}
+
         if ( insert.getQueryExpression() != null ) {
         	insert.setQueryExpression((QueryCommand)rewriteCommand(insert.getQueryExpression(), true));
         	return correctDatatypes(insert);
@@ -2523,7 +2550,68 @@
         }
     }
 
-	private Update rewriteUpdate(Update update) throws TeiidComponentException, TeiidProcessingException{
+	private Command rewriteUpdate(Update update) throws TeiidComponentException, TeiidProcessingException{
+		UpdateInfo info = update.getUpdateInfo();
+		if (info != null && info.isInherentUpdate()) {
+			UpdateMapping mapping = info.findUpdateMapping(update.getChangeList().getClauseMap().keySet(), false);
+			if (mapping == null) {
+				throw new QueryValidatorException(QueryPlugin.Util.getString("ValidationVisitor.nonUpdatable", update.getChangeList().getClauseMap().keySet())); //$NON-NLS-1$
+			}
+			Map<ElementSymbol, ElementSymbol> symbolMap = mapping.getUpdatableViewSymbols();
+			if (info.isSimple()) {
+				update.setGroup(mapping.getGroup().clone());
+				for (SetClause clause : update.getChangeList().getClauses()) {
+					clause.setSymbol(symbolMap.get(clause.getSymbol()));
+				}
+				//TODO: properly handle correlated references
+				DeepPostOrderNavigator.doVisit(update, new ExpressionMappingVisitor(symbolMap, true));
+				if (info.getViewDefinition().getCriteria() != null) {
+					update.setCriteria(Criteria.combineCriteria(update.getCriteria(), (Criteria)info.getViewDefinition().getCriteria().clone()));
+				}
+				//resolve
+				update.setUpdateInfo(ProcedureContainerResolver.getUpdateInfo(update.getGroup(), metadata));
+				return rewriteUpdate(update);
+			} 
+			Query query = (Query)info.getViewDefinition().clone();
+			query.setOrderBy(null);
+			SymbolMap expressionMapping = SymbolMap.createSymbolMap(update.getGroup(), query.getProjectedSymbols(), metadata);
+			
+			ArrayList<SingleElementSymbol> selectSymbols = new ArrayList<SingleElementSymbol>(update.getChangeList().getClauses().size());
+			int i = 0;
+			for (SetClause clause : update.getChangeList().getClauses()) {
+				Expression ex = clause.getValue();
+				SingleElementSymbol selectSymbol = null;
+				if (!EvaluatableVisitor.willBecomeConstant(ex)) {
+					if (!(ex instanceof SingleElementSymbol)) {
+						selectSymbol = new ExpressionSymbol("expr", ex); //$NON-NLS-1$
+					} else {
+						selectSymbol = (SingleElementSymbol)ex;
+					}
+					selectSymbols.add(new AliasSymbol("s_" +i, selectSymbol)); //$NON-NLS-1$
+					ex = new ElementSymbol("s_" +i); //$NON-NLS-1$
+				}
+				clause.setSymbol(symbolMap.get(clause.getSymbol()));
+				i++;
+			}
+			query.setSelect(new Select(selectSymbols));
+			ExpressionMappingVisitor emv = new ExpressionMappingVisitor(expressionMapping.asMap(), true);
+			PostOrderNavigator.doVisit(query.getSelect(), emv);
+			
+			Criteria crit = update.getCriteria();
+			if (crit != null) {
+				PostOrderNavigator.doVisit(crit, emv);
+				query.setCriteria(Criteria.combineCriteria(query.getCriteria(), crit));
+			}
+			
+			Update newUpdate = new Update();
+			newUpdate.setChangeList(update.getChangeList());
+			newUpdate.setGroup(mapping.getGroup().clone());
+			
+			List<Criteria> pkCriteria = createPkCriteria(mapping, query, i);
+			newUpdate.setCriteria(Criteria.combineCriteria(newUpdate.getCriteria(), new CompoundCriteria(pkCriteria)));
+			return createUpdateProcedure(update, query, newUpdate);
+		}
+		
 		if (commandType == Command.TYPE_UPDATE && variables != null) {
 	        SetClauseList newChangeList = new SetClauseList();
 	        for (SetClause entry : update.getChangeList().getClauses()) {
@@ -2549,6 +2637,44 @@
 
 		return update;
 	}
+
+	private Command createUpdateProcedure(ProcedureContainer update, Query query,
+			ProcedureContainer newUpdate) throws QueryResolverException,
+			TeiidComponentException, TeiidProcessingException {
+		CreateUpdateProcedureCommand cupc = new CreateUpdateProcedureCommand();
+		Block parent = new Block();
+		Block b = new Block();
+		LoopStatement ls = new LoopStatement(b, query, "X"); //$NON-NLS-1$
+		parent.addStatement(ls);
+		b.addStatement(new CommandStatement(newUpdate));
+		AssignmentStatement as = new AssignmentStatement();
+		ElementSymbol rowsUpdate = new ElementSymbol(ProcedureReservedWords.VARIABLES+ElementSymbol.SEPARATOR+ProcedureReservedWords.ROWS_UPDATED);
+		as.setVariable(rowsUpdate);
+		as.setExpression(new Function("+", new Expression[] {rowsUpdate, new Constant(1)})); //$NON-NLS-1$
+		b.addStatement(as);
+		cupc.setBlock(parent);
+		cupc.setVirtualGroup(update.getGroup());
+		QueryResolver.resolveCommand(cupc, metadata);
+		return rewriteUpdateProcedure(cupc);
+	}
+
+	private List<Criteria> createPkCriteria(UpdateMapping mapping, Query query,
+			int i) throws TeiidComponentException, QueryMetadataException {
+		Object pk = metadata.getPrimaryKey(mapping.getGroup().getMetadataID());
+		if (pk == null) {
+			pk = metadata.getUniqueKeysInGroup(mapping.getGroup().getMetadataID()).iterator().next();
+		}
+		List<Object> ids = metadata.getElementIDsInKey(pk);
+		List<Criteria> pkCriteria = new ArrayList<Criteria>(ids.size());
+		for (Object object : ids) {
+			ElementSymbol es = new ElementSymbol(mapping.getCorrelatedName().getName() + ElementSymbol.SEPARATOR + SingleElementSymbol.getShortName(metadata.getFullName(object)));
+			query.getSelect().addSymbol(new AliasSymbol("s_" +i, es)); //$NON-NLS-1$
+			es = new ElementSymbol(mapping.getGroup().getName() + ElementSymbol.SEPARATOR + SingleElementSymbol.getShortName(metadata.getFullName(object)));
+			pkCriteria.add(new CompareCriteria(es, CompareCriteria.EQ, new ElementSymbol("X.s_" + i))); //$NON-NLS-1$
+			i++;
+		}
+		return pkCriteria;
+	}
 	
     /**
      * Checks variables in an expression, if the variables are INPUT variables and if
@@ -2582,7 +2708,42 @@
         return true;
     }
 
-	private Delete rewriteDelete(Delete delete) throws TeiidComponentException, TeiidProcessingException{
+	private Command rewriteDelete(Delete delete) throws TeiidComponentException, TeiidProcessingException{
+		UpdateInfo info = delete.getUpdateInfo();
+		if (info != null && info.isInherentDelete()) {
+			UpdateMapping mapping = info.getDeleteTarget();
+			if (info.isSimple()) {
+				delete.setGroup(mapping.getGroup().clone());
+				//TODO: properly handle correlated references
+				DeepPostOrderNavigator.doVisit(delete, new ExpressionMappingVisitor(mapping.getUpdatableViewSymbols(), true));
+				delete.setUpdateInfo(ProcedureContainerResolver.getUpdateInfo(delete.getGroup(), metadata));
+				if (info.getViewDefinition().getCriteria() != null) {
+					delete.setCriteria(Criteria.combineCriteria(delete.getCriteria(), (Criteria)info.getViewDefinition().getCriteria().clone()));
+				}
+				return rewriteDelete(delete);
+			}
+			
+			Query query = (Query)info.getViewDefinition().clone();
+			query.setOrderBy(null);
+			SymbolMap expressionMapping = SymbolMap.createSymbolMap(delete.getGroup(), query.getProjectedSymbols(), metadata);
+			
+			query.setSelect(new Select());
+			ExpressionMappingVisitor emv = new ExpressionMappingVisitor(expressionMapping.asMap(), true);
+			PostOrderNavigator.doVisit(query.getSelect(), emv);
+			
+			Criteria crit = delete.getCriteria();
+			if (crit != null) {
+				PostOrderNavigator.doVisit(crit, emv);
+				query.setCriteria(Criteria.combineCriteria(query.getCriteria(), crit));
+			}
+			
+			Delete newUpdate = new Delete();
+			newUpdate.setGroup(mapping.getGroup().clone());
+			
+			List<Criteria> pkCriteria = createPkCriteria(mapping, query, 0);
+			newUpdate.setCriteria(Criteria.combineCriteria(newUpdate.getCriteria(), new CompoundCriteria(pkCriteria)));
+			return createUpdateProcedure(delete, query, newUpdate);
+		}
 		// Rewrite criteria
 		Criteria crit = delete.getCriteria();
 		if(crit != null) {

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/Delete.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/Delete.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/Delete.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -178,7 +178,7 @@
 	public Object clone() {
 	    GroupSymbol copyGroup = null;
 	    if(group != null) { 
-	    	copyGroup = (GroupSymbol) group.clone();    
+	    	copyGroup = group.clone();    
 	    }
 	    
 		Criteria copyCrit = null;

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/Insert.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/Insert.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/Insert.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -31,6 +31,7 @@
 import org.teiid.common.buffer.TupleSource;
 import org.teiid.core.util.EquivalenceUtil;
 import org.teiid.core.util.HashCodeUtil;
+import org.teiid.query.sql.LanguageObject;
 import org.teiid.query.sql.LanguageVisitor;
 import org.teiid.query.sql.ProcedureReservedWords;
 import org.teiid.query.sql.symbol.Constant;
@@ -250,20 +251,15 @@
 	public Object clone() {
 	    GroupSymbol copyGroup = null;
 	    if(group != null) { 
-	    	copyGroup = (GroupSymbol) group.clone();    
+	    	copyGroup = group.clone();    
 	    }
 	    
-	    List copyVars = new LinkedList();
-    	Iterator iter = getVariables().iterator();
-    	while(iter.hasNext()) { 
-    		ElementSymbol element = (ElementSymbol) iter.next();
-    		copyVars.add( element.clone() );    
-    	}    
+	    List<ElementSymbol> copyVars = LanguageObject.Util.deepClone(getVariables(), ElementSymbol.class);
 
         List copyVals = new LinkedList();
 
         if ( getValues() != null && getValues().size() > 0 ) {
-        	iter = getValues().iterator();
+        	Iterator iter = getValues().iterator();
         	while(iter.hasNext()) { 
         		Expression expression = (Expression) iter.next();
         		copyVals.add( expression.clone() );    

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/ProcedureContainer.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/ProcedureContainer.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/ProcedureContainer.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -27,16 +27,19 @@
 import org.teiid.query.sql.symbol.ElementSymbol;
 import org.teiid.query.sql.symbol.Expression;
 import org.teiid.query.sql.symbol.GroupSymbol;
+import org.teiid.query.validator.UpdateValidator.UpdateInfo;
 
 
 public abstract class ProcedureContainer extends Command {
 
     private int updateCount = -1;
+    private UpdateInfo updateInfo;
     
     public abstract GroupSymbol getGroup();
     
     protected void copyMetadataState(ProcedureContainer copy) {
         super.copyMetadataState(copy);
+        copy.setUpdateInfo(this.getUpdateInfo());
         copy.updateCount = updateCount;
     }
     
@@ -62,4 +65,13 @@
     }
     
     public abstract LinkedHashMap<ElementSymbol, Expression> getProcedureParameters();
+    
+    public UpdateInfo getUpdateInfo() {
+		return updateInfo;
+	}
+    
+    public void setUpdateInfo(UpdateInfo updateInfo) {
+		this.updateInfo = updateInfo;
+	}
+    
 }

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/Query.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/Query.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/Query.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -453,7 +453,7 @@
     
     public boolean hasAggregates() {
     	return getGroupBy() != null 
-    	|| (getHaving() != null && AggregateSymbolCollectorVisitor.getAggregates(getHaving(), false).isEmpty()) 
+    	|| getHaving() != null 
     	|| !AggregateSymbolCollectorVisitor.getAggregates(getSelect(), false).isEmpty();
     }
 }  // END CLASS

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/Update.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/Update.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/Update.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -224,7 +224,7 @@
 		Update copy = new Update();
 		
 	    if(group != null) { 
-	        copy.setGroup((GroupSymbol) group.clone());
+	        copy.setGroup(group.clone());
 	    }
 	    
 	    copy.setChangeList((SetClauseList)this.changeList.clone());

Modified: trunk/engine/src/main/java/org/teiid/query/sql/proc/Block.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/proc/Block.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/proc/Block.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -110,7 +110,7 @@
 	 * Deep clone statement to produce a new identical block.
 	 * @return Deep clone 
 	 */
-	public Object clone() {		
+	public Block clone() {		
 		Block copy = new Block();
 		for (Statement statement : statements) {
 			copy.addStatement((Statement)statement.clone());

Modified: trunk/engine/src/main/java/org/teiid/query/sql/symbol/ElementSymbol.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/symbol/ElementSymbol.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/symbol/ElementSymbol.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -44,7 +44,7 @@
     
     private GroupSymbol groupSymbol;
     private Object metadataID;
-	private Class type;
+	private Class<?> type;
     private boolean isExternalReference = false;
         
     private DisplayMode displayMode = DisplayMode.OUTPUT_NAME;
@@ -160,7 +160,7 @@
 	 * Get the type of the symbol
 	 * @return Type of the symbol, may be null before resolution
 	 */
-	public Class getType() {
+	public Class<?> getType() {
 		return this.type;
 	}	
 	
@@ -168,7 +168,7 @@
 	 * Set the type of the symbol
 	 * @param type New type
 	 */
-	public void setType(Class type) {
+	public void setType(Class<?> type) {
 		this.type = type;
 	}	
 
@@ -192,7 +192,7 @@
 	public Object clone() {
 		ElementSymbol copy = new ElementSymbol(getName(), getCanonical());
 		if(getGroupSymbol() != null) { 
-			copy.setGroupSymbol((GroupSymbol) getGroupSymbol().clone());
+			copy.setGroupSymbol(getGroupSymbol().clone());
 		}
 		copy.setMetadataID(getMetadataID());
 		copy.setType(getType());

Deleted: trunk/engine/src/main/java/org/teiid/query/sql/util/UpdateProcedureGenerator.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/util/UpdateProcedureGenerator.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/util/UpdateProcedureGenerator.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -1,196 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
-
-package org.teiid.query.sql.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.teiid.api.exception.query.QueryMetadataException;
-import org.teiid.core.TeiidComponentException;
-import org.teiid.query.QueryPlugin;
-import org.teiid.query.metadata.QueryMetadataInterface;
-import org.teiid.query.metadata.SupportConstants;
-import org.teiid.query.sql.ProcedureReservedWords;
-import org.teiid.query.sql.lang.*;
-import org.teiid.query.sql.navigator.PreOrderNavigator;
-import org.teiid.query.sql.proc.*;
-import org.teiid.query.sql.symbol.*;
-import org.teiid.query.validator.UpdateValidationVisitor;
-import org.teiid.query.validator.ValidatorReport;
-
-
-/**
- * Use existing query transformation to create a simple insert/update/delete procedure.
- * Only works for the virtual group that maps to single physical group.
- * No expression is allowed in SELECT statement.
- * All required elements must be specified.
- */
-public class UpdateProcedureGenerator {
-
-	// constant for an Insert procedure determining the procedure type
-	public static final int INSERT_PROCEDURE = 1;
-
-	// constant for an Update procedure determining the procedure type
-	public static final int UPDATE_PROCEDURE = 2;
-
-	// constant for an Delete procedure determining the procedure type
-	public static final int DELETE_PROCEDURE = 3;
-
-	/**
-	 * Create CreateUpdateProcedureCommand for the specific virtual group.
-     * @return Generated procedure or null if no procedure could be generated
-	 */
-	public static CreateUpdateProcedureCommand createProcedure (
-			int procedureType, String virtualGroup, Command queryTransformation, QueryMetadataInterface metadata)
-			throws TeiidComponentException, QueryMetadataException {
-
-        // validate that a procedure can be generated
-        UpdateValidationVisitor updateVisitor = new UpdateValidationVisitor(metadata);
-        PreOrderNavigator.doVisit(queryTransformation, updateVisitor);
-        ValidatorReport report = updateVisitor.getReport();
-        if(report.hasItems()) {
-            return null;
-        }
-
-		//the command should be a query
-		Query query = (Query)queryTransformation;
-		//get a list of symbols in select statement
-		List selectSymbols = query.getProjectedSymbols();
-        
-        if(query.getFrom() == null) {
-            return null;
-        }
-
-		GroupSymbol pGroup = query.getFrom().getGroups().iterator().next();
-        String pGroupName = pGroup.getName();
-        if(pGroup.getDefinition() != null) {
-            pGroupName = pGroup.getDefinition();
-        }
-
-        //get a list of the elements in the virtual group
-        List elementsInVG = metadata.getElementIDsInGroupID(metadata.getGroupID(virtualGroup));
-
-        // Create symbol for the ROWS_UPDATED special variable
-        ElementSymbol rowsUpdated = new ElementSymbol(ProcedureReservedWords.ROWS_UPDATED);
-
-		CreateUpdateProcedureCommand cupc = null;
-		switch(procedureType){
-			case INSERT_PROCEDURE:
-            {
-				List variables = new ArrayList();
-				List values = new ArrayList();
-				mapElements(selectSymbols, elementsInVG, pGroupName, metadata, variables, values);
-				Insert insert = new Insert();
-				insert.setGroup(new GroupSymbol(pGroupName));
-				insert.setVariables(variables);
-				insert.setValues(values);
-				AssignmentStatement assignStmt = new AssignmentStatement(rowsUpdated, insert);
-				Block b = new Block();
-    			b.addStatement(assignStmt);
-    			cupc = new CreateUpdateProcedureCommand(b);
-				break;
-            }
-			case UPDATE_PROCEDURE:
-            {
-                List variables = new ArrayList();
-                List values = new ArrayList();
-				mapElements(selectSymbols, elementsInVG, pGroupName, metadata, variables, values);
-				Update update = new Update();
-				update.setGroup(new GroupSymbol(pGroupName));
-				for(int i = 0; i < variables.size(); i++){
-					ElementSymbol variable = (ElementSymbol)variables.get(i);
-					Expression value = (Expression)values.get(i);
-					update.addChange(variable, value);
-				}
-				update.setCriteria(new TranslateCriteria(new CriteriaSelector()));
-				AssignmentStatement assignStmt = new AssignmentStatement(rowsUpdated, update);
-				Block b = new Block();
-    			b.addStatement(assignStmt);
-    			cupc = new CreateUpdateProcedureCommand(b);
-				break;
-            }
-			case DELETE_PROCEDURE:
-            {
-				Delete delete = new Delete();
-				delete.setGroup(new GroupSymbol(pGroupName));
-				delete.setCriteria(new TranslateCriteria(new CriteriaSelector()));
-				AssignmentStatement assignStmt = new AssignmentStatement(rowsUpdated, delete);
-				Block b = new Block();
-    			b.addStatement(assignStmt);
-    			cupc = new CreateUpdateProcedureCommand(b);
-				break;
-            }
-			default:
-				//should not come here
-                break;
-		}
-
-		return cupc;
-	}
-
-    /**
-     * Virtual elements and projected symbols should match up 1-to-1.  Short names
-     * of both are the same.  We want to build a mapping where the variables are
-     * the underlying physical element and the value is the INPUT variable for the
-     * virtual element.
-     *
-     * @param physicalElements Projected symbols from transformation query
-     * @param metadata Metadata access
-     * @param variables Collect each variable (physical element being updated)
-     * @param values Collect each value (INPUT value for respective virtual element)
-     */
-    private static void mapElements(List physicalElements, List virtualElements, String physicalGroup, QueryMetadataInterface metadata, List variables, List values)
-            throws TeiidComponentException, QueryMetadataException{
-
-        if(physicalElements.size()!= virtualElements.size()) {
-            throw new QueryMetadataException("ERR.015.010.0018", QueryPlugin.Util.getString("ERR.015.010.0018")); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-
-        //match the physical group elements to the virtual group elements
-        for(int i=0; i<physicalElements.size(); i++) {
-            // Strip alias if necessary to get physical element
-            SingleElementSymbol pSymbol = (SingleElementSymbol)physicalElements.get(i);
-            if(pSymbol instanceof AliasSymbol) {
-                pSymbol = ((AliasSymbol) pSymbol).getSymbol();
-            }
-
-            if(pSymbol instanceof ElementSymbol) {
-                final Object mid = ((ElementSymbol)pSymbol).getMetadataID();
-                final boolean supportsUpdate = metadata.elementSupports(mid, SupportConstants.Element.UPDATE);
-                //Only include elements that are updateable.
-                if(supportsUpdate) {
-                    // Create properly named physical element
-                    String properName = metadata.getFullElementName(physicalGroup, pSymbol.getShortName());
-                    variables.add(new ElementSymbol(properName));
-    
-                    // Construct properly named INPUT variable based on short name of virtual element
-                    String virtualElementName = metadata.getFullName(virtualElements.get(i));
-                    String virtualElementShortName = metadata.getShortElementName(virtualElementName);
-                    ElementSymbol inputElement = new ElementSymbol(ProcedureReservedWords.INPUTS + "." + virtualElementShortName); //$NON-NLS-1$
-                    values.add(inputElement);
-                }
-            }
-        }
-    }
-
-}

Modified: trunk/engine/src/main/java/org/teiid/query/sql/visitor/ExpressionMappingVisitor.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/visitor/ExpressionMappingVisitor.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/sql/visitor/ExpressionMappingVisitor.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -77,6 +77,7 @@
 public class ExpressionMappingVisitor extends LanguageVisitor {
 
     private Map symbolMap;
+    private boolean clone;
 
     /**
      * Constructor for ExpressionMappingVisitor.
@@ -85,6 +86,11 @@
     public ExpressionMappingVisitor(Map symbolMap) {
         this.symbolMap = symbolMap;
     }
+    
+    public ExpressionMappingVisitor(Map symbolMap, boolean clone) {
+        this.symbolMap = symbolMap;
+        this.clone = clone;
+    }
         
     protected boolean createAliases() {
     	return true;
@@ -94,6 +100,14 @@
         replaceSymbols(obj.getSymbols(), true);
     }
     
+    public boolean isClone() {
+		return clone;
+	}
+    
+    public void setClone(boolean clone) {
+		this.clone = clone;
+	}
+    
     @Override
     public void visit(DerivedColumn obj) {
     	Expression original = obj.getExpression();
@@ -287,7 +301,10 @@
     public Expression replaceExpression(Expression element) {
         Expression mapped = (Expression) this.symbolMap.get(element);
         if(mapped != null) {
-            return mapped;
+        	if (clone) {
+        		return (Expression)mapped.clone();
+        	}
+        	return mapped;
         }
         return element;    
     }

Deleted: trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidationVisitor.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidationVisitor.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidationVisitor.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -1,226 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
-
-package org.teiid.query.validator;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-
-import org.teiid.core.TeiidException;
-import org.teiid.query.QueryPlugin;
-import org.teiid.query.metadata.QueryMetadataInterface;
-import org.teiid.query.metadata.SupportConstants;
-import org.teiid.query.metadata.TempMetadataID;
-import org.teiid.query.sql.lang.*;
-import org.teiid.query.sql.symbol.*;
-import org.teiid.query.sql.util.SymbolMap;
-import org.teiid.query.sql.visitor.ElementCollectorVisitor;
-
-
-/**
- * <p> This visitor is used to validate updates through virtual groups. The command defining
- * the virtual group is always a <code>Query</code>. This object visits various parts of
- * this <code>Query</code> and verifies if the virtual group definition will allows it to be
- * updated.</p>
- */
-public class UpdateValidationVisitor extends AbstractValidationVisitor {
-
-	// metadata needed in validation process
-	private QueryMetadataInterface metadata;
-
-    // State during validation
-    // collection of elementIDs defined on the SELECT clause
-	private Collection elementsInSelect;
-
-    /**
-     * <p> This constructor initialises the visitor by setting the metadata
-     * needed for validation.</p>
-     * @param The metadata object needed for validation
-     */
-    public UpdateValidationVisitor(QueryMetadataInterface metadata) {
-        super();
-        this.metadata = metadata;
-        this.elementsInSelect = new HashSet();
-    }
-
-    /**
-     * This method get the metadata that this visitor uses.
-     * @return The metadata object needed for validation
-     */
-    protected QueryMetadataInterface getMetadata() {
-        return this.metadata;
-    }
-
-    // ############### Visitor methods for language objects ##################
-
-    /**
-     * <p> The command being visited should never be a <code>SetQuery</code> object, this method reports a
-     * validation error if this mehod is visited.</p>
-     * @param obj The <code>SetQuery</code> object to be visited for validation
-     */
-    public void visit(SetQuery obj) {
-    	handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0001")); //$NON-NLS-1$
-    }
-
-    /**
-     * <p> The command being visited should never be a <code>StoredProcedure</code> object, this method reports a
-     * validation error if this mehod is visited.</p>
-     * @param obj The <code>StoredProcedure</code> object to be visited for validation
-     */
-    public void visit(StoredProcedure obj) {
-        handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0002")); //$NON-NLS-1$
-    }
-
-    /**
-     * <p> The command being visited should never be a <code>Insert</code> object, this method reports a
-     * validation error if this mehod is visited.</p>
-     * @param obj The <code>Insert</code> object to be visited for validation
-     */
-    public void visit(Insert obj) {
-        handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0003")); //$NON-NLS-1$
-    }
-
-    /**
-     * <p> The command being visited should never be a <code>Update</code> object, this method reports a
-     * validation error if this mehod is visited.</p>
-     * @param obj The <code>Update</code> object to be visited for validation
-     */
-    public void visit(Update obj) {
-        handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0004")); //$NON-NLS-1$
-    }
-
-    /**
-     * <p> The command being visited should never be a <code>Delete</code> object, this method reports a
-     * validation error if this mehod is visited.</p>
-     * @param obj The <code>Delete</code> object to be visited for validation
-     */
-    public void visit(Delete obj) {
-        handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0005")); //$NON-NLS-1$
-    }
-
-    /**
-     * <p> This method visits the <code>Query</code> object and verifies that
-     * it has only a Select and From clause.</p>
-     * @param obj The <code>Query</code> object to be visited for validation
-     */
-    public void visit(Query obj) {
-    	if((obj.getGroupBy() != null) || (obj.getHaving() != null)) {
-    		handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0006")); //$NON-NLS-1$
-    	}
-    }
-
-    /**
-     * <p> This method visits the <code>Select</code> and verifies that the
-     * expressions defined on it are all <code>ElementSymbol<code>s or aliased
-     * <code>ElementSymbol<code>s</p>
-     * @param obj The <code>Select</code> object to be visited for validation
-     */
-    public void visit(Select obj) {
-
-    	Iterator elementIter = obj.getProjectedSymbols().iterator();
-
-    	while(elementIter.hasNext()) {
-            SingleElementSymbol symbol = (SingleElementSymbol) elementIter.next();
-            if(symbol instanceof AliasSymbol) {
-                symbol = ((AliasSymbol)symbol).getSymbol();
-            }
-            
-            Expression ex = SymbolMap.getExpression(symbol);
-            
-            if (!(ex instanceof ElementSymbol || ex instanceof Constant)) {
-                handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0007", symbol)); //$NON-NLS-1$
-            }
-    	}
-
-        elementsInSelect = ElementCollectorVisitor.getElements(obj, false);
-    }
-
-    /**
-     * <p> This method visits the <code>From</code> object to validate that
-     * it has only one physical group in it. It gets the list of elements present
-     * in the physical group but not specified in the Select clause and validates
-     * these elements according the guidelines governing virtual group updates.</p>
-     * @param obj The <code>From</code> object to be visited for validation
-     */
-    public void visit(From obj) {
-
-    	Iterator groupIter = obj.getGroups().iterator();
-
-    	GroupSymbol group = (GroupSymbol) groupIter.next();
-
-    	if(groupIter.hasNext()) {
-    		handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0009", obj.getGroups())); //$NON-NLS-1$
-    	} else {
-			try {
-				Object groupID = group.getMetadataID();
-                if(groupID instanceof TempMetadataID) {
-                    handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0002")); //$NON-NLS-1$
-                } else {
-    		    	Iterator elementsInGroupIter = getMetadata().getElementIDsInGroupID(groupID).iterator();
-    				// walk through all the elements in the physical group
-    		    	while(elementsInGroupIter.hasNext()) {
-    		    		Object elementID = elementsInGroupIter.next();
-                        ElementSymbol lookupSymbol = new ElementSymbol(getMetadata().getFullName(elementID));
-
-    		    		// get the element that is not in the Select
-    		    		if(!elementsInSelect.contains(lookupSymbol)) {
-                            lookupSymbol.setMetadataID(elementID);
-
-    			    		// validate the element is not required
-                            validateElementNotRequired(lookupSymbol);
-    			    	}
-    			    }
-                }
-    		} catch(TeiidException e) {
-    			handleException(e);
-    		}
-    	}
-    }
-
-	/**
-	 * <p> This method validates an elements present in the group specified in the
-	 * FROM clause of the query but not specified in its SELECT clause, according to
-	 * the rules governing virtul group updates.</p>
-	 * @param element The <code>ElementSymbol</code> being validated
-	 */
-	private void validateElementNotRequired(ElementSymbol element) {
-
-		try {
-			// checking if the elements not specified in the query are required.
-			if(getMetadata().elementSupports(element.getMetadataID(), SupportConstants.Element.NULL)) {
-			    return;
-			} else	if(getMetadata().elementSupports(element.getMetadataID(), SupportConstants.Element.DEFAULT_VALUE)) {
-				return;
-			} else if(getMetadata().elementSupports(element.getMetadataID(), SupportConstants.Element.AUTO_INCREMENT)) {
-				return;
-			}
-
-			// this method should only be executed if the element is a required element
-			// and none of cases above are true
-		    handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0010", element)); //$NON-NLS-1$
-		} catch(TeiidException e) {
-			handleException(e);
-		}
-
-	}
-}

Copied: trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidator.java (from rev 2690, trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidationVisitor.java)
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidator.java	                        (rev 0)
+++ trunk/engine/src/main/java/org/teiid/query/validator/UpdateValidator.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -0,0 +1,328 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.validator;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.teiid.api.exception.query.QueryMetadataException;
+import org.teiid.core.TeiidComponentException;
+import org.teiid.query.QueryPlugin;
+import org.teiid.query.metadata.QueryMetadataInterface;
+import org.teiid.query.metadata.SupportConstants;
+import org.teiid.query.resolver.util.ResolverUtil;
+import org.teiid.query.sql.lang.Command;
+import org.teiid.query.sql.lang.FromClause;
+import org.teiid.query.sql.lang.Query;
+import org.teiid.query.sql.lang.UnaryFromClause;
+import org.teiid.query.sql.symbol.ElementSymbol;
+import org.teiid.query.sql.symbol.Expression;
+import org.teiid.query.sql.symbol.GroupSymbol;
+import org.teiid.query.sql.symbol.SingleElementSymbol;
+import org.teiid.query.sql.util.SymbolMap;
+
+/**
+ * <p> This visitor is used to validate updates through virtual groups. The command defining
+ * the virtual group is always a <code>Query</code>. This object visits various parts of
+ * this <code>Query</code> and verifies if the virtual group definition will allows it to be
+ * updated.</p>
+ */
+public class UpdateValidator {
+	
+	public static class UpdateMapping {
+		private GroupSymbol group;
+		private GroupSymbol correlatedName;
+		private Map<ElementSymbol, ElementSymbol> updatableViewSymbols = new HashMap<ElementSymbol, ElementSymbol>();
+		private boolean insertAllowed = false;
+		private boolean updateAllowed = false;
+		
+		public Map<ElementSymbol, ElementSymbol> getUpdatableViewSymbols() {
+			return updatableViewSymbols;
+		}
+		
+		public boolean isInsertAllowed() {
+			return insertAllowed;
+		}
+		
+		public boolean isUpdateAllowed() {
+			return updateAllowed;
+		}
+		
+		public GroupSymbol getGroup() {
+			return group;
+		}
+		
+		public GroupSymbol getCorrelatedName() {
+			return correlatedName;
+		}
+	}
+	
+	public static class UpdateInfo {
+		private Map<String, UpdateMapping> updatableGroups = new HashMap<String, UpdateMapping>();
+		private boolean isSimple = true;
+		private UpdateMapping deleteTarget;
+		private String validationError;
+		private boolean inherentUpdate;
+		private boolean inherentDelete;
+		private boolean inherentInsert;
+		private Query view;
+		
+		public boolean isSimple() {
+			return isSimple;
+		}
+		
+		public UpdateMapping getDeleteTarget() {
+			return deleteTarget;
+		}
+		
+		public Map<String, UpdateMapping> getUpdatableGroups() {
+			return updatableGroups;
+		}
+		
+		public String getValidationError() {
+			return validationError;
+		}
+		
+		public void setValidationError(String validationError) {
+			this.validationError = validationError;
+		}
+		
+		public boolean isInherentDelete() {
+			return inherentDelete;
+		}
+		
+		public boolean isInherentInsert() {
+			return inherentInsert;
+		}
+		
+		public boolean isInherentUpdate() {
+			return inherentUpdate;
+		}
+		
+		public UpdateMapping findUpdateMapping(Collection<ElementSymbol> updateCols, boolean insert) {
+			for (UpdateMapping entry : getUpdatableGroups().values()) {
+				if (((insert && entry.insertAllowed) || (!insert && entry.updateAllowed)) && entry.updatableViewSymbols.keySet().containsAll(updateCols)) {
+					return entry;
+				}
+			}
+			return null;
+		}
+		
+		public Query getViewDefinition() {
+			return view;
+		}
+		
+	}
+	
+	private QueryMetadataInterface metadata;
+	private ValidatorReport report = new ValidatorReport();
+	private UpdateInfo updateInfo = new UpdateInfo();
+
+	public UpdateValidator(QueryMetadataInterface qmi, boolean inherentUpdate, boolean inherentDelete, boolean inherentInsert) {
+		this.metadata = qmi;
+		this.updateInfo.inherentDelete = inherentDelete;
+		this.updateInfo.inherentInsert = inherentInsert;
+		this.updateInfo.inherentUpdate = inherentUpdate;
+	}
+	
+	public UpdateInfo getUpdateInfo() {
+		return updateInfo;
+	}
+	
+	public ValidatorReport getReport() {
+		return report;
+	}
+	
+    public void validate(Command command, List<ElementSymbol> viewSymbols) throws QueryMetadataException, TeiidComponentException {
+    	if (!(command instanceof Query)) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0001")); //$NON-NLS-1$
+    		return;
+        }
+    	
+    	Query query = (Query)command;
+
+    	if (query.getFrom() == null || query.getInto() != null) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0001")); //$NON-NLS-1$
+    		return;
+    	}
+    	
+    	if (query.getWith() != null) {
+    		report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0002")); //$NON-NLS-1$
+    		updateInfo.isSimple = false;
+    	}
+
+    	if (query.hasAggregates()) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0006")); //$NON-NLS-1$
+    		return;
+    	}
+    	
+    	updateInfo.view = query;
+    	
+    	if (query.getLimit() != null) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0013")); //$NON-NLS-1$
+    		return;
+    	}
+    	
+    	if (query.getSelect().isDistinct()) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0008")); //$NON-NLS-1$
+    		return;
+    	} 
+    	
+    	List<SingleElementSymbol> projectedSymbols = query.getSelect().getProjectedSymbols();
+    	
+    	for (int i = 0; i < projectedSymbols.size(); i++) {
+            SingleElementSymbol symbol = projectedSymbols.get(i);
+            Expression ex = SymbolMap.getExpression(symbol);
+            
+            if (!metadata.elementSupports(viewSymbols.get(i).getMetadataID(), SupportConstants.Element.UPDATE)) {
+            	continue;
+            }
+            if (ex instanceof ElementSymbol) {
+            	ElementSymbol es = (ElementSymbol)ex;
+            	String groupName = es.getGroupSymbol().getCanonicalName();
+        		UpdateMapping info = updateInfo.updatableGroups.get(groupName);
+        		if (es.getGroupSymbol().getDefinition() != null) {
+            		ElementSymbol clone = (ElementSymbol)es.clone();
+            		clone.setName(es.getGroupSymbol().getDefinition() + ElementSymbol.SEPARATOR + es.getShortName());
+            		clone.getGroupSymbol().setName(clone.getGroupSymbol().getNonCorrelationName());
+            		clone.getGroupSymbol().setDefinition(null);
+            		es = clone;
+            	}
+            	if (info == null) {
+            		info = new UpdateMapping();
+            		info.group = es.getGroupSymbol();
+            		info.correlatedName = ((ElementSymbol)ex).getGroupSymbol();
+            		updateInfo.updatableGroups.put(groupName, info);
+            	}
+            	//TODO: warn if mapped twice
+            	info.updatableViewSymbols.put(viewSymbols.get(i), es);
+            } else {
+            	//TODO: look for reversable widening conversions
+            	
+                report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0007", viewSymbols.get(i), symbol)); //$NON-NLS-1$
+            }
+    	}
+    	
+    	if (query.getFrom().getClauses().size() > 1 || (!(query.getFrom().getClauses().get(0) instanceof UnaryFromClause))) {
+    		report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0009", query.getFrom())); //$NON-NLS-1$
+    		updateInfo.isSimple = false;
+    	}
+    	List<GroupSymbol> allGroups = query.getFrom().getGroups();
+    	HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
+    	
+    	if (query.getFrom().getClauses().size() == 1) {
+    		ResolverUtil.findKeyPreserinvg((FromClause)query.getFrom().getClauses().get(0), keyPreservingGroups, metadata);
+    	}
+    	
+		for (GroupSymbol groupSymbol : keyPreservingGroups) {
+			setUpdateFlags(groupSymbol);
+		}
+
+		allGroups.removeAll(keyPreservingGroups);
+		if (updateInfo.isSimple) {
+			if (!allGroups.isEmpty()) {
+				setUpdateFlags(allGroups.iterator().next());
+			}
+		} else if (this.updateInfo.inherentUpdate || this.updateInfo.inherentDelete) {
+			for (GroupSymbol groupSymbol : allGroups) {
+				UpdateMapping info = updateInfo.updatableGroups.get(groupSymbol.getCanonicalName());
+				if (info == null) {
+					continue; // not projected
+				}
+				report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0004", groupSymbol)); //$NON-NLS-1$
+			}
+		}
+
+    	boolean updatable = false;
+    	boolean insertable = false;
+    	for (UpdateMapping info : updateInfo.updatableGroups.values()) {
+    		if (info.updateAllowed) {
+    			if (!updatable) {
+    				this.updateInfo.deleteTarget = info;
+    			} else if (!info.getGroup().equals(this.updateInfo.deleteTarget.getGroup())){
+    				//TODO: warning about multiple
+    				this.updateInfo.deleteTarget = null;
+    			}
+    		}
+    		updatable |= info.updateAllowed;
+    		insertable |= info.insertAllowed;
+    	}
+    	if ((this.updateInfo.inherentInsert && !insertable)) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0015")); //$NON-NLS-1$
+    	} 
+    	if (this.updateInfo.inherentUpdate && !updatable) {
+    		report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0005")); //$NON-NLS-1$
+    	}
+    	if (this.updateInfo.inherentDelete && this.updateInfo.deleteTarget == null) {
+    		if (this.updateInfo.isSimple) {
+    			this.updateInfo.deleteTarget = this.updateInfo.updatableGroups.values().iterator().next();
+    		} else {
+    			report.handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0014")); //$NON-NLS-1$
+    		}
+    	}
+    }
+    
+    private void setUpdateFlags(GroupSymbol groupSymbol) throws QueryMetadataException, TeiidComponentException {
+    	UpdateMapping info = updateInfo.updatableGroups.get(groupSymbol.getCanonicalName());
+
+		if (info == null) {
+			return; // not projected
+		}
+
+		if (!metadata.groupSupports(groupSymbol.getMetadataID(), SupportConstants.Group.UPDATE)) {
+			report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0003", groupSymbol)); //$NON-NLS-1$
+			return;
+		}
+
+		info.insertAllowed = true;
+		for (ElementSymbol es : ResolverUtil.resolveElementsInGroup(info.group, metadata)) {
+			if (!info.updatableViewSymbols.values().contains(es) && !validateInsertElement(es)) {
+				info.insertAllowed = false;
+			}
+		}
+		info.updateAllowed = true;
+    }
+
+	/**
+	 * <p> This method validates an elements present in the group specified in the
+	 * FROM clause of the query but not specified in its SELECT clause</p>
+	 * @param element The <code>ElementSymbol</code> being validated
+	 * @throws TeiidComponentException 
+	 * @throws QueryMetadataException 
+	 */
+	private boolean validateInsertElement(ElementSymbol element) throws QueryMetadataException, TeiidComponentException {
+		// checking if the elements not specified in the query are required.
+		if(metadata.elementSupports(element.getMetadataID(), SupportConstants.Element.NULL) 
+			|| metadata.elementSupports(element.getMetadataID(), SupportConstants.Element.DEFAULT_VALUE) 
+			|| metadata.elementSupports(element.getMetadataID(), SupportConstants.Element.AUTO_INCREMENT)) {
+			return true;
+		}
+		if (this.updateInfo.inherentInsert) {
+			report.handleValidationWarning(QueryPlugin.Util.getString("ERR.015.012.0010", element, element.getGroupSymbol())); //$NON-NLS-1$
+		}
+	    return false;
+	}
+}

Modified: trunk/engine/src/main/java/org/teiid/query/validator/ValidationVisitor.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/validator/ValidationVisitor.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/validator/ValidationVisitor.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -136,6 +136,7 @@
 import org.teiid.query.sql.visitor.PredicateCollectorVisitor;
 import org.teiid.query.sql.visitor.SQLStringVisitor;
 import org.teiid.query.sql.visitor.ValueIteratorProviderCollectorVisitor;
+import org.teiid.query.validator.UpdateValidator.UpdateInfo;
 import org.teiid.query.xquery.saxon.SaxonXQueryExpression;
 import org.teiid.translator.SourceSystemFunctions;
 
@@ -198,11 +199,12 @@
 
 	public void visit(Delete obj) {
     	validateNoXMLUpdates(obj);
-        validateHasProjectedSymbols(obj);
         GroupSymbol group = obj.getGroup();
         validateGroupSupportsUpdate(group);
-        Criteria crit = obj.getCriteria();
-        validateVirtualUpdate(group, crit);
+        if (obj.getUpdateInfo() == null || !obj.getUpdateInfo().isInherentDelete()) {
+            Criteria crit = obj.getCriteria();
+        	validateVirtualUpdate(group, crit);
+        }
     }
 
 	private void validateVirtualUpdate(GroupSymbol group,
@@ -252,13 +254,18 @@
 
     public void visit(Insert obj) {
         validateNoXMLUpdates(obj);
-        validateHasProjectedSymbols(obj);
         validateGroupSupportsUpdate(obj.getGroup());
         validateInsert(obj);
         
         if (obj.getQueryExpression() != null) {
         	validateMultisourceInsert(obj.getGroup());
         }
+        if (obj.getUpdateInfo() != null && obj.getUpdateInfo().isInherentInsert()) {
+	        Collection<ElementSymbol> updateCols = obj.getVariables();
+	    	if (obj.getUpdateInfo().findUpdateMapping(updateCols, false) == null) {
+	    		handleValidationError(QueryPlugin.Util.getString("ValidationVisitor.nonUpdatable", updateCols), obj); //$NON-NLS-1$
+	    	}
+        }
     }
 
     @Override
@@ -326,10 +333,11 @@
     
     public void visit(Update obj) {
         validateNoXMLUpdates(obj);
-        validateHasProjectedSymbols(obj);
         validateGroupSupportsUpdate(obj.getGroup());
         validateUpdate(obj);
-        validateVirtualUpdate(obj.getGroup(), obj.getCriteria());
+        if (obj.getUpdateInfo() == null || !obj.getUpdateInfo().isInherentUpdate()) {
+        	validateVirtualUpdate(obj.getGroup(), obj.getCriteria());
+        }
     }
 
     public void visit(Into obj) {
@@ -902,12 +910,12 @@
     
     protected void validateUpdate(Update update) {
         try {
+            UpdateInfo info = update.getUpdateInfo();
+            boolean updatableView = info != null && info.isInherentUpdate();
+
             // list of elements that are being updated
 		    for (SetClause entry : update.getChangeList().getClauses()) {
         	    ElementSymbol elementID = entry.getSymbol();
-                if(elementID.isExternalReference()) {
-                    handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0058", SQLStringVisitor.getSQLString(elementID)), elementID); //$NON-NLS-1$
-                }
 
                 // Check that left side element is updatable
                 if(! getMetadata().elementSupports(elementID.getMetadataID(), SupportConstants.Element.UPDATE)) {
@@ -934,24 +942,23 @@
                     if(((Constant)value).isNull() && ! getMetadata().elementSupports(elementID.getMetadataID(), SupportConstants.Element.NULL)) {
                         handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0060", SQLStringVisitor.getSQLString(elementID)), elementID); //$NON-NLS-1$
                     }// end of if
-                } else if (!EvaluatableVisitor.willBecomeConstant(value)) {
+                } else if (!updatableView && getMetadata().isVirtualGroup(update.getGroup().getMetadataID()) && !EvaluatableVisitor.willBecomeConstant(value)) {
                     // If this is an update on a virtual group, verify that no elements are in the right side
-                    GroupSymbol group = update.getGroup();
-                    if(getMetadata().isVirtualGroup(group.getMetadataID())) {
-                        Collection elements = ElementCollectorVisitor.getElements(value, false);
-                        if(elements.size() > 0) {
-                            Iterator iter = elements.iterator();
-                            while(iter.hasNext()) {
-                                ElementSymbol element = (ElementSymbol) iter.next();
-                                if(! element.isExternalReference()) {
-                                    handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0061", SQLStringVisitor.getSQLString(value)), value); //$NON-NLS-1$
-                                }
-                            }
+                    Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(value, false);
+                    for (ElementSymbol element : elements) {
+                        if(! element.isExternalReference()) {
+                            handleValidationError(QueryPlugin.Util.getString("ERR.015.012.0061", SQLStringVisitor.getSQLString(value)), value); //$NON-NLS-1$
                         }
                     }
                 } 
-		    }// end of while
-        } catch(TeiidComponentException e) {
+		    }
+            if (updatableView) {
+            	Set<ElementSymbol> updateCols = update.getChangeList().getClauseMap().keySet();
+            	if (info.findUpdateMapping(updateCols, false) == null) {
+            		handleValidationError(QueryPlugin.Util.getString("ValidationVisitor.nonUpdatable", updateCols), update); //$NON-NLS-1$
+            	}
+            }
+        } catch(TeiidException e) {
             handleException(e, update);
         }
         

Modified: trunk/engine/src/main/java/org/teiid/query/validator/ValidatorReport.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/validator/ValidatorReport.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/java/org/teiid/query/validator/ValidatorReport.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -27,6 +27,7 @@
 
 import org.teiid.query.QueryPlugin;
 import org.teiid.query.report.ActivityReport;
+import org.teiid.query.sql.LanguageObject;
 
 
 public class ValidatorReport extends ActivityReport {
@@ -73,5 +74,21 @@
     public String toString() {
         return this.getFailureMessage();
     }
+    
+    public void handleValidationError(String message) {
+        this.addItem(new ValidatorFailure(message));
+    }
 
+    public void handleValidationError(String message, LanguageObject invalidObj) {
+        this.addItem(new ValidatorFailure(message, invalidObj));
+    }
+
+    public void handleValidationError(String message, Collection invalidObjs) {
+        this.addItem(new ValidatorFailure(message, invalidObjs));
+    }
+
+	public void handleValidationWarning(String message) {
+		this.addItem(new ValidatorWarning(message));
+	}
+
 }

Added: trunk/engine/src/main/java/org/teiid/query/validator/ValidatorWarning.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/validator/ValidatorWarning.java	                        (rev 0)
+++ trunk/engine/src/main/java/org/teiid/query/validator/ValidatorWarning.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -0,0 +1,84 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.validator;
+
+import java.util.*;
+
+import org.teiid.query.report.ReportItem;
+import org.teiid.query.sql.LanguageObject;
+
+public class ValidatorWarning extends ReportItem {
+
+	private static final long serialVersionUID = 3991298598581344564L;
+
+	public static final String VALIDATOR_WARNING = "ValidatorWarning"; //$NON-NLS-1$
+
+    // Don't want to pass this around, so make it transient
+    private transient Collection invalidObjects;  
+        
+    public ValidatorWarning(String description) { 
+        super(VALIDATOR_WARNING);
+        setMessage(description);
+    }
+    
+    public ValidatorWarning(String description, LanguageObject object) {
+        super(VALIDATOR_WARNING);
+        setMessage(description);
+        this.invalidObjects = new ArrayList(1);
+        this.invalidObjects.add(object);
+    }
+
+    public ValidatorWarning(String description, Collection objects) { 
+        super(VALIDATOR_WARNING);
+        setMessage(description);
+        this.invalidObjects = new ArrayList(objects);
+    }
+    
+    /** 
+     * Get count of invalid objects.
+     * @return Count of invalid objects
+     */
+    public int getInvalidObjectCount() { 
+        if(this.invalidObjects == null) { 
+            return 0;
+        }
+        return this.invalidObjects.size();
+    }   
+    
+    /**
+     * Get the objects that failed validation.  The collection may be null.
+     * @return Invalid objects, may be null
+     */
+    public Collection getInvalidObjects() { 
+        return this.invalidObjects;
+    } 
+    
+    /**
+     * Return description
+     * @return Description of failure
+     */    
+    public String toString() { 
+        return getMessage();
+    }
+
+}


Property changes on: trunk/engine/src/main/java/org/teiid/query/validator/ValidatorWarning.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/engine/src/main/resources/org/teiid/query/i18n.properties
===================================================================
--- trunk/engine/src/main/resources/org/teiid/query/i18n.properties	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/main/resources/org/teiid/query/i18n.properties	2010-11-23 04:10:27 UTC (rev 2734)
@@ -165,17 +165,21 @@
 # util (011)
 
 # validator (012)
-ERR.015.012.0001 = The query defining an updatable virtual group cannot be a UNION query
-ERR.015.012.0002 = The query defining an updatable virtual group cannot be a stored query or stored procedure execution
-ERR.015.012.0003 = The query defining an updatable virtual group cannot be an INSERT
-ERR.015.012.0004 = The query defining an updatable virtual group cannot be an UPDATE
-ERR.015.012.0005 = The query defining an updatable virtual group cannot be a DELETE
-ERR.015.012.0006 = The query defining an updatable virtual group should not use GROUP BY or HAVING.
-ERR.015.012.0007 = The query defining an updatable virtual group cannot use a non-constant, non-column reference expressions in its SELECT clause {0}.
-ERR.015.012.0009 = The query defining an updatable virtual group cannot have more than one group in its FROM clause {0}
-ERR.015.012.0010 = The query defining an updatable simple virtual group should select all the required elements in its FROM clause {0}
+ERR.015.012.0001 = The query defining an updatable view must be a simple query with a FROM clause.
+ERR.015.012.0002 = The query defining an updatable view has a WITH clause, pass-through processing will not be used for UPDATE/DELETE operations.
+ERR.015.012.0003 = The query defining an updatable view projects a column from a non-updatable group {0}.
+ERR.015.012.0004 = The query defining an updatable view has a non key preserving join group {0}, which cannot be targeted by UPDATE/DELETE operations.
+ERR.015.012.0005 = The query defining an updatable view has no valid target for UPDATEs.
+ERR.015.012.0006 = The query defining an updatable view should not use aggregates or grouping.
+ERR.015.012.0007 = The query defining an updatable view has a non-updatable expression {0} for view column {1}.
+ERR.015.012.0008 = The query defining an updatable view cannot use SELECT DISTINCT.
+ERR.015.012.0009 = The query defining an updatable view has a non-simple FROM clause, pass-through processing will not be used for UPDATE/DELETE operations.
+ERR.015.012.0010 = The query defining an updatable view does not project the column {0}, which is required to make {1} a target of INSERT operations.
 ERR.015.012.0011 = There must be exactly one projected symbol in the subcommand of an IN clause.
 ERR.015.012.0012 = An AssignmentStatement cannot change the value of a {0} or {1} variable.
+ERR.015.012.0013 = The query defining an updatable virtual group cannot use LIMIT.
+ERR.015.012.0014 = The non-simple query defining an updatable view does not have a valid key preserving delete target.
+ERR.015.012.0015 = The query defining an updatable view has no valid target for INSERTs.
 ERR.015.012.0016 = Variable {0} not assigned any value in this procedure.
 ERR.015.012.0017 = Variables declared the procedure''s DeclareStatement cannot be one of the special variables: {0}, {1} and {2}.
 ERR.015.012.0019 = TranslateCriteria cannot be used in on an if or while statement.
@@ -195,6 +199,7 @@
 ValidationVisitor.multisource_insert = A multi-source table, {0}, cannot be used in an INSERT with query expression or SELECT INTO statement.
 ValidationVisitor.virtual_update_subquery = Subqueries are not allowed in the criteria for a virtual UPDATE/DELETE: {0}
 ValidationVisitor.invalid_encoding = Invalid encoding: {0}.
+ValidationVisitor.nonUpdatable = The specified change set {0} against an inherently updatable view does not map to a key preserving group.
 ERR.015.012.0029 = INSERT, UPDATE, and DELETE not allowed on XML documents
 ERR.015.012.0030 = Commands used in stored procedure language not allowed on XML documents
 ERR.015.012.0031 = Queries against XML documents can not have a GROUP By clause
@@ -214,10 +219,9 @@
 ERR.015.012.0053 = Element in the group {0}, for which value is not specified in the insert command is neither nullable nor has a default value: {1}
 ERR.015.012.0054 = Column variables do not reference columns on group "{0}": {1}
 ERR.015.012.0055 = Element {0} does not allow nulls.
-ERR.015.012.0058 = Left side of update expression may not be a variable or a reference to an external element: {0}
 ERR.015.012.0059 = Left side of update expression must be an element that supports update: {0}
 ERR.015.012.0060 = Element {0} does not allow nulls.
-ERR.015.012.0061 = Update values must be a constant expression.  The expression ''{0}'' cannot be evaluated to a constant.
+ERR.015.012.0061 = Views using update procedures must have update values that are constant expressiosn.  The expression ''{0}'' cannot be evaluated to a constant.
 ERR.015.012.0062 = Elements cannot appear more than once in a SET or USING clause.  The following elements are duplicated: {0}
 ERR.015.012.0063 = Multiple failures occurred during validation:
 ERR.015.012.0064 = Validation succeeded

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestJoinPushdownRestrictions.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestJoinPushdownRestrictions.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestJoinPushdownRestrictions.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -87,6 +87,19 @@
         		new String[] {"SELECT g_0.e2, g_1.e2 FROM pm1.g1 AS g_0, pm1.g2 AS g_1 WHERE g_0.e2 = g_1.e2"}, capFinder, TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$  
 	}
 	
+	@Test public void testKeyRestriction1() throws Exception {
+		String sql = "select a.e1, b.e1 from pm4.g1 a inner join pm4.g2 b on (a.e1 = b.e1 and a.e2 = b.e2) left outer join pm4.g1 c on (c.e1 = b.e1 and c.e2 = b.e2)"; //$NON-NLS-1$
+
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_SELFJOIN, true);
+        caps.setSourceProperty(Capability.JOIN_CRITERIA_ALLOWED, SupportedJoinCriteria.KEY);
+        capFinder.addCapabilities("pm4", caps); //$NON-NLS-1$
+
+        TestOptimizer.helpPlan(sql, FakeMetadataFactory.example4(),  
+        		new String[] {"SELECT g_1.e1 AS c_0, g_1.e2 AS c_1, g_0.e1 AS c_2 FROM pm4.g1 AS g_0, pm4.g2 AS g_1 WHERE (g_0.e1 = g_1.e1) AND (g_0.e2 = g_1.e2) ORDER BY c_0, c_1", "SELECT g_0.e1 AS c_0, g_0.e2 AS c_1 FROM pm4.g1 AS g_0 ORDER BY c_0, c_1"}, capFinder, TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$   
+	}
+	
 	@Test public void testKeyPasses() throws Exception {
 		String sql = "select a.e1, b.e1 from pm4.g1 a, pm4.g2 b where a.e1 = b.e1 and a.e2 = b.e2"; //$NON-NLS-1$
 

Added: trunk/engine/src/test/java/org/teiid/query/processor/TestInherintlyUpdatableViews.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/processor/TestInherintlyUpdatableViews.java	                        (rev 0)
+++ trunk/engine/src/test/java/org/teiid/query/processor/TestInherintlyUpdatableViews.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -0,0 +1,123 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.processor;
+
+import static org.teiid.query.processor.TestProcessor.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.teiid.query.metadata.TransformationMetadata;
+import org.teiid.query.optimizer.TestOptimizer;
+import org.teiid.query.optimizer.capabilities.BasicSourceCapabilities;
+import org.teiid.query.optimizer.capabilities.DefaultCapabilitiesFinder;
+import org.teiid.query.rewriter.TestQueryRewriter;
+import org.teiid.query.sql.lang.Command;
+import org.teiid.query.util.CommandContext;
+import org.teiid.query.validator.TestUpdateValidator;
+import org.teiid.translator.SourceSystemFunctions;
+
+ at SuppressWarnings("nls")
+public class TestInherintlyUpdatableViews {
+
+	@Test public void testUpdatePassThrough() throws Exception {
+		String userSql = "update vm1.gx set e1 = e2"; //$NON-NLS-1$
+    	String viewSql = "select * from pm1.g1 where e3 < 5";
+    	String expectedSql = "UPDATE pm1.g1 SET e1 = pm1.g1.e2 WHERE e3 < 5";
+        helpTest(userSql, viewSql, expectedSql, null);	
+	}
+
+	private void helpTest(String userSql, String viewSql, String expectedSql, ProcessorDataManager dm)
+			throws Exception {
+		TransformationMetadata metadata = TestUpdateValidator.example1();
+        TestUpdateValidator.createView(viewSql, metadata, "gx");
+        Command command = TestQueryRewriter.helpTestRewriteCommand(userSql, expectedSql, metadata);
+
+        if (dm != null) {
+        	CommandContext context = createCommandContext();
+	        BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
+	        caps.setFunctionSupport(SourceSystemFunctions.CONVERT, true);
+	        ProcessorPlan plan = helpGetPlan(command, metadata, new DefaultCapabilitiesFinder(caps), context);
+	        List[] expected = new List[] {Arrays.asList(1)};
+        	helpProcess(plan, context, dm, expected);
+        }
+	}
+	
+	@Test public void testUpdatePassThroughWithAlias() throws Exception {
+		String userSql = "update vm1.gx set e1 = e2"; //$NON-NLS-1$
+    	String viewSql = "select * from pm1.g1 as x where e3 < 5";
+    	String expectedSql = "UPDATE pm1.g1 SET e1 = pm1.g1.e2 WHERE e3 < 5";
+        helpTest(userSql, viewSql, expectedSql, null);	
+	}
+	
+	@Test public void testDeletePassThrough() throws Exception {
+		String userSql = "delete from vm1.gx where e1 = e2"; //$NON-NLS-1$
+    	String viewSql = "select * from pm1.g1 where e3 < 5";
+        String expectedSql = "DELETE FROM pm1.g1 WHERE (pm1.g1.e1 = pm1.g1.e2) AND (e3 < 5)";
+        helpTest(userSql, viewSql, expectedSql, null);
+	}
+	
+	@Test public void testInsertPassThrough() throws Exception {
+		String userSql = "insert into vm1.gx (e1) values (1)"; //$NON-NLS-1$
+    	String viewSql = "select * from pm1.g1 where e3 < 5";
+        String expectedSql = "INSERT INTO pm1.g1 (pm1.g1.e1) VALUES ('1')";
+        helpTest(userSql, viewSql, expectedSql, null);
+	}
+	
+	/**
+	 * Here we should be able to figure out that we can pass through the join
+	 * @throws Exception
+	 */
+	@Test public void testInsertPassThrough1() throws Exception {
+		String userSql = "insert into vm1.gx (e1) values (1)"; //$NON-NLS-1$
+    	String viewSql = "select g2.* from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1";
+        String expectedSql = "INSERT INTO pm1.g2 (pm1.g2.e1) VALUES ('1')";
+        helpTest(userSql, viewSql, expectedSql, null);	
+	}
+	
+	@Test public void testUpdateComplex() throws Exception {
+		String userSql = "update vm1.gx set e1 = e2 where e3 is null"; //$NON-NLS-1$
+		String viewSql = "select g2.* from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1";
+		
+		HardcodedDataManager dm = new HardcodedDataManager();
+        dm.addData("SELECT g_1.e2, g_1.e2 FROM pm1.g1 AS g_0, pm1.g2 AS g_1 WHERE (g_0.e1 = g_1.e1) AND (g_1.e3 IS NULL)", new List[] {Arrays.asList(1, 1)});
+        dm.addData("UPDATE pm1.g2 SET e1 = pm1.g2.e2 WHERE pm1.g2.e2 = 1", new List[] {Arrays.asList(1)});
+        
+		helpTest(userSql, viewSql, "CREATE PROCEDURE\nBEGIN\nLOOP ON (SELECT pm1.g2.e2 AS s_0, pm1.g2.e2 AS s_1 FROM pm1.g1 INNER JOIN pm1.g2 ON g1.e1 = g2.e1 WHERE pm1.g2.e3 IS NULL) AS X\nBEGIN\nUPDATE pm1.g2 SET e1 = pm1.g2.e2 WHERE pm1.g2.e2 = X.s_1;\nVARIABLES.ROWS_UPDATED = (VARIABLES.ROWS_UPDATED + 1);\nEND\nEND",
+				dm);
+	}
+	
+	@Test public void testDeleteComplex() throws Exception {
+		String userSql = "delete from vm1.gx where e2 < 10"; //$NON-NLS-1$
+		String viewSql = "select g2.* from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1";
+		
+		HardcodedDataManager dm = new HardcodedDataManager();
+        dm.addData("SELECT g_1.e2 FROM pm1.g1 AS g_0, pm1.g2 AS g_1 WHERE (g_0.e1 = g_1.e1) AND (g_1.e2 < 10)", new List[] {Arrays.asList(2)});
+        dm.addData("DELETE FROM pm1.g2 WHERE pm1.g2.e2 = 2", new List[] {Arrays.asList(1)});
+        
+		helpTest(userSql, viewSql, "CREATE PROCEDURE\nBEGIN\nLOOP ON (SELECT pm1.g2.e2 AS s_0 FROM pm1.g1 INNER JOIN pm1.g2 ON g1.e1 = g2.e1 WHERE pm1.g2.e2 < 10) AS X\nBEGIN\nDELETE FROM pm1.g2 WHERE pm1.g2.e2 = X.s_0;\nVARIABLES.ROWS_UPDATED = (VARIABLES.ROWS_UPDATED + 1);\nEND\nEND",
+				dm);
+	}
+
+}


Property changes on: trunk/engine/src/test/java/org/teiid/query/processor/TestInherintlyUpdatableViews.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/engine/src/test/java/org/teiid/query/resolver/TestResolver.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/resolver/TestResolver.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/resolver/TestResolver.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -2573,24 +2573,6 @@
         helpResolveException(sql);
     }
                 
-    @Test public void testUpdateError() {
-        String userUpdateStr = "UPDATE vm1.g2 SET e1='x'"; //$NON-NLS-1$
-        
-        helpResolveException(userUpdateStr, metadata, "Error Code:ERR.015.008.0009 Message:Update is not allowed on the virtual group vm1.g2: no Update procedure was defined."); //$NON-NLS-1$
-    }
-    
-    @Test public void testInsertError() {
-        String userUpdateStr = "INSERT into vm1.g2 (e1) values ('x')"; //$NON-NLS-1$
-        
-        helpResolveException(userUpdateStr, metadata, "Error Code:ERR.015.008.0009 Message:Insert is not allowed on the virtual group vm1.g2: no Insert procedure was defined."); //$NON-NLS-1$
-    }
-    
-    @Test public void testDeleteError() {
-        String userUpdateStr = "DELETE from vm1.g2 where e1='x'"; //$NON-NLS-1$
-        
-        helpResolveException(userUpdateStr, metadata, "Error Code:ERR.015.008.0009 Message:Delete is not allowed on the virtual group vm1.g2: no Delete procedure was defined."); //$NON-NLS-1$
-    }
-    
     @Test public void testResolveXMLSelect() {
         String procedure = "CREATE VIRTUAL PROCEDURE "; //$NON-NLS-1$
         procedure = procedure + "BEGIN\n"; //$NON-NLS-1$
@@ -3064,5 +3046,19 @@
     @Test public void testWithNameMatchesFrom() {
     	helpResolve("with x as (TABLE pm1.g1) SELECT * from (TABLE x) x");
     }
+    
+	// variables cannot be used among insert elements
+    @Test(expected=QueryResolverException.class) public void testCreateUpdateProcedure23() throws Exception {
+        String procedure = "CREATE PROCEDURE  "; //$NON-NLS-1$
+        procedure = procedure + "BEGIN\n"; //$NON-NLS-1$
+        procedure = procedure + "DECLARE integer var1;\n"; //$NON-NLS-1$
+        procedure = procedure + "Update pm1.g1 SET pm1.g1.e2 =1 , var1 = 2;\n"; //$NON-NLS-1$
+        procedure = procedure + "ROWS_UPDATED =0;\n";         //$NON-NLS-1$
+        procedure = procedure + "END\n"; //$NON-NLS-1$
+
+        String userQuery = "UPDATE vm1.g3 SET x='x' where e3= 1"; //$NON-NLS-1$
+
+        helpResolveUpdateProcedure(procedure, userQuery);
+	}
         
 }
\ No newline at end of file

Deleted: trunk/engine/src/test/java/org/teiid/query/sql/util/TestUpdateProcedureGenerator.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/sql/util/TestUpdateProcedureGenerator.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/sql/util/TestUpdateProcedureGenerator.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -1,384 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
-
-package org.teiid.query.sql.util;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.teiid.client.metadata.ParameterInfo;
-import org.teiid.core.TeiidException;
-import org.teiid.core.types.DataTypeManager;
-import org.teiid.query.mapping.relational.QueryNode;
-import org.teiid.query.metadata.QueryMetadataInterface;
-import org.teiid.query.parser.QueryParser;
-import org.teiid.query.resolver.QueryResolver;
-import org.teiid.query.sql.lang.Command;
-import org.teiid.query.sql.proc.CreateUpdateProcedureCommand;
-import org.teiid.query.sql.util.UpdateProcedureGenerator;
-import org.teiid.query.unittest.FakeMetadataFacade;
-import org.teiid.query.unittest.FakeMetadataFactory;
-import org.teiid.query.unittest.FakeMetadataObject;
-import org.teiid.query.unittest.FakeMetadataStore;
-
-import junit.framework.TestCase;
-
-
-/**
-
- */
-public class TestUpdateProcedureGenerator extends TestCase{
-	
-	public TestUpdateProcedureGenerator(String name) { 
-		super(name);
-	}	
-
-	// ################################## TEST HELPERS ################################
-	
-	private void helpTest(int procedureType, String vGroup, String sql, QueryMetadataInterface md, String expectedProc) { 	
-		try {
-			Command command = QueryParser.getQueryParser().parseCommand(sql);
-			QueryResolver.resolveCommand(command, md);
-			
-	        CreateUpdateProcedureCommand actualProc = UpdateProcedureGenerator.createProcedure(procedureType, vGroup, command, md);
-	        if (expectedProc == null) {
-	        	assertNull(actualProc);
-	        } else {
-	        	assertNotNull(actualProc);
-	        	assertEquals("Didn't get expected generated procedure", expectedProc, actualProc.toString()); //$NON-NLS-1$
-		        QueryParser.getQueryParser().parseCommand(actualProc.toString());
-	        }
-		} catch (TeiidException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
- 	public static FakeMetadataFacade example1() { 
- 		return example1(true);
- 	}
-
- 	public static FakeMetadataFacade example1(boolean allUpdatable) { 
-		// Create models
-		FakeMetadataObject pm1 = FakeMetadataFactory.createPhysicalModel("pm1"); //$NON-NLS-1$
-		FakeMetadataObject vm1 = FakeMetadataFactory.createVirtualModel("vm1");	 //$NON-NLS-1$
-
-		// Create physical groups
-		FakeMetadataObject pm1g1 = FakeMetadataFactory.createPhysicalGroup("pm1.g1", pm1); //$NON-NLS-1$
-		FakeMetadataObject pm1g2 = FakeMetadataFactory.createPhysicalGroup("pm1.g2", pm1); //$NON-NLS-1$
-        FakeMetadataObject pm1g3 = FakeMetadataFactory.createPhysicalGroup("pm1.g3", pm1); //$NON-NLS-1$
-				
-		// Create physical elements
-		List pm1g1e = FakeMetadataFactory.createElements(pm1g1, 
-			new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
-		if (!allUpdatable) {
-			((FakeMetadataObject)pm1g1e.get(0)).putProperty(FakeMetadataObject.Props.UPDATE, Boolean.FALSE);
-		}
-		
-		List pm1g2e = FakeMetadataFactory.createElements(pm1g2, 
-			new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
-
-        List pm1g3e = FakeMetadataFactory.createElements(pm1g3, 
-            new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
-        ((FakeMetadataObject)pm1g3e.get(0)).putProperty(FakeMetadataObject.Props.NULL, Boolean.FALSE);
-        ((FakeMetadataObject)pm1g3e.get(0)).putProperty(FakeMetadataObject.Props.DEFAULT_VALUE, null);
-        
-        ((FakeMetadataObject)pm1g3e.get(1)).putProperty(FakeMetadataObject.Props.NULL, Boolean.FALSE);
-        ((FakeMetadataObject)pm1g3e.get(1)).putProperty(FakeMetadataObject.Props.AUTO_INCREMENT, Boolean.TRUE);
-        ((FakeMetadataObject)pm1g3e.get(1)).putProperty(FakeMetadataObject.Props.DEFAULT_VALUE, null);
-        
-        ((FakeMetadataObject)pm1g3e.get(2)).putProperty(FakeMetadataObject.Props.NULL, Boolean.FALSE);
-        ((FakeMetadataObject)pm1g3e.get(2)).putProperty(FakeMetadataObject.Props.DEFAULT_VALUE, "xyz"); //$NON-NLS-1$
-
-		// Create virtual groups
-		QueryNode vm1g1n1 = new QueryNode("vm1.g1", "SELECT e1 as a, e2 FROM pm1.g1 WHERE e3 > 5"); //$NON-NLS-1$ //$NON-NLS-2$
-		FakeMetadataObject vm1g1 = FakeMetadataFactory.createUpdatableVirtualGroup("vm1.g1", vm1, vm1g1n1); //$NON-NLS-1$
-		QueryNode vm1g2n1 = new QueryNode("vm1.g2", "SELECT e1, e2, e3, e4 FROM pm1.g2 WHERE e3 > 5"); //$NON-NLS-1$ //$NON-NLS-2$
-		FakeMetadataObject vm1g2 = FakeMetadataFactory.createUpdatableVirtualGroup("vm1.g2", vm1, vm1g2n1); //$NON-NLS-1$
-        QueryNode vm1g3n1 = new QueryNode("vm1.g3", "SELECT e1, e3 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
-        FakeMetadataObject vm1g3 = FakeMetadataFactory.createUpdatableVirtualGroup("vm1.g3", vm1, vm1g3n1); //$NON-NLS-1$
-        QueryNode vm1g4n1 = new QueryNode("vm1.g4", "SELECT e1, e2 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
-        FakeMetadataObject vm1g4 = FakeMetadataFactory.createUpdatableVirtualGroup("vm1.g4", vm1, vm1g4n1); //$NON-NLS-1$
-        QueryNode vm1g5n1 = new QueryNode("vm1.g5", "SELECT e2, e3 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
-        FakeMetadataObject vm1g5 = FakeMetadataFactory.createUpdatableVirtualGroup("vm1.g5", vm1, vm1g5n1); //$NON-NLS-1$
-
-		// Create virtual elements
-		List vm1g1e = FakeMetadataFactory.createElements(vm1g1, 
-			new String[] { "a", "e2"}, //$NON-NLS-1$ //$NON-NLS-2$
-			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER});
-		List vm1g2e = FakeMetadataFactory.createElements(vm1g2, 
-			new String[] { "e1", "e2","e3", "e4"}, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
-        List vm1g3e = FakeMetadataFactory.createElements(vm1g3, 
-            new String[] { "e1", "e2"}, //$NON-NLS-1$ //$NON-NLS-2$
-            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER,  });
-        List vm1g4e = FakeMetadataFactory.createElements(vm1g4, 
-            new String[] { "e1", "e3"}, //$NON-NLS-1$ //$NON-NLS-2$
-            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.BOOLEAN });
-        List vm1g5e = FakeMetadataFactory.createElements(vm1g5, 
-            new String[] { "e2","e3"}, //$NON-NLS-1$ //$NON-NLS-2$
-            new String[] { DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN });
-
-        // Stored queries
-        FakeMetadataObject rs1 = FakeMetadataFactory.createResultSet("pm1.rs1", pm1, new String[] { "e1", "e2" }, new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-        FakeMetadataObject rs1p1 = FakeMetadataFactory.createParameter("ret", 1, ParameterInfo.RESULT_SET, DataTypeManager.DefaultDataTypes.OBJECT, rs1);  //$NON-NLS-1$
-        QueryNode sq1n1 = new QueryNode("pm1.sq1", "CREATE VIRTUAL PROCEDURE BEGIN SELECT e1, e2 FROM pm1.g1; END"); //$NON-NLS-1$ //$NON-NLS-2$
-        FakeMetadataObject sq1 = FakeMetadataFactory.createVirtualProcedure("pm1.sq1", pm1, Arrays.asList(new FakeMetadataObject[] { rs1p1 }), sq1n1);  //$NON-NLS-1$
-
-		// Add all objects to the store
-		FakeMetadataStore store = new FakeMetadataStore();
-		store.addObject(pm1);
-		store.addObject(pm1g1);		
-		store.addObjects(pm1g1e);
-      	store.addObject(pm1g2);		
-		store.addObjects(pm1g2e);
-        store.addObject(pm1g3);     
-        store.addObjects(pm1g3e);
-		
-		store.addObject(vm1);
-		store.addObject(vm1g1);
-		store.addObjects(vm1g1e);
-		store.addObject(vm1g2);
-		store.addObjects(vm1g2e);
-        store.addObject(vm1g3);
-        store.addObjects(vm1g3e);
-        store.addObject(vm1g4);
-        store.addObjects(vm1g4e);
-        store.addObject(vm1g5);
-        store.addObjects(vm1g5e);
-        
-        store.addObject(rs1);
-        store.addObject(sq1);
-
-		// Create the facade from the store
-		return new FakeMetadataFacade(store);
-	}	
- 	
-	//actual tests
-	public void testCreateInsertCommand(){
-		helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-			"vm1.g1", //$NON-NLS-1$
-			"select e1 as a, e2 from pm1.g1 where e4 > 5",              //$NON-NLS-1$
-		    TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g1 (pm1.g1.e1, pm1.g1.e2) VALUES (INPUTS.a, INPUTS.e2);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-	
-	public void testCreateInsertCommand2(){ //put a constant in select statement
-		helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-			"vm1.g1", //$NON-NLS-1$
-			"select e1 as a, 5 from pm1.g1 where e4 > 5",              //$NON-NLS-1$
-		    TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g1 (pm1.g1.e1) VALUES (INPUTS.a);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-	
-	public void testCreateInsertCommand3(){ 
-		helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-			"vm1.g2", //$NON-NLS-1$
-			"select * from pm1.g2 where e4 > 5",              //$NON-NLS-1$
-		    TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g2 (pm1.g2.e1, pm1.g2.e2, pm1.g2.e3, pm1.g2.e4) VALUES (INPUTS.e1, INPUTS.e2, INPUTS.e3, INPUTS.e4);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-	
-	public void testCreateInsertCommand4(){ //test group alias
-		helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-			"vm1.g2", //$NON-NLS-1$
-			"select * from pm1.g2 as g_alias",              //$NON-NLS-1$
-			TestUpdateProcedureGenerator.example1(),
-			"CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g2 (pm1.g2.e1, pm1.g2.e2, pm1.g2.e3, pm1.g2.e4) VALUES (INPUTS.e1, INPUTS.e2, INPUTS.e3, INPUTS.e4);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}	
-
-	public void testCreateInsertCommand5(){
-		helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-			"vm1.g1", //$NON-NLS-1$
-			"select e1 as a, e2 from pm1.g1 as g_alias where e4 > 5",              //$NON-NLS-1$
-			TestUpdateProcedureGenerator.example1(),
-			"CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g1 (pm1.g1.e1, pm1.g1.e2) VALUES (INPUTS.a, INPUTS.e2);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-		
-	public void testCreateUpdateCommand(){
-		helpTest(UpdateProcedureGenerator.UPDATE_PROCEDURE, 
-			"vm1.g1", //$NON-NLS-1$
-			"select e1 as a, e2 from pm1.g1 where e4 > 5",              //$NON-NLS-1$
-		    TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nUPDATE pm1.g1 SET e1 = INPUTS.a, e2 = INPUTS.e2 WHERE TRANSLATE CRITERIA;\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-	
-	public void testCreateDeleteCommand(){
-		helpTest(UpdateProcedureGenerator.DELETE_PROCEDURE, 
-			"vm1.g1", //$NON-NLS-1$
-			"select e1 as a, e2 from pm1.g1 where e4 > 5",              //$NON-NLS-1$
-		    TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nDELETE FROM pm1.g1 WHERE TRANSLATE CRITERIA;\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-
-    public void testCreateInsertCommand1_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT pm1.g1.e1 FROM pm1.g1, pm1.g2", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand2_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT CONCAT(pm1.g1.e1, convert(pm1.g2.e1, string)) as x FROM pm1.g1, pm1.g2", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand3_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT e1 FROM pm1.g1 UNION SELECT e1 FROM pm1.g2", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand4_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT COUNT(*) FROM pm1.g1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand5_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT * FROM pm1.g1 GROUP BY e1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand6_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "EXEC pm1.sq1()", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand7_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "INSERT INTO pm1.g1 (e1) VALUES ('x')", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand8_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "UPDATE pm1.g1 SET e1='x'", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand9_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "DELETE FROM pm1.g1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand10_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT COUNT(*) FROM pm1.g1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    public void testCreateInsertCommand11_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT COUNT(e1) as x FROM pm1.g1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-    
-    public void testCreateInsertCommand12_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT * FROM (EXEC pm1.sq1()) AS a",              //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }    
-
-    public void testCreateInsertCommand13_fail(){
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE, 
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT 1",              //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }    
-    
-    // Check that e3 is not required (it has a default value)
-    public void testRequiredElements1() {
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g3", //$NON-NLS-1$
-            "SELECT e1, e2 FROM pm1.g3", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g3 (pm1.g3.e1, pm1.g3.e2) VALUES (INPUTS.e1, INPUTS.e2);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-    }
-
-    // Check that e2 is not required (it is auto-incremented)
-    public void testRequiredElements2() {
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g4", //$NON-NLS-1$
-            "SELECT e1, e3 FROM pm1.g3", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g3 (pm1.g3.e1, pm1.g3.e3) VALUES (INPUTS.e1, INPUTS.e3);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-    }
-
-    // Check that e1 is required (it is not-nullable, not auto-incrementable, and has no default value)
-    public void testRequiredElements3() {
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g5", //$NON-NLS-1$
-            "SELECT e2, e3 FROM pm1.g3", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(),
-            null);
-    }
-
-    // Verify that elements that are not updateable are exlcluded from update and delete procedures
-    public void testNonUpdateableElements() {
-        helpTest(UpdateProcedureGenerator.UPDATE_PROCEDURE, 
-                    "vm1.g1", //$NON-NLS-1$
-                    "select e1 as a, e2 from pm1.g1 where e4 > 5",              //$NON-NLS-1$
-                    TestUpdateProcedureGenerator.example1(false),
-                    "CREATE PROCEDURE\nBEGIN\nUPDATE pm1.g1 SET e2 = INPUTS.e2 WHERE TRANSLATE CRITERIA;\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-	}
-	
-    // Verify that elements that are not updateable are exlcluded from update and delete procedures
-    public void testNonUpdateableElements2() {
-        helpTest(UpdateProcedureGenerator.INSERT_PROCEDURE,
-            "vm1.g1", //$NON-NLS-1$
-            "SELECT e1, e2 FROM pm1.g1", //$NON-NLS-1$
-            TestUpdateProcedureGenerator.example1(false),
-            "CREATE PROCEDURE\nBEGIN\nINSERT INTO pm1.g1 (pm1.g1.e2) VALUES (INPUTS.e2);\nROWS_UPDATED = VARIABLES.ROWCOUNT;\nEND"); //$NON-NLS-1$
-    }
-
-}

Modified: trunk/engine/src/test/java/org/teiid/query/unittest/FakeMetadataFactory.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/unittest/FakeMetadataFactory.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/unittest/FakeMetadataFactory.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -2750,9 +2750,9 @@
         obj.putProperty(FakeMetadataObject.Props.IS_VIRTUAL, model.getProperty(FakeMetadataObject.Props.IS_VIRTUAL));
         obj.putProperty(FakeMetadataObject.Props.PLAN, plan);
         obj.putProperty(FakeMetadataObject.Props.UPDATE, Boolean.TRUE);
-        if (updatePlan != null) {
-            obj.putProperty(FakeMetadataObject.Props.UPDATE_PROCEDURE, updatePlan);
-        }
+        obj.putProperty(FakeMetadataObject.Props.UPDATE_PROCEDURE, updatePlan);
+        obj.putProperty(FakeMetadataObject.Props.INSERT_PROCEDURE, "");
+        obj.putProperty(FakeMetadataObject.Props.DELETE_PROCEDURE, "");
         return obj;
     }
 		

Modified: trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -31,6 +31,7 @@
 import org.teiid.core.types.DataTypeManager;
 import org.teiid.metadata.Column;
 import org.teiid.metadata.ColumnSet;
+import org.teiid.metadata.ForeignKey;
 import org.teiid.metadata.KeyRecord;
 import org.teiid.metadata.MetadataStore;
 import org.teiid.metadata.Procedure;
@@ -438,6 +439,17 @@
 		}
 		return key;
 	}
+	
+	public static ForeignKey createForeignKey(String name, Table group, List<Column> elements, KeyRecord primaryKey) {
+		ForeignKey key = new ForeignKey();
+		key.setName(name);
+		for (Column column : elements) {
+			key.addColumn(column);
+		}
+		key.setPrimaryKey(primaryKey);
+		group.getForeignKeys().add(key);
+		return key;
+	}
 
     /**
      * Create a physical model with default settings.

Added: trunk/engine/src/test/java/org/teiid/query/validator/TestUpdateValidator.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/validator/TestUpdateValidator.java	                        (rev 0)
+++ trunk/engine/src/test/java/org/teiid/query/validator/TestUpdateValidator.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -0,0 +1,341 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.validator;
+
+import static org.junit.Assert.*;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+import org.teiid.api.exception.query.QueryParserException;
+import org.teiid.api.exception.query.QueryResolverException;
+import org.teiid.core.TeiidComponentException;
+import org.teiid.core.TeiidException;
+import org.teiid.core.types.DataTypeManager;
+import org.teiid.metadata.Column;
+import org.teiid.metadata.ColumnSet;
+import org.teiid.metadata.KeyRecord;
+import org.teiid.metadata.MetadataStore;
+import org.teiid.metadata.Procedure;
+import org.teiid.metadata.Schema;
+import org.teiid.metadata.Table;
+import org.teiid.metadata.BaseColumn.NullType;
+import org.teiid.metadata.KeyRecord.Type;
+import org.teiid.query.mapping.relational.QueryNode;
+import org.teiid.query.metadata.TransformationMetadata;
+import org.teiid.query.parser.QueryParser;
+import org.teiid.query.report.ReportItem;
+import org.teiid.query.resolver.QueryResolver;
+import org.teiid.query.resolver.util.ResolverUtil;
+import org.teiid.query.sql.lang.Command;
+import org.teiid.query.sql.symbol.GroupSymbol;
+import org.teiid.query.sql.symbol.SingleElementSymbol;
+import org.teiid.query.unittest.RealMetadataFactory;
+
+ at SuppressWarnings("nls")
+public class TestUpdateValidator {
+	
+	private UpdateValidator helpTest(String sql, TransformationMetadata md, boolean shouldFail) { 	
+		try {
+			String vGroup = "gx";
+			Command command = createView(sql, md, vGroup);
+			
+			UpdateValidator uv = new UpdateValidator(md, true, true, true);
+			GroupSymbol gs = new GroupSymbol(vGroup);
+			ResolverUtil.resolveGroup(gs, md);
+			uv.validate(command, ResolverUtil.resolveElementsInGroup(gs, md));
+			boolean failed = false;
+			for (ReportItem item : (Collection<ReportItem>)uv.getReport().getItems()) {
+				if (item instanceof ValidatorFailure) {
+					failed = true;
+					if (!shouldFail) {
+						fail(item.toString());
+					}
+				}
+			}
+			if (!failed && shouldFail) {
+				fail("expeceted failures, but got none: " + uv.getReport());
+			}
+			return uv;
+		} catch (TeiidException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static Command createView(String sql, TransformationMetadata md, String vGroup)
+			throws QueryParserException, QueryResolverException,
+			TeiidComponentException {
+		QueryNode vm1g1n1 = new QueryNode(vGroup, sql); 
+		Table vm1g1 = RealMetadataFactory.createUpdatableVirtualGroup(vGroup, md.getMetadataStore().getSchema("vm1"), vm1g1n1);
+
+		Command command = QueryParser.getQueryParser().parseCommand(sql);
+		QueryResolver.resolveCommand(command, md);
+		
+		List<SingleElementSymbol> symbols = command.getProjectedSymbols();
+		String[] names = new String[symbols.size()];
+		String[] types = new String[symbols.size()];
+		int i = 0;
+		for (SingleElementSymbol singleElementSymbol : symbols) {
+			names[i] = singleElementSymbol.getShortName();
+			types[i++] = DataTypeManager.getDataTypeName(singleElementSymbol.getType());
+		}
+		
+		RealMetadataFactory.createElements(vm1g1, names, types);
+		return command;
+	}
+	
+ 	public static TransformationMetadata example1() { 
+ 		return example1(true);
+ 	}
+
+ 	public static TransformationMetadata example1(boolean allUpdatable) { 
+ 		MetadataStore metadataStore = new MetadataStore();
+		
+ 		// Create models
+		Schema pm1 = RealMetadataFactory.createPhysicalModel("pm1", metadataStore); //$NON-NLS-1$
+		Schema vm1 = RealMetadataFactory.createVirtualModel("vm1", metadataStore);	 //$NON-NLS-1$
+
+		// Create physical groups
+		Table pm1g1 = RealMetadataFactory.createPhysicalGroup("g1", pm1); //$NON-NLS-1$
+		Table pm1g2 = RealMetadataFactory.createPhysicalGroup("g2", pm1); //$NON-NLS-1$
+		Table pm1g3 = RealMetadataFactory.createPhysicalGroup("g3", pm1); //$NON-NLS-1$
+				
+		// Create physical elements
+		List<Column> pm1g1e = RealMetadataFactory.createElements(pm1g1, 
+			new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
+		if (!allUpdatable) {
+			pm1g1e.get(0).setUpdatable(false);
+		}
+		
+		KeyRecord pk = RealMetadataFactory.createKey(Type.Primary, "pk", pm1g1, pm1g1e.subList(0, 1));
+		
+		List<Column> pm1g2e = RealMetadataFactory.createElements(pm1g2, 
+			new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
+
+		RealMetadataFactory.createKey(Type.Primary, "pk", pm1g2, pm1g1e.subList(1, 2));
+		RealMetadataFactory.createForeignKey("fk", pm1g2, pm1g2e.subList(0, 1), pk);
+		
+        List<Column> pm1g3e = RealMetadataFactory.createElements(pm1g3, 
+            new String[] { "e1", "e2", "e3", "e4" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
+        pm1g3e.get(0).setNullType(NullType.No_Nulls);
+        pm1g3e.get(0).setDefaultValue(null);
+        
+        pm1g3e.get(1).setNullType(NullType.No_Nulls);
+        pm1g3e.get(1).setAutoIncremented(true);
+        pm1g3e.get(1).setDefaultValue(null);
+        
+        pm1g3e.get(2).setNullType(NullType.No_Nulls);
+        pm1g3e.get(2).setDefaultValue("xyz"); //$NON-NLS-1$
+
+		// Create virtual groups
+		QueryNode vm1g1n1 = new QueryNode("g1", "SELECT e1 as a, e2 FROM pm1.g1 WHERE e3 > 5"); //$NON-NLS-1$ //$NON-NLS-2$
+		Table vm1g1 = RealMetadataFactory.createUpdatableVirtualGroup("g1", vm1, vm1g1n1); //$NON-NLS-1$
+		QueryNode vm1g2n1 = new QueryNode("g2", "SELECT e1, e2, e3, e4 FROM pm1.g2 WHERE e3 > 5"); //$NON-NLS-1$ //$NON-NLS-2$
+		Table vm1g2 = RealMetadataFactory.createUpdatableVirtualGroup("g2", vm1, vm1g2n1); //$NON-NLS-1$
+        QueryNode vm1g3n1 = new QueryNode("g3", "SELECT e1, e3 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
+        Table vm1g3 = RealMetadataFactory.createUpdatableVirtualGroup("g3", vm1, vm1g3n1); //$NON-NLS-1$
+        QueryNode vm1g4n1 = new QueryNode("g4", "SELECT e1, e2 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
+        Table vm1g4 = RealMetadataFactory.createUpdatableVirtualGroup("g4", vm1, vm1g4n1); //$NON-NLS-1$
+        QueryNode vm1g5n1 = new QueryNode("g5", "SELECT e2, e3 FROM pm1.g3"); //$NON-NLS-1$ //$NON-NLS-2$
+        Table vm1g5 = RealMetadataFactory.createVirtualGroup("g5", vm1, vm1g5n1); //$NON-NLS-1$
+
+		// Create virtual elements
+		List<Column> vm1g1e = RealMetadataFactory.createElements(vm1g1, 
+			new String[] { "a", "e2"}, //$NON-NLS-1$ //$NON-NLS-2$
+			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER});
+		List<Column> vm1g2e = RealMetadataFactory.createElements(vm1g2, 
+			new String[] { "e1", "e2","e3", "e4"}, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN, DataTypeManager.DefaultDataTypes.DOUBLE });
+        List<Column> vm1g3e = RealMetadataFactory.createElements(vm1g3, 
+            new String[] { "e1", "e2"}, //$NON-NLS-1$ //$NON-NLS-2$
+            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER,  });
+        List<Column> vm1g4e = RealMetadataFactory.createElements(vm1g4, 
+            new String[] { "e1", "e3"}, //$NON-NLS-1$ //$NON-NLS-2$
+            new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.BOOLEAN });
+        List<Column> vm1g5e = RealMetadataFactory.createElements(vm1g5, 
+            new String[] { "e2","e3"}, //$NON-NLS-1$ //$NON-NLS-2$
+            new String[] { DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.BOOLEAN });
+
+        // Stored queries
+        ColumnSet<Procedure> rs1 = RealMetadataFactory.createResultSet("rs1", new String[] { "e1", "e2" }, new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        QueryNode sq1n1 = new QueryNode("sq1", "CREATE VIRTUAL PROCEDURE BEGIN SELECT e1, e2 FROM pm1.g1; END"); //$NON-NLS-1$ //$NON-NLS-2$
+        Procedure sq1 = RealMetadataFactory.createVirtualProcedure("pm1.sq1", pm1, Collections.EMPTY_LIST, sq1n1);  //$NON-NLS-1$
+        sq1.setResultSet(rs1);
+		// Create the facade from the store
+		return RealMetadataFactory.createTransformationMetadata(metadataStore, "example");
+	}	
+ 	
+	//actual tests
+	@Test public void testCreateInsertCommand(){
+		helpTest("select e1 as a, e2 from pm1.g1 where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+	
+	@Test public void testCreateInsertCommand2(){ //put a constant in select statement
+		helpTest("select e1 as a, 5 from pm1.g1 where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+	
+	@Test public void testCreateInsertCommand3(){ 
+		helpTest("select * from pm1.g2 where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+	
+	@Test public void testCreateInsertCommand4(){ //test group alias
+		helpTest("select * from pm1.g2 as g_alias", 
+			example1(), false); //$NON-NLS-1$
+	}	
+
+	@Test public void testCreateInsertCommand5(){
+		helpTest("select e1 as a, e2 from pm1.g1 as g_alias where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+		
+	@Test public void testCreateUpdateCommand(){
+		helpTest("select e1 as a, e2 from pm1.g1 where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+	
+	@Test public void testCreateDeleteCommand(){
+		helpTest("select e1 as a, e2 from pm1.g1 where e4 > 5", 
+			example1(), false); //$NON-NLS-1$
+	}
+
+    @Test public void testCreateInsertCommand1(){
+        helpTest("SELECT pm1.g1.e1 FROM pm1.g1, pm1.g2",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand2_fail(){
+        helpTest("SELECT CONCAT(pm1.g1.e1, convert(pm1.g2.e1, string)) as x FROM pm1.g1, pm1.g2",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand3_fail(){
+        helpTest("SELECT e1 FROM pm1.g1 UNION SELECT e1 FROM pm1.g2",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand4_fail(){
+        helpTest("SELECT COUNT(*) FROM pm1.g1",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand5_fail(){
+        helpTest("SELECT * FROM pm1.g1 GROUP BY e1",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand6_fail(){
+        helpTest("EXEC pm1.sq1()",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand7_fail(){
+        helpTest("INSERT INTO pm1.g1 (e1) VALUES ('x')",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand8_fail(){
+        helpTest("UPDATE pm1.g1 SET e1='x'",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand9_fail(){
+        helpTest("DELETE FROM pm1.g1",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand10_fail(){
+        helpTest("SELECT COUNT(*) FROM pm1.g1",
+            example1(), true);
+    }
+
+    @Test public void testCreateInsertCommand11_fail(){
+        helpTest("SELECT COUNT(e1) as x FROM pm1.g1",
+            example1(), true);
+    }
+    
+    @Test public void testCreateInsertCommand12_fail(){
+        helpTest("SELECT * FROM (EXEC pm1.sq1()) AS a", 
+            example1(), true);
+    }    
+
+    @Test public void testCreateInsertCommand13_fail(){
+        helpTest("SELECT 1", 
+            example1(), true);
+    }    
+    
+    // Check that e3 is not required (it has a default value)
+    @Test public void testRequiredElements1() {
+        helpTest("SELECT e1, e2 FROM pm1.g3",
+            example1(), false); //$NON-NLS-1$
+    }
+
+    // Check that e2 is not required (it is auto-incremented)
+    @Test public void testRequiredElements2() {
+        helpTest("SELECT e1, e3 FROM pm1.g3",
+            example1(), false); //$NON-NLS-1$
+    }
+
+    // Check that e1 is required (it is not-nullable, not auto-incrementable, and has no default value)
+    @Test public void testRequiredElements3() {
+        helpTest("SELECT e2, e3 FROM pm1.g3",
+            example1(), true);
+    }
+
+    // Verify that elements that are not updateable are exlcluded from update and delete procedures
+    @Test public void testNonUpdateableElements() {
+        helpTest("select e1 as a, e2 from pm1.g1 where e4 > 5", 
+                    example1(false), false); //$NON-NLS-1$
+	}
+	
+    // Verify that elements that are not updateable are exlcluded from update and delete procedures
+    @Test public void testNonUpdateableElements2() {
+        helpTest("SELECT e1, e2 FROM pm1.g1",
+            example1(false), false); //$NON-NLS-1$
+    }
+    
+    // Verify that elements that are not updateable are exlcluded from update and delete procedures
+    @Test public void testSelectDistinct() {
+        helpTest("SELECT distinct e1, e2 FROM pm1.g1",
+            example1(), true); //$NON-NLS-1$
+    }
+    
+    @Test public void testNonUpdatable() {
+        helpTest("SELECT e2 FROM vm1.g5",
+            example1(), true); //$NON-NLS-1$
+    }
+    
+    @Test public void testAnsiJoin() {
+        helpTest("SELECT g1.e1, x.e2 FROM pm1.g2 x inner join pm1.g1 on (x.e1 = g1.e1)",
+            example1(), false); //$NON-NLS-1$
+    }
+
+}
+


Property changes on: trunk/engine/src/test/java/org/teiid/query/validator/TestUpdateValidator.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/engine/src/test/java/org/teiid/query/validator/TestValidator.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/validator/TestValidator.java	2010-11-21 01:48:42 UTC (rev 2733)
+++ trunk/engine/src/test/java/org/teiid/query/validator/TestValidator.java	2010-11-23 04:10:27 UTC (rev 2734)
@@ -693,25 +693,6 @@
         helpValidate("UPDATE vm1.g1 SET e1=1 WHERE exists (select * from vm1.g1)" , new String[] {"EXISTS (SELECT * FROM vm1.g1)"}, FakeMetadataFactory.example1Cached()); //$NON-NLS-1$ //$NON-NLS-2$
     }
     
-    @Test public void testUpdate3() throws Exception {
-        QueryMetadataInterface metadata = exampleMetadata();
-        
-        Command command = QueryParser.getQueryParser().parseCommand("UPDATE test.group SET p1=1"); //$NON-NLS-1$
-
-        // Create external metadata
-        GroupSymbol sqGroup = new GroupSymbol("pm1.sq5"); //$NON-NLS-1$
-        ArrayList sqParams = new ArrayList();
-        ElementSymbol in = new ElementSymbol("pm1.sq5.p1"); //$NON-NLS-1$
-        in.setType(DataTypeManager.DefaultDataClasses.STRING);
-        sqParams.add(in);
-        Map externalMetadata = new HashMap();
-        externalMetadata.put(sqGroup, sqParams);
-        
-        QueryResolver.resolveCommand(command, externalMetadata, metadata, AnalysisRecord.createNonRecordingRecord());
-                
-        helpRunValidator(command, new String[] {"p1"}, metadata); //$NON-NLS-1$
-    }
-
     @Test public void testUpdate4() throws Exception {
         QueryMetadataInterface metadata = exampleMetadata();
 
@@ -1284,36 +1265,6 @@
 				FakeMetadataObject.Props.UPDATE_PROCEDURE);
 	}
 
-	// variables cannot be used among insert elements
-    @Test public void testCreateUpdateProcedure23() {
-        String procedure = "CREATE PROCEDURE  "; //$NON-NLS-1$
-        procedure = procedure + "BEGIN\n"; //$NON-NLS-1$
-        procedure = procedure + "DECLARE integer var1;\n"; //$NON-NLS-1$
-        procedure = procedure + "Update pm1.g1 SET pm1.g1.e2 =1 , var1 = 2;\n"; //$NON-NLS-1$
-        procedure = procedure + "ROWS_UPDATED =0;\n";         //$NON-NLS-1$
-        procedure = procedure + "END\n"; //$NON-NLS-1$
-
-        String userQuery = "UPDATE vm1.g3 SET x='x' where e3= 1"; //$NON-NLS-1$
-
-		helpFailProcedure(procedure, userQuery, 
-				FakeMetadataObject.Props.UPDATE_PROCEDURE);
-	}
-	
-	// variables cannot be used among insert elements
-    @Test public void testCreateUpdateProcedure24() {
-        String procedure = "CREATE PROCEDURE  "; //$NON-NLS-1$
-        procedure = procedure + "BEGIN\n"; //$NON-NLS-1$
-        procedure = procedure + "DECLARE integer var1;\n"; //$NON-NLS-1$
-        procedure = procedure + "Update pm1.g1 SET pm1.g1.e2 =1 , INPUT.x = 2;\n"; //$NON-NLS-1$
-        procedure = procedure + "ROWS_UPDATED =0;\n";         //$NON-NLS-1$
-        procedure = procedure + "END\n"; //$NON-NLS-1$
-
-        String userQuery = "UPDATE vm1.g3 SET x='x' where e3= 1"; //$NON-NLS-1$
-
-		helpFailProcedure(procedure, userQuery, 
-				FakeMetadataObject.Props.UPDATE_PROCEDURE);
-	}
-
 	// virtual group elements used in procedure in if statement(TRANSLATE CRITERIA)
     @Test public void testCreateUpdateProcedure25() {
         String procedure = "CREATE PROCEDURE  "; //$NON-NLS-1$



More information about the teiid-commits mailing list