You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2022/03/01 23:33:18 UTC

[empire-db] branch master updated: EMPIREDB-362 Bugfix: PreparedStatments parameters out of order for Joins with additional constraints

This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new c3b9da0  EMPIREDB-362 Bugfix: PreparedStatments parameters out of order for Joins with additional constraints
c3b9da0 is described below

commit c3b9da0eac5661bd2edc8f17013d75fd85e350aa
Author: Rainer Döbele <do...@apache.org>
AuthorDate: Wed Mar 2 00:33:15 2022 +0100

    EMPIREDB-362 Bugfix: PreparedStatments parameters out of order for Joins with additional constraints
---
 .../org/apache/empire/commons/ObjectUtils.java     |  21 +-
 .../main/java/org/apache/empire/db/DBCommand.java  | 529 ++++++++++++++++-----
 .../java/org/apache/empire/db/DBCommandExpr.java   |  22 +
 .../main/java/org/apache/empire/db/DBQuery.java    |   2 +-
 .../java/org/apache/empire/db/DBQueryColumn.java   |  10 -
 .../org/apache/empire/dbms/hsql/DBCommandHSql.java |  94 +---
 .../apache/empire/dbms/oracle/DBCommandOracle.java | 103 +---
 .../org/apache/empire/commons/ObjectUtilsTest.java |   2 +-
 8 files changed, 461 insertions(+), 322 deletions(-)

diff --git a/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java b/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
index 886741c..68589ab 100644
--- a/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
+++ b/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.Locale;
 
 import org.apache.commons.beanutils.MethodUtils;
+import org.apache.empire.data.ColumnExpr;
 import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.InvalidValueException;
 import org.apache.empire.exceptions.ItemNotFoundException;
