--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.BinaryComparisonOperatorNode\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.sql.compile.TypeCompiler;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import java.util.Vector;\r
+import java.sql.Types;\r
+\r
+/**\r
+ * This node is the superclass for all binary comparison operators, such as =,\r
+ * <>, <, etc.\r
+ *\r
+ */\r
+\r
+public abstract class BinaryComparisonOperatorNode extends BinaryOperatorNode\r
+{\r
+ // Use between selectivity?\r
+ private boolean forQueryRewrite;\r
+ private boolean betweenSelectivity;\r
+\r
+ /**\r
+ * Initializer for a BinaryComparisonOperatorNode\r
+ *\r
+ * @param leftOperand The left operand of the comparison\r
+ * @param rightOperand The right operand of the comparison\r
+ * @param operator The name of the operator\r
+ * @param methodName The name of the method to call in the generated\r
+ * class\r
+ */\r
+\r
+ public void init(\r
+ Object leftOperand,\r
+ Object rightOperand,\r
+ Object operator,\r
+ Object methodName)\r
+ {\r
+ super.init(leftOperand, rightOperand, operator, methodName,\r
+ ClassName.DataValueDescriptor, ClassName.DataValueDescriptor);\r
+ }\r
+\r
+ /**\r
+ * This node was generated as part of a query rewrite. Bypass the\r
+ * normal comparability checks.\r
+ * @param val true if this was for a query rewrite\r
+ */\r
+ public void setForQueryRewrite(boolean val)\r
+ {\r
+ forQueryRewrite=val;\r
+ }\r
+\r
+ /**\r
+ * Was this node generated in a query rewrite?\r
+ *\r
+ * @return true if it was generated in a query rewrite.\r
+ */\r
+ public boolean getForQueryRewrite()\r
+ {\r
+ return forQueryRewrite;\r
+ }\r
+\r
+ /**\r
+ * Use between selectivity when calculating the selectivity.\r
+ */\r
+ void setBetweenSelectivity()\r
+ {\r
+ betweenSelectivity = true;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not to use the between selectivity for this node.\r
+ *\r
+ * @return Whether or not to use the between selectivity for this node.\r
+ */\r
+ boolean getBetweenSelectivity()\r
+ {\r
+ return betweenSelectivity;\r
+ }\r
+\r
+\r
+ /**\r
+ * Bind this comparison operator. All that has to be done for binding\r
+ * a comparison operator is to bind the operands, check the compatibility\r
+ * of the types, and set the result type to SQLBoolean.\r
+ *\r
+ * @param fromList The query's FROM list\r
+ * @param subqueryList The subquery list being built as we find SubqueryNodes\r
+ * @param aggregateVector The aggregate vector being built as we find AggregateNodes\r
+ *\r
+ * @return The new top of the expression tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public ValueNode bindExpression(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ super.bindExpression(fromList, subqueryList, aggregateVector);\r
+\r
+ TypeCompiler leftTC = leftOperand.getTypeCompiler();\r
+ TypeCompiler rightTC = rightOperand.getTypeCompiler();\r
+ TypeId leftTypeId = leftOperand.getTypeId();\r
+ TypeId rightTypeId = rightOperand.getTypeId();\r
+\r
+ /*\r
+ * If we are comparing a non-string with a string type, then we\r
+ * must prevent the non-string value from being used to probe into\r
+ * an index on a string column. This is because the string types\r
+ * are all of low precedence, so the comparison rules of the non-string\r
+ * value are used, so it may not find values in a string index because\r
+ * it will be in the wrong order. So, cast the string value to its\r
+ * own type. This is easier than casting it to the non-string type,\r
+ * because we would have to figure out the right length to cast it to.\r
+ */\r
+ if (! leftTypeId.isStringTypeId() && rightTypeId.isStringTypeId())\r
+ {\r
+ DataTypeDescriptor rightTypeServices = rightOperand.getTypeServices();\r
+\r
+ rightOperand = (ValueNode)\r
+ getNodeFactory().getNode(\r
+ C_NodeTypes.CAST_NODE,\r
+ rightOperand, \r
+ new DataTypeDescriptor(\r
+ rightTypeId,\r
+ true, \r
+ rightTypeServices.getMaximumWidth()),\r
+ getContextManager());\r
+ ((CastNode) rightOperand).bindCastNodeOnly();\r
+ }\r
+ else if (! rightTypeId.isStringTypeId() && leftTypeId.isStringTypeId())\r
+ {\r
+ DataTypeDescriptor leftTypeServices = leftOperand.getTypeServices();\r
+\r
+ leftOperand = (ValueNode)\r
+ getNodeFactory().getNode(\r
+ C_NodeTypes.CAST_NODE,\r
+ leftOperand, \r
+ new DataTypeDescriptor(\r
+ leftTypeId,\r
+ true, \r
+ leftTypeServices.getMaximumWidth()),\r
+ getContextManager());\r
+ ((CastNode) leftOperand).bindCastNodeOnly();\r
+ }\r
+\r
+ /* Test type compatability and set type info for this node */\r
+ bindComparisonOperator();\r
+\r
+ return this;\r
+ }\r
+\r
+\r
+ /**\r
+ * Test the type compatability of the operands and set the type info\r
+ * for this node. This method is useful both during binding and\r
+ * when we generate nodes within the language module outside of the parser.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindComparisonOperator()\r
+ throws StandardException\r
+ {\r
+ TypeId leftType;\r
+ TypeId rightType;\r
+ boolean nullableResult;\r
+\r
+ leftType = leftOperand.getTypeId();\r
+ rightType = rightOperand.getTypeId();\r
+\r
+\r
+ /*\r
+ ** Can the types be compared to each other? If not, throw an\r
+ ** exception.\r
+ */\r
+ boolean forEquals = operator.equals("=") || operator.equals("<>");\r
+\r
+ boolean cmp = leftOperand.getTypeServices().comparable(\r
+ rightOperand.getTypeServices(),\r
+ forEquals,\r
+ getClassFactory());\r
+ // Bypass the comparable check if this is a rewrite from the \r
+ // optimizer. We will assume Mr. Optimizer knows what he is doing.\r
+ if (!cmp && !forQueryRewrite) {\r
+ throw StandardException.newException(SQLState.LANG_NOT_COMPARABLE, \r
+ leftOperand.getTypeServices().getSQLTypeNameWithCollation() ,\r
+ rightOperand.getTypeServices().getSQLTypeNameWithCollation());\r
+ \r
+ }\r
+\r
+ \r
+ /*\r
+ ** Set the result type of this comparison operator based on the\r
+ ** operands. The result type is always SQLBoolean - the only question\r
+ ** is whether it is nullable or not. If either of the operands is\r
+ ** nullable, the result of the comparison must be nullable, too, so\r
+ ** we can represent the unknown truth value.\r
+ */\r
+ nullableResult = leftOperand.getTypeServices().isNullable() ||\r
+ rightOperand.getTypeServices().isNullable();\r
+ setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID, nullableResult));\r
+\r
+\r
+ }\r
+\r
+ /**\r
+ * Preprocess an expression tree. We do a number of transformations\r
+ * here (including subqueries, IN lists, LIKE and BETWEEN) plus\r
+ * subquery flattening.\r
+ * NOTE: This is done before the outer ResultSetNode is preprocessed.\r
+ *\r
+ * @param numTables Number of tables in the DML Statement\r
+ * @param outerFromList FromList from outer query block\r
+ * @param outerSubqueryList SubqueryList from outer query block\r
+ * @param outerPredicateList PredicateList from outer query block\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode preprocess(int numTables,\r
+ FromList outerFromList,\r
+ SubqueryList outerSubqueryList,\r
+ PredicateList outerPredicateList) \r
+ throws StandardException\r
+ {\r
+ leftOperand = leftOperand.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+\r
+ /* This is where we start to consider flattening expression subqueries based\r
+ * on a uniqueness condition. If the right child is a SubqueryNode then\r
+ * it is a potentially flattenable expression subquery. If we flatten the\r
+ * subquery then we at least need to change the right operand of this \r
+ * comparison. However, we may want to push the comparison into the subquery\r
+ * itself and replace this outer comparison with TRUE in the tree. Thus we\r
+ * return rightOperand.preprocess() if the rightOperand is a SubqueryNode.\r
+ * NOTE: SubqueryNode.preprocess() is smart enough to return this node\r
+ * if it is not flattenable.\r
+ * NOTE: We only do this if the subquery has not yet been preprocessed.\r
+ * (A subquery can get preprocessed multiple times if it is a child node\r
+ * in an expression that gets transformed, like BETWEEN. The subquery\r
+ * remembers whether or not it has been preprocessed and simply returns if\r
+ * it has already been preprocessed. The return returns the SubqueryNode,\r
+ * so an invalid tree is returned if we set the parent comparison operator\r
+ * when the subquery has already been preprocessed.)\r
+ */\r
+ if ((rightOperand instanceof SubqueryNode) &&\r
+ !((SubqueryNode) rightOperand).getPreprocessed())\r
+ {\r
+ ((SubqueryNode) rightOperand).setParentComparisonOperator(this);\r
+ return rightOperand.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+ }\r
+ else\r
+ {\r
+ rightOperand = rightOperand.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+ return this;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Eliminate NotNodes in the current query block. We traverse the tree, \r
+ * inverting ANDs and ORs and eliminating NOTs as we go. We stop at \r
+ * ComparisonOperators and boolean expressions. We invert \r
+ * ComparisonOperators and replace boolean expressions with \r
+ * boolean expression = false.\r
+ * NOTE: Since we do not recurse under ComparisonOperators, there\r
+ * still could be NotNodes left in the tree.\r
+ *\r
+ * @param underNotNode Whether or not we are under a NotNode.\r
+ * \r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ ValueNode eliminateNots(boolean underNotNode) \r
+ throws StandardException\r
+ {\r
+ if (! underNotNode)\r
+ {\r
+ return this;\r
+ }\r
+\r
+ /* Convert the BinaryComparison operator to its negation */\r
+ return getNegation(leftOperand, rightOperand);\r
+ }\r
+\r
+ /**\r
+ * Negate the comparison.\r
+ *\r
+ * @param leftOperand The left operand of the comparison operator\r
+ * @param rightOperand The right operand of the comparison operator\r
+ *\r
+ * @return BinaryOperatorNode The negated expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ BinaryOperatorNode getNegation(ValueNode leftOperand,\r
+ ValueNode rightOperand)\r
+ throws StandardException\r
+ {\r
+ /* Keep the compiler happy - this method should never be called.\r
+ * We should always be calling the method in a sub-class.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(false,\r
+ "expected to call getNegation() for subclass " +\r
+ getClass().toString());\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Finish putting an expression into conjunctive normal\r
+ * form. An expression tree in conjunctive normal form meets\r
+ * the following criteria:\r
+ * o If the expression tree is not null,\r
+ * the top level will be a chain of AndNodes terminating\r
+ * in a true BooleanConstantNode.\r
+ * o The left child of an AndNode will never be an AndNode.\r
+ * o Any right-linked chain that includes an AndNode will\r
+ * be entirely composed of AndNodes terminated by a true BooleanConstantNode.\r
+ * o The left child of an OrNode will never be an OrNode.\r
+ * o Any right-linked chain that includes an OrNode will\r
+ * be entirely composed of OrNodes terminated by a false BooleanConstantNode.\r
+ * o ValueNodes other than AndNodes and OrNodes are considered\r
+ * leaf nodes for purposes of expression normalization.\r
+ * In other words, we won't do any normalization under\r
+ * those nodes.\r
+ *\r
+ * In addition, we track whether or not we are under a top level AndNode. \r
+ * SubqueryNodes need to know this for subquery flattening.\r
+ *\r
+ * @param underTopAndNode Whether or not we are under a top level AndNode.\r
+ * \r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode changeToCNF(boolean underTopAndNode) \r
+ throws StandardException\r
+ {\r
+ /* If our right child is a subquery and we are under a top and node\r
+ * then we want to mark the subquery as under a top and node.\r
+ * That will allow us to consider flattening it.\r
+ */\r
+ if (underTopAndNode && (rightOperand instanceof SubqueryNode))\r
+ {\r
+ rightOperand = rightOperand.changeToCNF(underTopAndNode);\r
+ }\r
+\r
+ return this;\r
+ }\r
+ \r
+ /** @see BinaryOperatorNode#genSQLJavaSQLTree */\r
+ public ValueNode genSQLJavaSQLTree() throws StandardException\r
+ {\r
+ TypeId leftTypeId = leftOperand.getTypeId();\r
+\r
+ /* If I have Java types, I need only add java->sql->java if the types\r
+ * are not comparable \r
+ */\r
+ if (leftTypeId.userType())\r
+ {\r
+ if (leftOperand.getTypeServices().comparable(leftOperand.getTypeServices(),\r
+ false, getClassFactory()))\r
+ return this;\r
+\r
+ leftOperand = leftOperand.genSQLJavaSQLTree();\r
+ }\r
+\r
+ TypeId rightTypeId = rightOperand.getTypeId();\r
+\r
+ if (rightTypeId.userType())\r
+ {\r
+ if (rightOperand.getTypeServices().comparable(rightOperand.getTypeServices(),\r
+ false, getClassFactory()))\r
+ return this;\r
+\r
+ rightOperand = rightOperand.genSQLJavaSQLTree();\r
+ }\r
+\r
+ return this;\r
+ }\r
+}\r