db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r1448025 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/types/ engine/org/apache/derby/impl/sql/compile/ testing/org/apache/derbyTesting/functionTests/tests/lang/
Date Wed, 20 Feb 2013 07:57:50 GMT
Author: kahatlen
Date: Wed Feb 20 07:57:49 2013
New Revision: 1448025

URL: http://svn.apache.org/r1448025
Log:
DERBY-6017: IN lists with mixed types may return wrong results

Cast the left operand to the dominant type if it isn't already of
that type. This ensures the dominant type is always used in all the
comparison operations when evaluating an IN predicate.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataType.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataTypeDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InListOperatorNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNodeList.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/InPredicateTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataType.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataType.java?rev=1448025&r1=1448024&r2=1448025&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataType.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataType.java Wed Feb 20 07:57:49
2013
@@ -1140,6 +1140,13 @@ public abstract class DataType
 		 * compare using the dominant type of the two values being compared.
 		 * Otherwise we can end up with wrong results when doing the binary
 		 * search (ex. as caused by incorrect truncation).  DERBY-2256.
+         *
+         * DERBY-6017: Actually, it's not good enough to compare using the
+         * dominant type of the two values being compared. It has to be the
+         * dominant type of *all* the values in the left operand and the right
+         * operand. Therefore, InListOperatorNode.preprocess() inserts a
+         * cast if necessary to ensure that either the left side or the right
+         * side of each comparison is of the overall dominating type.
 		 */
 		int leftPrecedence = left.typePrecedence();
 		DataValueDescriptor comparator = null;

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataTypeDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataTypeDescriptor.java?rev=1448025&r1=1448024&r2=1448025&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataTypeDescriptor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/DataTypeDescriptor.java Wed
Feb 20 07:57:49 2013
@@ -700,11 +700,8 @@ public final class DataTypeDescriptor im
 	 * @param cf		A ClassFactory
 	 *
 	 * @return DataTypeDescriptor  DTS for dominant type
-	 *
-	 * @exception StandardException		Thrown on error
 	 */
 	public DataTypeDescriptor getDominantType(DataTypeDescriptor otherDTS, ClassFactory cf)
-			throws StandardException
 	{
 		boolean				nullable;
 		TypeId				thisType;

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InListOperatorNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InListOperatorNode.java?rev=1448025&r1=1448024&r2=1448025&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InListOperatorNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InListOperatorNode.java
Wed Feb 20 07:57:49 2013
@@ -150,7 +150,29 @@ public final class InListOperatorNode ex
 			equal.bindComparisonOperator();
 			return equal;
 		}