@@ -249,7 +250,7 @@ public final class ObjectUtils
     }
 
     /**
-     * Compares two object arrrays for equality
+     * Compares two arrays for equality
      *
      * @param array1    the first array
      * @param array2    the second array
@@ -272,6 +273,24 @@ public final class ObjectUtils
     }
     
     /**
+     * Compares two ColumnExpr for equality
+     *
+     * @param expr1
+     * @param expr2
+     *
+     * @return true if both expressions are equal or false otherwise
+     */
+    public static boolean compareEqual(ColumnExpr expr, ColumnExpr other)
+    {
+        if (other.isWrapper() && !expr.isWrapper())
+            return expr.equals(other.unwrap());
+        else if (!other.isWrapper() && expr.isWrapper())
+            return expr.unwrap().equals(other);
+        // both wrapped or both unwrapped
+        return expr.equals(other);
+    }
+    
+    /**
      * Checks whether a preferred value is valid and returns an alternative value if not.
      * @param <T> the type of the values
      * @param preferred the preferred return value
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBCommand.java b/empire-db/src/main/java/org/apache/empire/db/DBCommand.java
index 67f6a7e..6883adb 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBCommand.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBCommand.java
@@ -26,10 +26,16 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.StringUtils;
+import org.apache.empire.commons.Unwrappable;
 import org.apache.empire.data.DataType;
+import org.apache.empire.db.expr.column.DBAliasExpr;
+import org.apache.empire.db.expr.column.DBValueExpr;
+import org.apache.empire.db.expr.compare.DBCompareAndOrExpr;
 import org.apache.empire.db.expr.compare.DBCompareColExpr;
 import org.apache.empire.db.expr.compare.DBCompareExpr;
+import org.apache.empire.db.expr.compare.DBCompareNotExpr;
 import org.apache.empire.db.expr.join.DBColumnJoinExpr;
 import org.apache.empire.db.expr.join.DBCompareJoinExpr;
 import org.apache.empire.db.expr.join.DBCrossJoinExpr;
@@ -37,9 +43,9 @@ import org.apache.empire.db.expr.join.DBJoinExpr;
 import org.apache.empire.db.expr.order.DBOrderByExpr;
 import org.apache.empire.db.expr.set.DBSetExpr;
 import org.apache.empire.dbms.DBSqlPhrase;
-import org.apache.empire.exceptions.InternalException;
 import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.ItemNotFoundException;
+import org.apache.empire.exceptions.NotSupportedException;
 import org.apache.empire.exceptions.ObjectNotValidException;
 import org.apache.empire.exceptions.UnspecifiedErrorException;
 import org.slf4j.Logger;
@@ -56,6 +62,172 @@ public abstract class DBCommand extends DBCommandExpr
 {
     // *Deprecated* private static final long serialVersionUID = 1L;
 
+    /**
+     * DBMergeCommand
+     * @author rainer
+     */
+    protected static class DBMergeCommand extends DBCommand
+    {
+        private final DBCommand parent;
+        
+        protected DBMergeCommand(DBCommand parent)
+        {
+            super(parent.isAutoPrepareStmt());
+            this.parent = parent;
+            // set 
+            List<DBSetExpr> set = parent.getSetExpressions();
+            if (set!=null)
+                this.set = new ArrayList<DBSetExpr>(set);
+            // joins
+            List<DBJoinExpr> joins = parent.getJoins();
+            if (joins!=null)
+                this.joins = new ArrayList<DBJoinExpr>(joins);
+            // where
+            List<DBCompareExpr> where = parent.getWhereConstraints();
+            if (where!=null)
+                this.where = new ArrayList<DBCompareExpr>(where);
+            // groupBy
+            List<DBColumnExpr> groupBy = parent.getGroupBy();
+            if (groupBy!=null)
+                this.groupBy = new ArrayList<DBColumnExpr>(groupBy);
+            // having
+            List<DBCompareExpr> having = parent.getHavingConstraints();
+            if (having!=null)
+                this.having = new ArrayList<DBCompareExpr>(having);
+        }   
+        
+        @Override
+        protected void notifyParamUsage(DBCmdParam param)
+        {
+            throw new NotSupportedException(this, "notifyParamUsage");
+        }
+        
+        @Override
+        protected void resetParamUsage()
+        {
+            /* Nothing */
+        }
+        
+        @Override
+        protected void completeParamUsage()
+        {
+            /* Nothing */
+        }
+        
+        @Override
+        protected void mergeSubqueryParams(Object[] subQueryParams)
+        {
+            parent.mergeSubqueryParams(subQueryParams);
+        }
+        
+        @Override
+        protected void addJoin(StringBuilder buf, DBJoinExpr join, long context, int whichParams)
+        {
+            parent.addJoin(buf, join, context, whichParams);
+        }
+        
+        public List<DBSetExpr> addUsing(StringBuilder buf, DBRowSet table, DBColumnJoinExpr updateJoin)
+        {
+            buf.append("\r\nUSING ");
+            // clearSelect();
+            // clearOrderBy();
+            DBRowSet outerTable = updateJoin.getOuterTable();
+            if (outerTable==null)
+                outerTable=table;
+            Set<DBColumn> joinColumns = new HashSet<DBColumn>();
+            updateJoin.addReferencedColumns(joinColumns);
+            for (DBColumn jcol : joinColumns)
+            {   // Select join columns
+                if (jcol.getRowSet().equals(outerTable)==false)
+                    select(jcol);
+            }
+            // find the source table
+            DBColumnExpr left  = updateJoin.getLeft();
+            DBColumnExpr right = updateJoin.getRight();
+            DBRowSet source = right.getUpdateColumn().getRowSet();
+            if (source==table)
+                source = left.getUpdateColumn().getRowSet();
+            // Add set expressions
+            String sourceAliasPrefix = source.getAlias()+".";
+            List<DBSetExpr> mergeSet = new ArrayList<DBSetExpr>(set.size());   
+            for (DBSetExpr sex : set)
+            {   // Select set expressions
+                Object val = sex.getValue();
+                if (val instanceof DBColumnExpr)
+                {
+                    DBColumnExpr expr = ((DBColumnExpr)val);
+                    if (!(expr instanceof DBColumn) && !(expr instanceof DBAliasExpr))
+                    {   // rename column
+                        String name = "COL_"+String.valueOf(mergeSet.size());
+                        expr = expr.as(name);
+                    }
+                    // select
+                    select(expr);
+                    // Name
+                    DBValueExpr NAME_EXPR = getDatabase().getValueExpr(sourceAliasPrefix+expr.getName(), DataType.UNKNOWN);
+                    mergeSet.add(sex.getColumn().to(NAME_EXPR));
+                }
+                else
+                {   // add original
+                    mergeSet.add(sex);
+                }
+            }
+            // remove join (if not necessary)
+            if (hasConstraintOn(table)==false)
+                removeJoinsOn(table);
+            // add SQL for inner statement
+            addSQL(buf, CTX_DEFAULT);
+            // add Alias
+            buf.append(" ");
+            buf.append(source.getAlias());
+            buf.append("\r\nON (");
+            left.addSQL(buf, CTX_DEFAULT);
+            buf.append(" = ");
+            right.addSQL(buf, CTX_DEFAULT);
+            // Compare Expression
+            if (updateJoin.getWhere() != null)
+                appendMergeConstraint(buf, table, updateJoin.getWhere());
+            // More constraints
+            for (DBCompareExpr cmpExpr : this.where) 
+            {
+                appendMergeConstraint(buf, table, cmpExpr);
+            }
+            // done
+            return mergeSet;
+        }
+    
+        protected void appendMergeConstraint(StringBuilder buf, DBRowSet table, DBCompareExpr cmpExpr)
+        {
+            if (cmpExpr instanceof DBCompareColExpr)
+            {   // a compare column expression
+                DBCompareColExpr cce = (DBCompareColExpr)cmpExpr;
+                DBColumn ccecol = cce.getColumn().getUpdateColumn();
+                if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
+                {   // yes, add
+                    buf.append(" AND ");
+                    cce.addSQL(buf, CTX_DEFAULT);
+                }
+            }
+            // else if (cmpExpr instanceof DBCompareAndOrExpr)
+            // else if (cmpExpr instanceof DBCompareNotExpr)
+            else  
+            {   // just add
+                buf.append(" AND ");
+                cmpExpr.addSQL(buf, CTX_DEFAULT);
+            }
+        }
+            
+        protected boolean isSetColumn(DBColumn col)
+        {
+            for (DBSetExpr se : this.set)
+            {
+                if (se.getColumn().equals(col))
+                    return true;
+            }
+            return false;
+        }
+    }
+    
     // Logger
     protected static final Logger log             = LoggerFactory.getLogger(DBCommand.class);
 
@@ -74,15 +246,6 @@ public abstract class DBCommand extends DBCommandExpr
     protected List<DBCmdParam>    cmdParams       = null;
     private int                   paramUsageCount = 0;
 
-    /**
-     * Constructs a new DBCommand object and set the specified DBDatabase object.
-     * 
-     * @param db the current database object
-     */
-    protected DBCommand(boolean autoPrepareStmt)
-    {
-        this.autoPrepareStmt = autoPrepareStmt;
-    }
 
     /**
      * Custom serialization for transient database.
@@ -109,6 +272,78 @@ public abstract class DBCommand extends DBCommandExpr
      */
     
     /**
+     * Constructs a new DBCommand object and set the specified DBDatabase object.
+     * 
+     * @param db the current database object
+     */
+    protected DBCommand(boolean autoPrepareStmt)
+    {
+        this.autoPrepareStmt = autoPrepareStmt;
+    }
+
+    /**
+     * @return true if auto Prepared Statements is activated for this record
+     */
+    public final boolean isAutoPrepareStmt()
+    {
+        return autoPrepareStmt;
+    }
+    
+    /**
+     * Creates a clone of this class.
+     */
+    @Override
+    public DBCommand clone()
+    {
+        DBCommand clone = (DBCommand)super.clone();
+        // Clone lists
+        if (select!=null)
+            clone.select = new ArrayList<DBColumnExpr>(select);
+        if (set!=null)
+            clone.set = new ArrayList<DBSetExpr>(set);
+        if (joins!=null)
+            clone.joins = new ArrayList<DBJoinExpr>(joins);
+        if (where!=null)
+            clone.where = new ArrayList<DBCompareExpr>(where);
+        if (groupBy!=null)
+            clone.groupBy = new ArrayList<DBColumnExpr>(groupBy);
+        if (having!=null)
+            clone.having = new ArrayList<DBCompareExpr>(having);
+        if (cmdParams!=null && !cmdParams.isEmpty())
+        {   // clone params
+            clone.paramUsageCount = 0;
+            clone.cmdParams = new ArrayList<DBCmdParam>(cmdParams.size());
+            // clone set
+            for (int i=0; (clone.set!=null && i<clone.set.size()); i++)
+                clone.set.set(i, clone.set.get(i).copy(clone));
+            // clone where and having
+            for (int i=0; (clone.where!=null && i<clone.where.size()); i++)
+                clone.where.set(i, clone.where.get(i).copy(clone));
+            for (int i=0; (clone.having!=null && i<clone.having.size()); i++)
+                clone.having.set(i, clone.having.get(i).copy(clone));
+        }
+        // done
+        return clone;
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public final DBDatabase getDatabase()
+    {
+        if (hasSelectExpr())
+            return this.select.get(0).getDatabase();
+        if (hasSetExpr())
+            return this.set.get(0).getDatabase();
+        // two more chances (should we?)
+        if (where!=null && !where.isEmpty())
+            return where.get(0).getDatabase();
+        if (orderBy!=null && !orderBy.isEmpty())
+            return orderBy.get(0).getDatabase();
+        // not valid yet
+        throw new ObjectNotValidException(this);
+    }
+    
+    /**
      * internally used to reset the command param usage count.
      * Note: Only one thread my generate an SQL statement 
      */
@@ -136,7 +371,10 @@ public abstract class DBCommand extends DBCommandExpr
         {   // Remove unused parameters
             log.warn("DBCommand has {} unused Command params", cmdParams.size()-paramUsageCount);
             for (int i=cmdParams.size()-1; i>=paramUsageCount; i--)
-                cmdParams.remove(i);
+            {   // Remove temporary params
+                if (cmdParams.get(i).getCmd()!=this)
+                    cmdParams.remove(i);
+            }
         }
     }
     