-		else if ((leftOperand instanceof ColumnReference) &&
+
+        // DERBY-6017: All comparisons have to be performed using the dominant
+        // type of *all* the values in the left operand and the right operand.
+        // If either the left operand is of the dominant type, or all of the
+        // values in the right operand are of the dominant type, we know that
+        // each comparison will be performed using the dominant type.
+        // Otherwise, cast the left operand to the dominant type to ensure
+        // that each comparison operation will use the dominant type.
+        DataTypeDescriptor targetType = getDominantType();
+        int targetTypePrecedence = targetType.getTypeId().typePrecedence();
+        if ((leftOperand.getTypeServices().getTypeId().typePrecedence() !=
+                    targetTypePrecedence) &&
+                !rightOperandList.allSamePrecendence(targetTypePrecedence)) {
+            CastNode cn = (CastNode) getNodeFactory().getNode(
+                    C_NodeTypes.CAST_NODE,
+                    leftOperand,
+                    targetType,
+                    getContextManager());
+            cn.bindCastNodeOnly();
+            leftOperand = cn;
+        }
+
+        if ((leftOperand instanceof ColumnReference) &&
 				 rightOperandList.containsOnlyConstantAndParamNodes())
 		{
 			/* At this point we have an IN-list made up of constant and/or
@@ -235,27 +257,8 @@ public final class InListOperatorNode ex
 				 * would lead to comparisons with truncated values and could
 				 * therefore lead to an incorrect sort order. DERBY-2256.
 				 */
-				DataTypeDescriptor targetType = leftOperand.getTypeServices();
-				TypeId judgeTypeId = targetType.getTypeId();
-
-				if (!rightOperandList.allSamePrecendence(
-					judgeTypeId.typePrecedence()))
-				{
-					/* Iterate through the entire list of values to find out
-					 * what the dominant type is.
-					 */
-					ClassFactory cf = getClassFactory();
-					int sz = rightOperandList.size();
-					for (int i = 0; i < sz; i++)
-					{
-						ValueNode vn = (ValueNode)rightOperandList.elementAt(i);
-						targetType =
-							targetType.getDominantType(
-								vn.getTypeServices(), cf);
-					}
-				}
  
-				/* Now wort the list in ascending order using the dominant
+				/* Now sort the list in ascending order using the dominant
 				 * type found above.
 				 */
 				DataValueDescriptor judgeODV = targetType.getNull();
@@ -279,27 +282,6 @@ public final class InListOperatorNode ex
 
 				if (judgeODV.equals(minODV, maxODV).equals(true))
 				{
-                    int judgePrecedence = judgeODV.typePrecedence();
-                    int leftPrecedence = leftOperand.getTypeServices()
-                            .getTypeId().typePrecedence();
-                    if (leftPrecedence != judgePrecedence &&
-                            minODV.typePrecedence() != judgePrecedence) {
-                        // DERBY-6017: If neither the minimum value nor the
-                        // left operand is of the dominant type, cast the
-                        // minimum value to the dominant type. Otherwise, the
-                        // equals operation will be performed using a different
-                        // type, which may not have the same ordering as the
-                        // type used to sort the list, and it could produce
-                        // unexpected results.
-                        CastNode cn = (CastNode) getNodeFactory().getNode(
-                                C_NodeTypes.CAST_NODE,
-                                minValue,
-                                targetType,
-                                getContextManager());
-                        cn.bindCastNodeOnly();
-                        minValue = cn;
-                    }
-
 					BinaryComparisonOperatorNode equal = 
 						(BinaryComparisonOperatorNode)getNodeFactory().getNode(
 							C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,
@@ -386,6 +368,31 @@ public final class InListOperatorNode ex
 		}
 	}
 
+    /**
+     * Get the dominant type of all the operands in this IN list.
+     * @return the type descriptor for the dominant type
+     * @see DataTypeDescriptor#getDominantType(DataTypeDescriptor, ClassFactory)
+     */
+    private DataTypeDescriptor getDominantType() {
+        DataTypeDescriptor targetType = leftOperand.getTypeServices();
+        TypeId judgeTypeId = targetType.getTypeId();
+
+        if (!rightOperandList.allSamePrecendence(
+                judgeTypeId.typePrecedence())) {
+            // Iterate through the entire list of values to find out
+            // what the dominant type is.
+            ClassFactory cf = getClassFactory();
+            int sz = rightOperandList.size();
+            for (int i = 0; i < sz; i++) {
+                ValueNode vn = (ValueNode) rightOperandList.elementAt(i);
+                targetType = targetType.getDominantType(
+                        vn.getTypeServices(), cf);
+            }
+        }
+
+        return targetType;
+    }
+
 	/**
 	 * Eliminate NotNodes in the current query block.  We traverse the tree, 
 	 * inverting ANDs and ORs and eliminating NOTs as we go.  We stop at 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNodeList.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNodeList.java?rev=1448025&r1=1448024&r2=1448025&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNodeList.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNodeList.java Wed
Feb 20 07:57:49 2013
@@ -260,7 +260,6 @@ public class ValueNodeList extends Query
 	 *			type precendence as the specified value.
 	 */
 	boolean allSamePrecendence(int precedence)
-	throws StandardException
 	{
 		boolean allSame = true;
 		int size = size();

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/InPredicateTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/InPredicateTest.java?rev=1448025&r1=1448024&r2=1448025&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/InPredicateTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/InPredicateTest.java
Wed Feb 20 07:57:49 2013
@@ -118,4 +118,68 @@ public class InPredicateTest extends Bas
                 " 9223372036854775807, 9.223372036854776E18)"),
                 allRows);
     }
+
+    /**
+     * Another test case for DERBY-6017. Derby used to evaluate IN lists by
+     * using the semantics of the dominant type of the two values being
+     * compared. It should use the dominant type of all the values in the
+     * left operand and in the IN list. Verify that it works as expected now.
+     */
+    public void testMixedTypes() throws SQLException {
+        setAutoCommit(false);
+
+        // Test an IN predicate that mixes BIGINT and DOUBLE.
+
+        Statement s = createStatement();
+        s.executeUpdate("create table t2(b1 bigint, b2 bigint, d double)");
+        s.executeUpdate("insert into t2 values " +
+                        "(9223372036854775805, 9223372036854775806, 1)");
+
+        // The first query used to return zero rows. However, the next two
+        // queries used to return one row, and SQL:2003, 8.4 <in predicate>
+        // says that the three queries are equivalent. Now, they all return
+        // one row.
+
+        JDBC.assertSingleValueResultSet(
+            s.executeQuery("select true from t2 where b1 in (b2, d)"),
+            "true");
+
+        JDBC.assertSingleValueResultSet(
+            s.executeQuery("select true from t2 where b1 in (values b2, d)"),
+            "true");
+
+        JDBC.assertSingleValueResultSet(
+            s.executeQuery("select true from t2 where b1 = any (values b2, d)"),
+            "true");
+
+        // Test an IN predicate that mixes INT and REAL. They are supposed
+        // to be compared using DOUBLE semantics, but used to be compared as
+        // REALs.
+
+        s.executeUpdate("create table t3 (i1 int, r1 real, r2 real)");
+        s.executeUpdate("insert into t3 values " +
+                        "(2147483645, 2147483645, 2147483645), " +
+                        "(2147483645, 2147483645, 0)");
+
+        String[][] expectedRows = {
+            { "2147483645", "2.14748365E9", "2.14748365E9" }
+        };
+
+        // The first query used to return two rows. However, the next two
+        // queries used to return one row, and SQL:2003, 8.4 <in predicate>
+        // says that the three queries are equivalent. Now, they all return
+        // one row.
+
+        JDBC.assertFullResultSet(
+            s.executeQuery("select * from t3 where r1 in (i1, r2)"),
+            expectedRows);
+
+        JDBC.assertFullResultSet(
+            s.executeQuery("select * from t3 where r1 in (values i1, r2)"),
+            expectedRows);
+
+        JDBC.assertFullResultSet(
+            s.executeQuery("select * from t3 where r1 = any (values i1, r2)"),
+            expectedRows);
+    }
 }



Mime
View raw message