@@ -146,11 +384,16 @@ public abstract class DBCommand extends DBCommandExpr
     protected void notifyParamUsage(DBCmdParam param)
     {
         int index = cmdParams.indexOf(param);
-        if (index < paramUsageCount)
+        if (index<0) 
         {   // Error: parameter probably used twice in statement!
-            throw new UnspecifiedErrorException("A parameter may only be used once in a command.");
+            throw new UnspecifiedErrorException("The CmdParam has not been found on this Command.");
+        }
+        if (index < paramUsageCount)
+        {   // Warn: parameter used twice in statement!
+            log.debug("The DBCmdParam already been used. Adding a temporary copy");
+            cmdParams.add(paramUsageCount, new DBCmdParam(null, param.getDataType(), param.getValue()));
         }
-        if (index > paramUsageCount)
+        else if (index > paramUsageCount)
         {   // Correct parameter order
             cmdParams.remove(index);
             cmdParams.add(paramUsageCount, param);
@@ -161,90 +404,44 @@ public abstract class DBCommand extends DBCommandExpr
     /**
      * internally used to remove the command param used in a constraint
      */
-   	private void removeCommandParam(DBCompareColExpr cmp) 
+   	protected void removeCommandParams(DBCompareExpr cmpExpr) 
    	{
-        if (cmdParams!=null && (cmp.getValue() instanceof DBCmdParam))
-   			cmdParams.remove(cmp.getValue());
+   	    if (cmdParams==null)
+   	        return; // Nothing to do
+   	    // unwrap
+   	    if (cmpExpr instanceof Unwrappable<?>)
+   	        cmpExpr = (DBCompareExpr)((Unwrappable<?>)cmpExpr).unwrap();
+   	    // check type
+   	    if (cmpExpr instanceof DBCompareColExpr)
+   	    {   // DBCompareColExpr
+   	        DBCompareColExpr cmp = ((DBCompareColExpr)cmpExpr);
+            if (cmp.getValue() instanceof DBCmdParam)
+                cmdParams.remove(cmp.getValue());
+   	    }
+        else if (cmpExpr instanceof DBCompareAndOrExpr) 
+        {   // DBCompareAndOrExpr
+            removeCommandParams(((DBCompareAndOrExpr)cmpExpr).getLeft());
+            removeCommandParams(((DBCompareAndOrExpr)cmpExpr).getRight());
+        }
+        else if (cmpExpr instanceof DBCompareNotExpr) 
+        {   // DBCompareNotExpr
+            removeCommandParams(((DBCompareNotExpr)cmpExpr).getExpr());
+        }
    	}
 
     /**
      * internally used to remove all command params used in a list of constraints
      */
-   	private void removeAllCommandParams(List<DBCompareExpr> list)
+   	protected void removeAllCommandParams(List<DBCompareExpr> list)
     {
         if (cmdParams == null)
         	return;
         for(DBCompareExpr cmp : list)
-        {	// Check whether it is a compare column expr.
-            if (!(cmp instanceof DBCompareColExpr))
-            	continue;
-            // Check the value is a DBCommandParam
-        	removeCommandParam((DBCompareColExpr)cmp);
-        }
-    }
-   	
-    
-    /**
-     * Creates a clone of this class.
-     */
-    @Override
-    public DBCommand clone()
-    {
-        try 
-        {
-            DBCommand clone = (DBCommand)super.clone();
-            // Clone lists
-            if (select!=null)
-                clone.select = new ArrayList<DBColumnExpr>(select);
-            if (set!=null)
-                clone.set = new ArrayList<DBSetExpr>(set);
-            if (joins!=null)
-                clone.joins = new ArrayList<DBJoinExpr>(joins);
-            if (where!=null)
-                clone.where = new ArrayList<DBCompareExpr>(where);
-            if (groupBy!=null)
-                clone.groupBy = new ArrayList<DBColumnExpr>(groupBy);
-            if (having!=null)
-                clone.having = new ArrayList<DBCompareExpr>(having);
-            if (cmdParams!=null && !cmdParams.isEmpty())
-            {   // clone params
-                clone.paramUsageCount = 0;
-                clone.cmdParams = new ArrayList<DBCmdParam>(cmdParams.size());
-                // clone set
-                for (int i=0; (clone.set!=null && i<clone.set.size()); i++)
-                    clone.set.set(i, clone.set.get(i).copy(clone));
-                // clone where and having
-                for (int i=0; (clone.where!=null && i<clone.where.size()); i++)
-                    clone.where.set(i, clone.where.get(i).copy(clone));
-                for (int i=0; (clone.having!=null && i<clone.having.size()); i++)
-                    clone.having.set(i, clone.having.get(i).copy(clone));
-            }
-            // done
-            return clone;
-            
-        } catch (CloneNotSupportedException e) {
-            log.error("Cloning DBCommand object failed!", e);
-            throw new InternalException(e); 
+        {   // Check the value is a DBCommandParam
+        	removeCommandParams(cmp);
         }
     }
 
-    @SuppressWarnings("unchecked")
-    @Override
-	public final DBDatabase getDatabase()
-    {
-        if (hasSelectExpr())
-            return this.select.get(0).getDatabase();
-        if (hasSetExpr())
-            return this.set.get(0).getDatabase();
-        // two more chances (should we?)
-        if (where!=null && !where.isEmpty())
-            return where.get(0).getDatabase();
-        if (orderBy!=null && !orderBy.isEmpty())
-            return orderBy.get(0).getDatabase();
-        // not valid yet
-        throw new ObjectNotValidException(this);
-    }
-
     /**
      * Returns true if the this command has either Select or Set expressions
      */
@@ -538,12 +735,11 @@ public abstract class DBCommand extends DBCommandExpr
     {
         if (set==null)
             return false;
-        Iterator<DBSetExpr> i = set.iterator();
-        while (i.hasNext())
-        {
-            DBSetExpr chk = i.next();
-            if (chk.column.equals(column))
+        for (DBSetExpr setExpr : set)
+        {   // Find column
+            if (setExpr.column.equals(column))
                 return true;
+            
         }
         return false;
     }
@@ -992,6 +1188,17 @@ public abstract class DBCommand extends DBCommandExpr
      * removes a constraint on a particular column from the where clause
      * @param col the column expression for which to remove the constraint
      */
+    public void removeWhereConstraint(DBCompareExpr cmpExpr)
+    {
+        if (where == null)
+            return;
+        removeConstraint(where, cmpExpr);
+    }
+    
+    /**
+     * removes a constraint on a particular column from the where clause
+     * @param col the column expression for which to remove the constraint
+     */
     public void removeWhereConstraintOn(DBColumnExpr col)
     {
         if (where == null)
@@ -1056,6 +1263,17 @@ public abstract class DBCommand extends DBCommandExpr
     }
     
     /**
+     * removes a constraint on a particular column from the where clause
+     * @param col the column expression for which to remove the constraint
+     */
+    public void removeHavingConstraint(DBCompareExpr cmpExpr)
+    {
+        if (having == null)
+            return;
+        removeConstraint(having, cmpExpr);
+    }
+    
+    /**
      * removes a constraint on a particular column from the having clause
      * @param col the column expression for which to remove the constraint
      */
@@ -1247,6 +1465,16 @@ public abstract class DBCommand extends DBCommandExpr
         clearLimit();
         resetParamUsage();
     }
+
+    /**
+     * Create a special Merge-command
+     * This forwards parameter usage to the parent command 
+     * @return the merge command
+     */
+    protected DBMergeCommand createMergeCommand()
+    {
+        return new DBMergeCommand(this); 
+    }
     
     /**
      * returns true if prepared statements are enabled for this command
@@ -1291,8 +1519,7 @@ public abstract class DBCommand extends DBCommandExpr
             if (expr.isMutuallyExclusive(other)==false)
                 continue;
             // Check if we replace a DBCommandParam
-            if (other instanceof DBCompareColExpr)
-            	removeCommandParam((DBCompareColExpr)other);
+            removeCommandParams(other);
             // columns match
             list.set(i, expr);
             return;
@@ -1306,23 +1533,51 @@ public abstract class DBCommand extends DBCommandExpr
      * @param list the 'where' or 'having' list
      * @param col the column expression for which to remove the constraint
      */
-    protected void removeConstraintOn(List<DBCompareExpr> list, DBColumnExpr col)
+    protected void removeConstraint(List<DBCompareExpr> list, DBCompareExpr cmpExpr)
+    {
+        if (list == null)
+            return;
+        for (DBCompareExpr cmp : list)
+        {   // Compare columns
+            if (cmp.isMutuallyExclusive(cmpExpr))
+            {   // Check if we replace a DBCommandParam
+                removeCommandParams(cmp);
+                // remove the constraint
+                list.remove(cmp);
+                return;
+            }
+        }
+    }
+    
+    /**
+     * removes a constraint on a particular column to the 'where' or 'having' collections 
+     * @param list the 'where' or 'having' list
+     * @param col the column expression for which to remove the constraint
+     */
+    protected void removeConstraintOn(List<DBCompareExpr> list, DBColumnExpr colExpr)
     {
         if (list == null)
         	return;
-        for(DBCompareExpr cmp : list)
+        for (DBCompareExpr cmp : list)
         {	// Check whether it is a compare column expr.
             if (!(cmp instanceof DBCompareColExpr))
             	continue;
             // Compare columns
-            DBColumnExpr c = ((DBCompareColExpr)cmp).getColumn();
-            DBColumn udc = c.getUpdateColumn();
-            if (c.equals(col) || (udc!=null && udc.equals(col.getUpdateColumn())))
+            DBColumnExpr cmpCol = ((DBCompareColExpr)cmp).getColumn();
+            if (ObjectUtils.compareEqual(cmpCol, colExpr))
             {   // Check if we replace a DBCommandParam
-            	removeCommandParam((DBCompareColExpr)cmp);
-            	// remove the constraint
-            	list.remove(cmp);
-            	return;
+                removeCommandParams(cmp);
+                // remove the constraint
+                list.remove(cmp);
+                return;
+            }
+            // Update column
+            if ((colExpr instanceof DBColumn) && !(cmpCol instanceof DBColumn) && colExpr.equals(colExpr.getUpdateColumn()))
+            {   // Check if we replace a DBCommandParam
+                removeCommandParams(cmp);
+                // remove the constraint
+                list.remove(cmp);
+                return;
             }
         }
     }
@@ -1381,20 +1636,20 @@ public abstract class DBCommand extends DBCommandExpr
     
     /**
      * Returns an array of parameter values for a prepared statement.
-     * To ensure that all values are in the order of their occurrence, getSelect() should be called first.
+     * The parameters are supplied only after getSelect(), getUpdate(), getInsert() or getDelete() have been called
      * @return an array of parameter values for a prepared statement 
      */
     @Override
     public Object[] getParamValues()
     {
-        if (cmdParams==null || cmdParams.size()==0)
+        if (cmdParams==null || paramUsageCount==0)
             return null;
         // Check whether all parameters have been used
-        if (paramUsageCount>0 && paramUsageCount!=cmdParams.size())
-	        log.warn("DBCommand parameter count ("+String.valueOf(cmdParams.size())
-	        	   + ") does not match parameter use count ("+String.valueOf(paramUsageCount)+")");
+        if (paramUsageCount!=cmdParams.size())
+	        log.info("DBCommand parameter count ("+String.valueOf(cmdParams.size())
+                   + ") does not match parameter use count ("+String.valueOf(paramUsageCount)+")");
         // Create result array
-        Object[] values = new Object[cmdParams.size()];
+        Object[] values = new Object[paramUsageCount];
         for (int i=0; i<values.length; i++)
             values[i]=cmdParams.get(i).getValue();
         // values
@@ -1449,11 +1704,8 @@ public abstract class DBCommand extends DBCommandExpr
         {   // Convert ColumnExpression List to Column List
             compexpr = new ArrayList<DBCompareColExpr>(where.size());
             for (DBCompareExpr expr : where)
-            {   if (expr instanceof DBCompareColExpr)
-                {   DBColumn column = ((DBCompareColExpr)expr).getColumn().getUpdateColumn();
-                    if (column!=null && hasSetExprOn(column)==false)
-                        compexpr.add((DBCompareColExpr)expr);
-                }
+            {
+                appendCompareColExprs(table, expr, compexpr);
             }
             // Add Column Names from where clause
             if (compexpr.size()>0)
@@ -1485,6 +1737,29 @@ public abstract class DBCommand extends DBCommandExpr
         completeParamUsage();
         return buf.toString();
     }
+    
+    protected void appendCompareColExprs(DBRowSet table, DBCompareExpr expr, List<DBCompareColExpr> list)
+    {
+        // unwrap
+        if (expr instanceof Unwrappable<?>)
+            expr = (DBCompareExpr)((Unwrappable<?>)expr).unwrap();
+        // check type
+        if (expr instanceof DBCompareColExpr)
+        {   // DBCompareColExpr
+            DBColumn column = ((DBCompareColExpr)expr).getColumn().getUpdateColumn();
+            if (column!=null && column.getRowSet().equals(table) && !hasSetExprOn(column))
+                list.add((DBCompareColExpr)expr);
+        }
+        else if (expr instanceof DBCompareAndOrExpr) 
+        {   // DBCompareAndOrExpr
+            appendCompareColExprs(table, ((DBCompareAndOrExpr)expr).getLeft(),  list);
+            appendCompareColExprs(table, ((DBCompareAndOrExpr)expr).getRight(), list);
+        }
+        else if (expr instanceof DBCompareNotExpr) 
+        {   // DBCompareNotExpr
+            appendCompareColExprs(table, ((DBCompareNotExpr)expr).getExpr(),  list);
+        }
+    }
 
     /**
      * Creates an update SQL-Statement
@@ -1627,11 +1902,8 @@ public abstract class DBCommand extends DBCommandExpr
                      buf.append( "\t" );
                      whichParams = 1;
                  }
-                 join.addSQL(buf, context);
-                 // Merge subquery params
-                 Object[] subQueryParams = join.getSubqueryParams(whichParams);
-                 if (subQueryParams!=null)
-                     mergeSubqueryParams(subQueryParams);
+                 // check
+                 addJoin(buf, join, context, whichParams);
                  // add CRLF
                  if( i!=joins.size()-1 )
                      buf.append("\r\n");
@@ -1664,6 +1936,29 @@ public abstract class DBCommand extends DBCommandExpr
         }
     }
     
+    protected void addJoin(StringBuilder buf, DBJoinExpr join, long context, int whichParams)
+    {
+        // remember insert pos
+        int paramInsertPos = paramUsageCount;
+        // now add the join
+        join.addSQL(buf, context);
+        // Merge subquery params
+        Object[] subQueryParams = join.getSubqueryParams(whichParams);
+        if (subQueryParams!=null)
+        {
+            if (paramInsertPos == paramUsageCount)
+                mergeSubqueryParams(subQueryParams);
+            else
+            {   // Some Params have been used in additional Join constraints
+                int tempCounter = paramUsageCount;
+                paramUsageCount = paramInsertPos;
+                mergeSubqueryParams(subQueryParams);
+                int insertCount = (paramUsageCount - paramInsertPos);
+                paramUsageCount = tempCounter + insertCount;
+            }
+        }
+    }
+    
     protected void mergeSubqueryParams(Object[] subQueryParams)
     {
         if (subQueryParams==null || subQueryParams.length==0)
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBCommandExpr.java b/empire-db/src/main/java/org/apache/empire/db/DBCommandExpr.java
index 2db0887..24fac9a 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBCommandExpr.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBCommandExpr.java
@@ -26,8 +26,11 @@ import java.util.Set;
 import org.apache.empire.commons.Options;
 import org.apache.empire.data.DataType;
 import org.apache.empire.db.expr.compare.DBCompareExpr;
+import org.apache.empire.db.expr.join.DBJoinExpr;
 import org.apache.empire.db.expr.order.DBOrderByExpr;
+import org.apache.empire.db.expr.set.DBSetExpr;
 import org.apache.empire.dbms.DBMSHandler;
+import org.apache.empire.exceptions.InternalException;
 import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.NotSupportedException;
 import org.apache.empire.exceptions.ObjectNotValidException;
@@ -290,6 +293,25 @@ public abstract class DBCommandExpr extends DBExpr
         // Default Constructor
     }
 
+    /**
+     * Creates a clone of this class.
+     */
+    @Override
+    public DBCommandExpr clone()
+    {
+        try 
+        {   DBCommandExpr clone = (DBCommandExpr)super.clone();
+            // Clone lists
+            if (orderBy!=null)
+                clone.orderBy = new ArrayList<DBOrderByExpr>(orderBy);
+            // done
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            log.error("Cloning DBCommand object failed!", e);
+            throw new InternalException(e); 
+        }
+    }
+    
     public abstract boolean isValid();
     
     /**
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBQuery.java b/empire-db/src/main/java/org/apache/empire/db/DBQuery.java
index ca4981c..553ff8e 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBQuery.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBQuery.java
@@ -246,7 +246,7 @@ public class DBQuery extends DBRowSet
     {
         for (int i = 0; i < queryColumns.length; i++)
         {
-            if (queryColumns[i].exprEquals(expr))
+            if (ObjectUtils.compareEqual(queryColumns[i].getExpr(), expr))
                 return queryColumns[i];
         }
         // not found
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBQueryColumn.java b/empire-db/src/main/java/org/apache/empire/db/DBQueryColumn.java
index 2f568a3..43d4772 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBQueryColumn.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBQueryColumn.java
@@ -46,16 +46,6 @@ public class DBQueryColumn extends DBColumn
     {
         return expr;
     }
-    
-    public boolean exprEquals(DBColumnExpr other)
-    {
-        if (other.isWrapper() && !expr.isWrapper())
-            return expr.equals(other.unwrap());
-        else if (!other.isWrapper() && expr.isWrapper())
-            return expr.unwrap().equals(other);
-        // both wrapped or both unwrapped
-        return expr.equals(other);
-    }
 
     @Override
     public DataType getDataType()
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java
index bc88c43..d9a3c39 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBCmdParam;
 import org.apache.empire.db.DBColumn;
 import org.apache.empire.db.DBColumnExpr;
 import org.apache.empire.db.DBCommand;
@@ -116,101 +117,12 @@ public class DBCommandHSql extends DBCommand
         }
         if (updateJoin==null)
             throw new ObjectNotValidException(this);
-        Set<DBColumn> joinColumns = new HashSet<DBColumn>();
-        updateJoin.addReferencedColumns(joinColumns);
         // using
-        buf.append("\r\nUSING ");
-        DBCommand inner = this.clone();
-        inner.clearSelect();
-        inner.clearOrderBy();
-        DBRowSet outerTable = updateJoin.getOuterTable();
-        if (outerTable==null)
-            outerTable=table;
-        for (DBColumn jcol : joinColumns)
-        {   // Select join columns
-            if (jcol.getRowSet().equals(outerTable)==false)
-                inner.select(jcol);
-        }
-        // find the source table
-        DBColumnExpr left  = updateJoin.getLeft();
-        DBColumnExpr right = updateJoin.getRight();
-        DBRowSet source = right.getUpdateColumn().getRowSet();
-        if (source==table)
-            source = left.getUpdateColumn().getRowSet();
-        // Add set expressions
-        String sourceAliasPrefix = source.getAlias()+".";
-        List<DBSetExpr> mergeSet = new ArrayList<DBSetExpr>(set.size());   
-        for (DBSetExpr sex : set)
-        {   // Select set expressions
-            Object val = sex.getValue();
-            if (val instanceof DBColumnExpr)
-            {
-                DBColumnExpr expr = ((DBColumnExpr)val);
-                if (!(expr instanceof DBColumn) && !(expr instanceof DBAliasExpr))
-                {   // rename column
-                    String name = "COL_"+String.valueOf(mergeSet.size());
-                    expr = expr.as(name);
-                }
-                // select
-                inner.select(expr);
-                // Name
-                DBValueExpr NAME_EXPR = getDatabase().getValueExpr(sourceAliasPrefix+expr.getName(), DataType.UNKNOWN);
-                mergeSet.add(sex.getColumn().to(NAME_EXPR));
-            }
-            else
-            {   // add original
-                mergeSet.add(sex);
-            }
-        }
-        // remove join (if not necessary)
-        if (inner.hasConstraintOn(table)==false)
-            inner.removeJoinsOn(table);
-        // add SQL for inner statement
-        inner.addSQL(buf, CTX_DEFAULT);
-        // add Alias
-        buf.append(" ");
-        buf.append(source.getAlias());
-        buf.append("\r\nON (");
-        left.addSQL(buf, CTX_DEFAULT);
-        buf.append(" = ");
-        right.addSQL(buf, CTX_DEFAULT);
-        // Compare Expression
-        if (updateJoin.getWhere() != null)
-        {   buf.append(" AND ");
-            updateJoin.getWhere().addSQL(buf, CTX_DEFAULT);
-        }
-        // More constraints
-        for (DBCompareExpr we : this.where) 
-        {
-            if (we instanceof DBCompareColExpr)
-            {   // a compare column expression
-                DBCompareColExpr cce = (DBCompareColExpr)we;
-                DBColumn ccecol = cce.getColumn().getUpdateColumn();
-                if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
-                {
-                    buf.append(" AND ");
-                    cce.addSQL(buf, CTX_DEFAULT);
-                }
-            }
-            else
-            {   // just add
-                buf.append(" AND ");
-                we.addSQL(buf, CTX_DEFAULT);
-            }
-        }
+        DBMergeCommand merge = createMergeCommand();
+        List<DBSetExpr> mergeSet = merge.addUsing(buf, table, updateJoin);
         // Set Expressions
         buf.append(")\r\nWHEN MATCHED THEN UPDATE ");
         buf.append("\r\nSET ");
         addListExpr(buf, mergeSet, CTX_DEFAULT, ", ");
     }
-        
-    protected boolean isSetColumn(DBColumn col)
-    {
-        for (DBSetExpr se : this.set)
-        {
-            if (se.getColumn().equals(col))
-                return true;
-        }
-        return false;
-    }
 }
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
index 506f1f3..3791a1b 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
@@ -18,22 +18,12 @@
  */
 package org.apache.empire.dbms.oracle;
 
-import java.util.ArrayList;
-// Imports
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 import org.apache.empire.commons.StringUtils;
-import org.apache.empire.data.DataType;
-import org.apache.empire.db.DBColumn;
-import org.apache.empire.db.DBColumnExpr;
 import org.apache.empire.db.DBCommand;
 import org.apache.empire.db.DBIndex;
 import org.apache.empire.db.DBRowSet;
-import org.apache.empire.db.expr.column.DBAliasExpr;
-import org.apache.empire.db.expr.column.DBValueExpr;
-import org.apache.empire.db.expr.compare.DBCompareColExpr;
 import org.apache.empire.db.expr.compare.DBCompareExpr;
 import org.apache.empire.db.expr.join.DBColumnJoinExpr;
 import org.apache.empire.db.expr.join.DBJoinExpr;
@@ -287,103 +277,14 @@ public class DBCommandOracle extends DBCommand
         }
         if (updateJoin==null)
             throw new ObjectNotValidException(this);
-        Set<DBColumn> joinColumns = new HashSet<DBColumn>();
-        updateJoin.addReferencedColumns(joinColumns);
         // using
-        buf.append("\r\nUSING ");
-        DBCommand inner = this.clone();
-        inner.clearSelect();
-        inner.clearOrderBy();
-        DBRowSet outerTable = updateJoin.getOuterTable();
-        if (outerTable==null)
-            outerTable=table;
-        for (DBColumn jcol : joinColumns)
-        {   // Select join columns
-            if (jcol.getRowSet().equals(outerTable)==false)
-                inner.select(jcol);
-        }
-        // find the source table
-        DBColumnExpr left  = updateJoin.getLeft();
-        DBColumnExpr right = updateJoin.getRight();
-        DBRowSet source = right.getUpdateColumn().getRowSet();
-        if (source==table)
-            source = left.getUpdateColumn().getRowSet();
-        // Add set expressions
-        String sourceAliasPrefix = source.getAlias()+".";
-        List<DBSetExpr> mergeSet = new ArrayList<DBSetExpr>(set.size());   
-        for (DBSetExpr sex : set)
-        {   // Select set expressions
-            Object val = sex.getValue();
-            if (val instanceof DBColumnExpr)
-            {
-                DBColumnExpr expr = ((DBColumnExpr)val);
-                if (!(expr instanceof DBColumn) && !(expr instanceof DBAliasExpr))
-                {   // rename column
-                    String name = "COL_"+String.valueOf(mergeSet.size());
-                    expr = expr.as(name);
-                }
-                // select
-                inner.select(expr);
-                // Name
-                DBValueExpr NAME_EXPR = getDatabase().getValueExpr(sourceAliasPrefix+expr.getName(), DataType.UNKNOWN);
-                mergeSet.add(sex.getColumn().to(NAME_EXPR));
-            }
-            else
-            {   // add original
-                mergeSet.add(sex);
-            }
-        }
-        // remove join (if not necessary)
-        if (inner.hasConstraintOn(table)==false)
-            inner.removeJoinsOn(table);
-        // add SQL for inner statement
-        inner.addSQL(buf, CTX_DEFAULT);
-        // add Alias
-        buf.append(" ");
-        buf.append(source.getAlias());
-        buf.append("\r\nON (");
-        left.addSQL(buf, CTX_DEFAULT);
-        buf.append(" = ");
-        right.addSQL(buf, CTX_DEFAULT);
-        // Compare Expression
-        if (updateJoin.getWhere() != null)
-        {   buf.append(" AND ");
-            updateJoin.getWhere().addSQL(buf, CTX_DEFAULT);
-        }
-        // More constraints
-        for (DBCompareExpr we : this.where) 
-        {
-            if (we instanceof DBCompareColExpr)
-            {   // a compare column expression
-                DBCompareColExpr cce = (DBCompareColExpr)we;
-                DBColumn ccecol = cce.getColumn().getUpdateColumn();
-                if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
-                {
-                    buf.append(" AND ");
-                    cce.addSQL(buf, CTX_DEFAULT);
-                }
-            }
-            else
-            {   // just add
-                buf.append(" AND ");
-                we.addSQL(buf, CTX_DEFAULT);
-            }
-        }
+        DBMergeCommand merge = createMergeCommand();
+        List<DBSetExpr> mergeSet = merge.addUsing(buf, table, updateJoin);
         // Set Expressions
         buf.append(")\r\nWHEN MATCHED THEN UPDATE ");
         buf.append("\r\nSET ");
         addListExpr(buf, mergeSet, CTX_DEFAULT, ", ");
     }
-        
-    protected boolean isSetColumn(DBColumn col)
-    {
-        for (DBSetExpr se : this.set)
-        {
-            if (se.getColumn().equals(col))
-                return true;
-        }
-        return false;
-    }
     
     /**
      * Creates an Oracle specific delete statement.
diff --git a/empire-db/src/test/java/org/apache/empire/commons/ObjectUtilsTest.java b/empire-db/src/test/java/org/apache/empire/commons/ObjectUtilsTest.java
index 72025bf..9b48375 100644
--- a/empire-db/src/test/java/org/apache/empire/commons/ObjectUtilsTest.java
+++ b/empire-db/src/test/java/org/apache/empire/commons/ObjectUtilsTest.java
@@ -60,7 +60,7 @@ public class ObjectUtilsTest
 	@Test
 	public void testCompareEqual()
 	{
-		assertTrue(ObjectUtils.compareEqual(null, null));
+		assertTrue(ObjectUtils.compareEqual((Object)null, null));
 		
 		Object object = new Object();
 		assertTrue(ObjectUtils.compareEqual(object, object));