--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.ValueNode\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 java.util.Vector;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.NodeFactory;\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+import org.apache.derby.iapi.sql.compile.TypeCompiler;\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueFactory;\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+/**\r
+ * A ValueNode is an abstract class for all nodes that can represent data\r
+ * values, that is, constants, columns, and expressions.\r
+ *\r
+ */\r
+\r
+public abstract class ValueNode extends QueryTreeNode\r
+{\r
+ /**\r
+ * The data type for this node.\r
+ */\r
+ protected DataTypeDescriptor dataTypeServices;\r
+ \r
+ private TypeCompiler typeCompiler;\r
+\r
+ // Whether or not additional predicates have been created from this one.\r
+ boolean transformed;\r
+\r
+ /*\r
+ ** Constructor for untyped ValueNodes, for example, untyped NULLs\r
+ ** and parameter nodes.\r
+ **\r
+ ** Binding will replace all untyped ValueNodes with typed ValueNodes\r
+ ** when it figures out what their types should be.\r
+ */\r
+ public ValueNode()\r
+ {\r
+ }\r
+ \r
+ /**\r
+ * Set this node's type from type components.\r
+ */\r
+ final void setType(TypeId typeId,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ throws StandardException\r
+ \r
+ {\r
+ setType(\r
+ new DataTypeDescriptor(\r
+ (TypeId) typeId,\r
+ isNullable,\r
+ maximumWidth\r
+ )\r
+ ); \r
+ }\r
+\r
+ /**\r
+ * Set this node's type from type components.\r
+ */\r
+ final void setType(TypeId typeId,\r
+ int precision, int scale,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ throws StandardException\r
+ {\r
+ setType(\r
+ new DataTypeDescriptor(\r
+ (TypeId) typeId,\r
+ precision,\r
+ scale,\r
+ isNullable,\r
+ maximumWidth\r
+ )\r
+ ); \r
+ }\r
+\r
+ /**\r
+ * Initializer for numeric types.\r
+ * \r
+ *\r
+ * @param typeId The TypeID of this new node\r
+ * @param precision The precision of this new node\r
+ * @param scale The scale of this new node\r
+ * @param isNullable The nullability of this new node\r
+ * @param maximumWidth The maximum width of this new node\r
+ *\r
+ * @exception StandardException\r
+ */\r
+\r
+ public void init(\r
+ Object typeId,\r
+ Object precision,\r
+ Object scale,\r
+ Object isNullable,\r
+ Object maximumWidth)\r
+ throws StandardException\r
+ {\r
+ setType(\r
+ new DataTypeDescriptor(\r
+ (TypeId) typeId,\r
+ ((Integer) precision).intValue(),\r
+ ((Integer) scale).intValue(),\r
+ ((Boolean) isNullable).booleanValue(),\r
+ ((Integer) maximumWidth).intValue()\r
+ )\r
+ );\r
+ }\r
+\r
+ /**\r
+ * Initializer for non-numeric types.\r
+ * \r
+ *\r
+ * @param tcf The factory to get the\r
+ * DataTypeServicesFactory from\r
+ * @param typeId The TypeID of this new node\r
+ * @param isNullable The nullability of this new node\r
+ * @param maximumWidth The maximum width of this new node\r
+ *\r
+ * @exception StandardException\r
+ */\r
+\r
+ ValueNode(\r
+ Object tcf,\r
+ Object typeId,\r
+ Object isNullable,\r
+ Object maximumWidth)\r
+ throws StandardException\r
+ {\r
+ setType(new DataTypeDescriptor(\r
+ (TypeId) typeId,\r
+ ((Boolean) isNullable).booleanValue(),\r
+ ((Integer) maximumWidth).intValue()\r
+ )\r
+ );\r
+ }\r
+\r
+ \r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return "dataTypeServices: " +\r
+ ( ( dataTypeServices != null) ?\r
+ dataTypeServices.toString() : "null" ) + "\n" +\r
+ super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the DataTypeServices from this ValueNode.\r
+ *\r
+ * @return The DataTypeServices from this ValueNode. This\r
+ * may be null if the node isn't bound yet.\r
+ */\r
+ public DataTypeDescriptor getTypeServices() throws StandardException\r
+ {\r
+ return dataTypeServices;\r
+ }\r
+\r
+ /**\r
+ * Get the TypeId from this ValueNode.\r
+ *\r
+ * @return The TypeId from this ValueNode. This\r
+ * may be null if the node isn't bound yet.\r
+ */\r
+ public TypeId getTypeId() throws StandardException\r
+ {\r
+ DataTypeDescriptor dtd = getTypeServices();\r
+ if (dtd != null)\r
+ return dtd.getTypeId();\r
+ return null;\r
+ }\r
+\r
+\r
+ /**\r
+ Return the DataValueFactory\r
+ */\r
+ protected final DataValueFactory getDataValueFactory() {\r
+ return getLanguageConnectionContext().getDataValueFactory();\r
+ }\r
+\r
+ /**\r
+ * Get the TypeCompiler from this ValueNode, based on its TypeId\r
+ * using getTypeId().\r
+ *\r
+ * @return This ValueNode's TypeCompiler\r
+ *\r
+ */\r
+ public final TypeCompiler getTypeCompiler() throws StandardException\r
+ {\r
+ return getTypeCompiler(getTypeId());\r
+ }\r
+\r
+ /**\r
+ * Set the DataTypeServices for this ValueNode. This method is\r
+ * overridden in ParameterNode.\r
+ *\r
+ * @param dataTypeServices The DataTypeServices to set in this\r
+ * ValueNode\r
+ */\r
+\r
+ public void setType(DataTypeDescriptor dataTypeServices) throws StandardException\r
+ {\r
+ this.dataTypeServices = dataTypeServices;\r
+\r
+ // Clear the typeCompiler, just in case type has changed\r
+ typeCompiler = null;\r
+ }\r
+ \r
+ /**\r
+ * There are many subclasses of ValueNode where we want the \r
+ * DataTypeDescriptor of the node to have the same collation type as the \r
+ * compilation schema's collation type. For that purpose, this method in \r
+ * the baseclass here can be utilized by the subclasses. In addition, the\r
+ * subclasses can pass the collationDerivation that they expect the\r
+ * DataTypeDescriptor to have.\r
+ * \r
+ * @param collationDerivation This can be \r
+ * StringDataValue#COLLATION_DERIVATION_IMPLICIT\r
+ * StringDataValue#COLLATION_DERIVATION_NONE\r
+ * StringDataValue#COLLATION_DERIVATION_EXPLICIT\r
+ * \r
+ * @throws StandardException\r
+ */\r
+ protected void setCollationUsingCompilationSchema(int collationDerivation)\r
+ throws StandardException {\r
+ getTypeServices().setCollationType(\r
+ getSchemaDescriptor(null, false).getCollationType());\r
+ getTypeServices().setCollationDerivation(collationDerivation);\r
+ }\r
+\r
+\r
+ /**\r
+ * Get the source for this ValueNode.\r
+ *\r
+ * @return The source of this ValueNode, null if this node\r
+ * is not sourced by a column.\r
+ */\r
+\r
+ public ResultColumn getSourceResultColumn()\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Mark this predicate has having been transformed (other predicates\r
+ * were generated from it). This will help us with ensure that the\r
+ * predicate does not get calculated into the selectivity multiple\r
+ * times.\r
+ */\r
+ void setTransformed()\r
+ {\r
+ transformed = true;\r
+ }\r
+\r
+ /** \r
+ * Return whether or not this predicate has been transformed.\r
+ *\r
+ * @return Whether or not this predicate has been transformed.\r
+ */\r
+ boolean getTransformed()\r
+ {\r
+ return transformed;\r
+ }\r
+\r
+ \r
+ public ValueNode bindExpression(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ return bindExpression(fromList, subqueryList, aggregateVector,false);\r
+ }\r
+ \r
+\r
+ /**\r
+ * Bind this expression. This is a place-holder method - it should never\r
+ * be called.\r
+ *\r
+ * @param fromList The FROM list to use for binding\r
+ * @param subqueryList The SubqueryList we are building as we hit\r
+ * 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, boolean forQueryRewrite) \r
+ throws StandardException\r
+ {\r
+ /* There are a bizillion classes which extend ValueNode. Here is info\r
+ * on some of the classes that bindExpression() should not be called on\r
+ * and why:\r
+ * o BaseColumnNodes should only appear under the ResultColumnList\r
+ * in the FromBaseTable. They are created/bound when binding the\r
+ * FromBaseTable.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(false, \r
+ "bindExpression() not expected to be called on a " + \r
+ this.getClass().toString());\r
+ }\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Generate a SQL->Java->SQL conversion tree above the current node\r
+ * and bind the new nodes individually.\r
+ * This is useful when doing comparisons, built-in functions, etc. on\r
+ * java types which have a direct mapping to system built-in types.\r
+ *\r
+ * @return ValueNode The new tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode genSQLJavaSQLTree()\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(getTypeId() != null,\r
+ "genSQLJavaSQLTree() only expected to be called on a bound node");\r
+ SanityManager.ASSERT(getTypeId().userType(),\r
+ "genSQLJavaSQLTree() only expected to be called on user types");\r
+ }\r
+\r
+ JavaValueNode stjvn = (JavaValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,\r
+ this,\r
+ getContextManager());\r
+\r
+ ValueNode jtsvn = (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,\r
+ stjvn,\r
+ getContextManager());\r
+ jtsvn.setType(DataTypeDescriptor.getSQLDataTypeDescriptor(stjvn.getJavaTypeName()));\r
+ return jtsvn;\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
+ return this;\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
+ /* bind() has ensured that this node's type is SQLBoolean */\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ getTypeId().isBooleanTypeId(),\r
+ "Node's type (" +\r
+ getTypeId().getSQLTypeName() +\r
+ ") is expected to be boolean");\r
+\r
+ /* Return ValueNode = false */\r
+ return genEqualsFalseTree();\r
+ }\r
+\r
+ /**\r
+ * Transform this into this = false. Useful for NOT elimination.\r
+ *\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode genEqualsFalseTree()\r
+ throws StandardException\r
+ {\r
+ BinaryRelationalOperatorNode equalsNode;\r
+ BooleanConstantNode falseNode;\r
+ boolean nullableResult;\r
+ NodeFactory nodeFactory = getNodeFactory();\r
+\r
+ falseNode = (BooleanConstantNode) nodeFactory.getNode(\r
+ C_NodeTypes.BOOLEAN_CONSTANT_NODE,\r
+ Boolean.FALSE,\r
+ getContextManager());\r
+ equalsNode = (BinaryRelationalOperatorNode)\r
+ nodeFactory.getNode(\r
+ C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+ this,\r
+ falseNode,\r
+ getContextManager());\r
+ nullableResult = getTypeServices().isNullable();\r
+ equalsNode.setType(new DataTypeDescriptor(\r
+ TypeId.BOOLEAN_ID,\r
+ nullableResult)\r
+ );\r
+ return equalsNode;\r
+ }\r
+\r
+ /**\r
+ * Transform this into this is null. Useful for NOT elimination.\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode genIsNullTree()\r
+ throws StandardException\r
+ {\r
+ IsNullNode isNullNode;\r
+\r
+ isNullNode = (IsNullNode)\r
+ getNodeFactory().getNode(\r
+ C_NodeTypes.IS_NULL_NODE,\r
+ this,\r
+ getContextManager());\r
+ isNullNode.setType(new DataTypeDescriptor(\r
+ TypeId.BOOLEAN_ID,\r
+ false)\r
+ );\r
+ return isNullNode;\r
+ }\r
+\r
+ /**\r
+ * Verify that eliminateNots() did its job correctly. Verify that\r
+ * there are no NotNodes above the top level comparison operators\r
+ * and boolean expressions.\r
+ *\r
+ * @return Boolean which reflects validity of the tree.\r
+ */\r
+ boolean verifyEliminateNots()\r
+ {\r
+ if (SanityManager.ASSERT)\r
+ {\r
+ return (! (this instanceof NotNode));\r
+ }\r
+ else\r
+ {\r
+ return true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Do the 1st step in putting an expression into conjunctive normal\r
+ * form. This step ensures that the top level of the expression is\r
+ * a chain of AndNodes.\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode putAndsOnTop() \r
+ throws StandardException\r
+ {\r
+ NodeFactory nodeFactory = getNodeFactory();\r
+\r
+ QueryTreeNode trueNode = nodeFactory.getNode(\r
+ C_NodeTypes.BOOLEAN_CONSTANT_NODE,\r
+ Boolean.TRUE,\r
+ getContextManager());\r
+ AndNode andNode = (AndNode) nodeFactory.getNode(\r
+ C_NodeTypes.AND_NODE,\r
+ this,\r
+ trueNode,\r
+ getContextManager());\r
+ andNode.postBindFixup();\r
+ return andNode;\r
+ }\r
+\r
+ /**\r
+ * Verify that putAndsOnTop() did its job correctly. Verify that the top level \r
+ * of the expression is a chain of AndNodes.\r
+ *\r
+ * @return Boolean which reflects validity of the tree.\r
+ */\r
+ public boolean verifyPutAndsOnTop()\r
+ {\r
+ return true;\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
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Verify that changeToCNF() did its job correctly. Verify that:\r
+ * o AndNode - rightOperand is not instanceof OrNode\r
+ * leftOperand is not instanceof AndNode\r
+ * o OrNode - rightOperand is not instanceof AndNode\r
+ * leftOperand is not instanceof OrNode\r
+ *\r
+ * @return Boolean which reflects validity of the tree.\r
+ */\r
+ public boolean verifyChangeToCNF()\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Categorize this predicate. Initially, this means\r
+ * building a bit map of the referenced tables for each predicate.\r
+ * If the source of this ColumnReference (at the next underlying level) \r
+ * is not a ColumnReference or a VirtualColumnNode then this predicate\r
+ * will not be pushed down.\r
+ *\r
+ * For example, in:\r
+ * select * from (select 1 from s) a (x) where x = 1\r
+ * we will not push down x = 1.\r
+ * NOTE: It would be easy to handle the case of a constant, but if the\r
+ * inner SELECT returns an arbitrary expression, then we would have to copy\r
+ * that tree into the pushed predicate, and that tree could contain\r
+ * subqueries and method calls.\r
+ * RESOLVE - revisit this issue once we have views.\r
+ *\r
+ * @param referencedTabs JBitSet with bit map of referenced FromTables\r
+ * @param simplePredsOnly Whether or not to consider method\r
+ * calls, field references and conditional nodes\r
+ * when building bit map\r
+ *\r
+ * @return boolean Whether or not source.expression is a ColumnReference\r
+ * or a VirtualColumnNode.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)\r
+ throws StandardException\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * This returns the user-supplied schema name of the column.\r
+ * At this class level, it simply returns null. But, the subclasses\r
+ * of ValueNode will overwrite this method to return the\r
+ * user-supplied schema name.\r
+ * \r
+ * When the value node is in a result column of a select list,\r
+ * the user can request metadata information. The result column\r
+ * won't have a column descriptor, so we return some default\r
+ * information through the expression. This lets expressions that\r
+ * are simply columns return all of the info, and others use\r
+ * this supertype's default values.\r
+ *\r
+ * @return the default schema name for an expression -- null\r
+ */\r
+ public String getSchemaName() throws StandardException\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This returns the user-supplied table name of the column.\r
+ * At this class level, it simply returns null. But, the subclasses\r
+ * of ValueNode will overwrite this method to return the\r
+ * user-supplied table name.\r
+ *\r
+ * When the value node is in a result column of a select list,\r
+ * the user can request metadata information. The result column\r
+ * won't have a column descriptor, so we return some default\r
+ * information through the expression. This lets expressions that\r
+ * are simply columns return all of the info, and others use\r
+ * this supertype's default values.\r
+ *\r
+ * @return the default table name for an expression -- null\r
+ */\r
+ public String getTableName()\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @return the default updatability for an expression - false\r
+ */\r
+ public boolean updatableByCursor()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * This is null so that the caller will substitute in the resultset generated\r
+ * name as needed.\r
+ *\r
+ * @return the default column name for an expression -- null.\r
+ */\r
+ public String getColumnName()\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Get a bit map of table references in this expression\r
+ *\r
+ * @return A bit map of table numbers referred to in this expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ JBitSet getTablesReferenced()\r
+ throws StandardException\r
+ {\r
+ ReferencedTablesVisitor rtv = new ReferencedTablesVisitor(new JBitSet(0));\r
+ accept(rtv);\r
+ return rtv.getTableMap();\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this expression tree is cloneable.\r
+ *\r
+ * @return boolean Whether or not this expression tree is cloneable.\r
+ */\r
+ public boolean isCloneable()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Return a clone of this node.\r
+ *\r
+ * @return ValueNode A clone of this node.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode getClone() throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(false,\r
+ "getClone() not expected to be called for " +\r
+ getClass().getName());\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Copy all of the "appropriate fields" for a shallow copy.\r
+ *\r
+ * @param oldVN The ValueNode to copy from.\r
+ *\r
+ */\r
+ public void copyFields(ValueNode oldVN) throws StandardException\r
+ {\r
+ dataTypeServices = oldVN.getTypeServices();\r
+ }\r
+\r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @return ValueNode The remapped expression tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode remapColumnReferencesToExpressions() throws StandardException\r
+ {\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this expression tree represents a constant expression.\r
+ *\r
+ * @return Whether or not this expression tree represents a constant expression.\r
+ */\r
+ public boolean isConstantExpression()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this expression tree represents a constant value.\r
+ * In this case, "constant" means that it will always evaluate to the\r
+ * same thing, even if it includes columns. A column is constant if it\r
+ * is compared to a constant expression.\r
+ *\r
+ * @return True means this expression tree represents a constant value.\r
+ */\r
+ public boolean constantExpression(PredicateList whereClause)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Bind time logic. Raises an error if this ValueNode, once compiled, returns\r
+ * unstable results AND if we're in a context where unstable results are\r
+ * forbidden.\r
+ *\r
+ * Called by children who may NOT appear in the WHERE subclauses of ADD TABLE clauses.\r
+ *\r
+ * @param fragmentType Type of fragment as a String, for inclusion in error messages.\r
+ * @param fragmentBitMask Type of fragment as a bitmask of possible fragment types\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void checkReliability( String fragmentType, int fragmentBitMask )\r
+ throws StandardException\r
+ {\r
+ // if we're in a context that forbids unreliable fragments, raise an error\r
+ if ( ( getCompilerContext().getReliability() & fragmentBitMask ) != 0 )\r
+ {\r
+ throwReliabilityException( fragmentType );\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind time logic. Raises an error if this ValueNode, once compiled, returns\r
+ * unstable results AND if we're in a context where unstable results are\r
+ * forbidden.\r
+ *\r
+ * Called by children who may NOT appear in the WHERE subclauses of ADD TABLE clauses.\r
+ *\r
+ * @param fragmentBitMask Type of fragment as a bitmask of possible fragment types\r
+ * @param fragmentType Type of fragment as a String, to be fetch for the error message.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void checkReliability( int fragmentBitMask, String fragmentType )\r
+ throws StandardException\r
+ {\r
+ // if we're in a context that forbids unreliable fragments, raise an error\r
+ if ( ( getCompilerContext().getReliability() & fragmentBitMask ) != 0 )\r
+ {\r
+ String fragmentTypeTxt = MessageService.getTextMessage( fragmentType );\r
+ throwReliabilityException( fragmentTypeTxt );\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Common code for the 2 checkReliability functions. Always throws StandardException.\r
+ *\r
+ * @param fragmentType Type of fragment as a string, for inclusion in error messages.\r
+ * @exception StandardException Throws an error, always.\r
+ */\r
+ private void throwReliabilityException( String fragmentType ) throws StandardException\r
+ {\r
+ String sqlState;\r
+ /* Error string somewhat dependent on operation due to different\r
+ * nodes being allowed for different operations.\r
+ */\r
+ if (getCompilerContext().getReliability() == CompilerContext.DEFAULT_RESTRICTION)\r
+ {\r
+ sqlState = SQLState.LANG_INVALID_DEFAULT_DEFINITION;\r
+ }\r
+ else\r
+ {\r
+ sqlState = SQLState.LANG_UNRELIABLE_QUERY_FRAGMENT;\r
+ }\r
+ throw StandardException.newException(sqlState, fragmentType);\r
+ }\r
+\r
+ /**\r
+ * Return the variant type for the underlying expression.\r
+ * The variant type can be:\r
+ * VARIANT - variant within a scan\r
+ * (method calls and non-static field access)\r
+ * SCAN_INVARIANT - invariant within a scan\r
+ * (column references from outer tables)\r
+ * QUERY_INVARIANT - invariant within the life of a query\r
+ * (constant expressions)\r
+ *\r
+ * @return The variant type for the underlying expression.\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected int getOrderableVariantType() throws StandardException\r
+ {\r
+ // The default is VARIANT\r
+ return Qualifier.VARIANT;\r
+ }\r
+\r
+\r
+ /**\r
+ * Bind time logic. Raises an error if this ValueNode does not resolve to\r
+ * a boolean value. This method is called by WHERE clauses.\r
+ *\r
+ * @return bound coercion of this node to a builtin type as necessary\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode checkIsBoolean()\r
+ throws StandardException\r
+ {\r
+ ValueNode whereClause = this;\r
+\r
+ /*\r
+ ** Is the datatype of the WHERE clause BOOLEAN?\r
+ **\r
+ ** NOTE: This test is not necessary in SQL92 entry level, because\r
+ ** it is syntactically impossible to have a non-Boolean WHERE clause\r
+ ** in that level of the standard. But we intend to extend the\r
+ ** language to allow Boolean user functions in the WHERE clause,\r
+ ** so we need to test for the error condition.\r
+ */\r
+ TypeId whereTypeId = whereClause.getTypeId();\r
+\r
+ /* If the where clause is not a built-in type, then generate a bound \r
+ * conversion tree to a built-in type.\r
+ */\r
+ if (whereTypeId.userType())\r
+ {\r
+ whereClause = whereClause.genSQLJavaSQLTree();\r
+ whereTypeId = whereClause.getTypeId();\r
+ }\r
+\r
+ if (! whereTypeId.equals(TypeId.BOOLEAN_ID))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE, \r
+ whereTypeId.getSQLTypeName()\r
+ );\r
+ }\r
+\r
+ return whereClause;\r
+ }\r
+\r
+ /**\r
+ * Return an Object representing the bind time value of this\r
+ * expression tree. If the expression tree does not evaluate to\r
+ * a constant at bind time then we return null.\r
+ * This is useful for bind time resolution of VTIs.\r
+ * RESOLVE: What do we do for primitives?\r
+ *\r
+ * @return An Object representing the bind time value of this expression tree.\r
+ * (null if not a bind time constant.)\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ Object getConstantValueAsObject()\r
+ throws StandardException\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////\r
+ //\r
+ // The ValueNode defers its generate() work to a method that works on\r
+ // ExpressionClassBuilders rather than ActivationClassBuilders. This\r
+ // is so that expression generation can be shared by the Core compiler\r
+ // AND the Replication Filter compiler.\r
+ //\r
+ /////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+ /**\r
+ * Do the code generation for this node. Call the more general\r
+ * routine that generates expressions.\r
+ *\r
+ * @param acb The ActivationClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ protected final void generate(ActivationClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ generateExpression( acb, mb );\r
+ }\r
+\r
+ /**\r
+ * The only reason this routine exists is so that I don't have to change\r
+ * the protection on generateExpression() and rototill all of QueryTree.\r
+ *\r
+ * @param ecb The ExpressionClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generateFilter(ExpressionClassBuilder ecb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ generateExpression( ecb, mb );\r
+ }\r
+ \r
+\r
+ /**\r
+ * The default selectivity for value nodes is 50%. This is overridden\r
+ * in specific cases, such as the RelationalOperators.\r
+ */\r
+ public double selectivity(Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ // Return 1 if additional predicates have been generated from this one.\r
+ if (transformed)\r
+ {\r
+ return 1.0;\r
+ }\r
+ else\r
+ {\r
+ return 0.5d;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Update the array of columns in = conditions with expressions without\r
+ * column references from the same table. This is useful when doing\r
+ * subquery flattening on the basis of an equality condition.\r
+ * eqOuterCols or tableColMap may be null if the calling routine\r
+ * doesn't need the information provided\r
+ *\r
+ * @param tableNumber The tableNumber of the table from which\r
+ * the columns of interest come from.\r
+ * @param eqOuterCols Array of booleans for noting which columns\r
+ * are in = predicates without columns from the\r
+ * subquery block. May be null.\r
+ * @param tableNumbers Array of table numbers in this query block.\r
+ * @param tableColMap Array of bits for noting which columns\r
+ * are in = predicates for each table in the\r
+ * query block. May be null.\r
+ * @param resultColTable True if tableNumber is the table containing result\r
+ * columns\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ * \r
+ */\r
+ void checkTopPredicatesForEqualsConditions(\r
+ int tableNumber, boolean[] eqOuterCols, int[] tableNumbers, \r
+ JBitSet[] tableColMap, boolean resultColTable)\r
+ throws StandardException\r
+ {\r
+ for (ValueNode whereWalker = this; whereWalker instanceof AndNode; \r
+ whereWalker = ((AndNode) whereWalker).getRightOperand())\r
+ {\r
+ // See if this is a candidate =\r
+ AndNode and = (AndNode) whereWalker;\r
+\r
+ if (!and.getLeftOperand().isRelationalOperator() ||\r
+ !(((RelationalOperator)(and.getLeftOperand())).getOperator() == RelationalOperator.EQUALS_RELOP))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ BinaryRelationalOperatorNode beon =\r
+ (BinaryRelationalOperatorNode) and.getLeftOperand();\r
+ ValueNode left = beon.getLeftOperand();\r
+ ValueNode right = beon.getRightOperand();\r
+ int resultTable = 0;\r
+ if (resultColTable)\r
+ {\r
+ for ( ; resultTable < tableNumbers.length; resultTable++)\r
+ {\r
+ if (tableNumbers[resultTable] == tableNumber)\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ resultTable = -1;\r
+\r
+ /* Is this = of the right form? */\r
+ if ((left instanceof ColumnReference) &&\r
+ ((ColumnReference) left).getTableNumber() == tableNumber)\r
+ {\r
+ updateMaps(tableColMap, eqOuterCols, tableNumbers, tableNumber,\r
+ resultTable, right, left);\r
+ }\r
+ else if ((right instanceof ColumnReference) &&\r
+ ((ColumnReference) right).getTableNumber() == tableNumber)\r
+ {\r
+ updateMaps(tableColMap, eqOuterCols, tableNumbers, tableNumber,\r
+ resultTable, left, right);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Does this represent a true constant.\r
+ *\r
+ * @return Whether or not this node represents a true constant.\r
+ */\r
+ boolean isBooleanTrue()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Does this represent a false constant.\r
+ *\r
+ * @return Whether or not this node represents a false constant.\r
+ */\r
+ boolean isBooleanFalse()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Generate code for this calculation. This is a place-holder method -\r
+ * it should not be called.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(false, "Code generation for this type of ValueNode is unimplemented");\r
+ }\r
+\r
+ /**\r
+ * Set the correct bits in tableColMap and set the boolean value in eqOuterCols \r
+ * given two arguments to an = predicate\r
+ * tableColMap[t] - bit is set if the column is in an = predicate with a column \r
+ * in table t, or a bit is set if the column is in an \r
+ * = predicate with a constant,parameter or correlation variable \r
+ * (for all table t, if this tableColMap is not for the\r
+ * table with the result columns)\r
+ * eqOuterCols[c] - is true if the column is in an = predicate with a constant,\r
+ * parameter or correlation variable\r
+ * \r
+ *\r
+ * @param tableColMap Array of bitmaps for noting which columns are in = \r
+ * predicates with columns from each table\r
+ * @param eqOuterCols Array of booleans for noting which columns\r
+ * are in = predicates without columns from the\r
+ * subquery block.\r
+ * @param tableNumber table number for which we are setting up the Maps\r
+ * @param resultTable -1 if this table is not the result table; otherwise\r
+ * the index into tableNumbers for the result table\r
+ * @param arg1 one side of the = predicate\r
+ * @param arg2 other side of the = predicate\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void updateMaps(JBitSet[] tableColMap, boolean[] eqOuterCols,\r
+ int[] tableNumbers, int tableNumber, int resultTable,\r
+ ValueNode arg1, ValueNode arg2)\r
+ throws StandardException\r
+ {\r
+ /* arg2 is a column from our table. This\r
+ * is a good = for both All tables and Outer arrays\r
+ * if the right side is a constant or a parameter\r
+ * or a column from an outer table.\r
+ * It is a good = for only the All array if\r
+ * the right side is a column from this query block.\r
+ */\r
+ if ((arg1 instanceof ConstantNode) || (arg1.requiresTypeFromContext()))\r
+ {\r
+ setValueCols(tableColMap, eqOuterCols,\r
+ ((ColumnReference) arg2).getColumnNumber(), resultTable);\r
+ }\r
+ else if((arg1 instanceof ColumnReference &&\r
+ ((ColumnReference) arg1).getTableNumber() != tableNumber))\r
+ {\r
+ /* See if other columns is a correlation column */\r
+ int otherTN = ((ColumnReference) arg1).getTableNumber();\r
+ int index = 0;\r
+ int colNumber = ((ColumnReference) arg2).getColumnNumber();\r
+\r
+ for ( ; index < tableNumbers.length; index++)\r
+ {\r
+ if (otherTN == tableNumbers[index])\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ /* Correlation column, so we can treat it as a constant */\r
+ if (index == tableNumbers.length)\r
+ {\r
+ setValueCols(tableColMap, eqOuterCols, colNumber, resultTable);\r
+ }\r
+ else if (tableColMap != null)\r
+ {\r
+ tableColMap[index].set(colNumber);\r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+ /* See if other side contains a column reference from the same table */\r
+ JBitSet referencedTables = arg1.getTablesReferenced();\r
+ /* See if other columns are all correlation columns */\r
+ int index = 0;\r
+ int colNumber = ((ColumnReference) arg2).getColumnNumber();\r
+ for ( ; index < tableNumbers.length; index++)\r
+ {\r
+ if (referencedTables.get(tableNumbers[index]))\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ /* Correlation column, so we can treat it as a constant */\r
+ if (index == tableNumbers.length)\r
+ {\r
+ setValueCols(tableColMap, eqOuterCols, colNumber, resultTable);\r
+ }\r
+ else if (tableColMap != null && !referencedTables.get(tableNumber))\r
+ {\r
+ tableColMap[index].set(colNumber);\r
+ }\r
+ }\r
+ }\r
+ /**\r
+ * Set eqOuterCols and the column in all the tables for constants,\r
+ * parmeters and correlation columns\r
+ * The column in the tableColMap is set only for the current table\r
+ * if the table is the result column table. For other tables in the\r
+ * query we set the column for all the tables since the constant will\r
+ * reduced the number of columns required in a unique multicolumn index for\r
+ * distinctness.\r
+ * For example, given an unique index on t1(a,b), setting b=1 means that\r
+ * t1(a) is unique since there can be no duplicates for a where b=1 without\r
+ * destroying the uniqueness of t1(a,b). However, for the result columns\r
+ * setting b=1, does not mean that a select list of t1.a is distinct if\r
+ * t1.a is the only column used in joining with another table\r
+ * e.g. select t1.a from t1, t2 where t1.a = t2.a and t1.b = 1;\r
+ * \r
+ * t1 t2 result\r
+ * a b a a\r
+ * 1 1 1 1\r
+ * 1 2 2 1\r
+ * 2 1\r
+ * \r
+ *\r
+ * @param tableColMap Array of bitmaps for noting which columns are in = \r
+ * predicates with columns from each table\r
+ * @param eqOuterCols Array of booleans for noting which columns\r
+ * are in = predicates without columns from the\r
+ * subquery block.\r
+ * @param colReference The column to set\r
+ * @param resultTable If -1 set all the bit for all the tables for that\r
+ * column; otherwise set the bit for the specified table\r
+ *\r
+ *\r
+ */\r
+ private void setValueCols(JBitSet[] tableColMap, boolean[] eqOuterCols, \r
+ int colReference, int resultTable)\r
+ {\r
+ if (eqOuterCols != null)\r
+ eqOuterCols[colReference] = true;\r
+\r
+ if (tableColMap != null)\r
+ {\r
+ if (resultTable == -1)\r
+ {\r
+ for (int i = 0; i < tableColMap.length; i++)\r
+ tableColMap[i].set(colReference);\r
+ }\r
+ else\r
+ tableColMap[resultTable].set(colReference);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns true if this ValueNode is a relational operator. Relational\r
+ * Operators are <, <=, =, >, >=, <> as well as IS NULL and IS NOT\r
+ * NULL. This is the preferred way of figuring out if a ValueNode is\r
+ * relational or not. \r
+ * @see RelationalOperator\r
+ * @see BinaryRelationalOperatorNode\r
+ * @see IsNullNode\r
+ */\r
+ public boolean isRelationalOperator()\r
+ {\r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * Returns true if this value node is a <em>equals</em> operator. \r
+ *\r
+ * @see ValueNode#isRelationalOperator\r
+ */\r
+ public boolean isBinaryEqualsOperatorNode()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns true if this value node is an operator created\r
+ * for optimized performance of an IN list.\r
+ *\r
+ * Or more specifically, returns true if this value node is\r
+ * an equals operator of the form "col = ?" that we generated\r
+ * during preprocessing to allow index multi-probing.\r
+ */\r
+ public boolean isInListProbeNode()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /** Return true if the predicate represents an optimizable equality node.\r
+ * an expression is considered to be an optimizable equality node if all the\r
+ * following conditions are met:\r
+ * <ol>\r
+ * <li> the operator is an <em>=</em> or <em>IS NULL</em> operator </li>\r
+ * <li> one of the operands is a column specified by optTable/columnNumber</li>\r
+ * <li> Both operands are not the same column; i.e tab.col = tab.col </li>\r
+ * <li> There are no implicit varchar comparisons of the operands; i.e\r
+ * either both operands are string like (varchar, char, longvarchar) or\r
+ * neither operand is string like </li>\r
+ * </ol>\r
+ * \r
+ * @param optTable the table being optimized. Column reference must be from\r
+ * this table.\r
+ * @param columnNumber the column number. One of the operands of this\r
+ * predicate must be the column number specified by optTable/columnNumber\r
+ * @param isNullOkay if set to true we also consider IS NULL predicates;\r
+ * otherwise consider only = predicates.\r
+ */\r
+ public boolean optimizableEqualityNode(Optimizable optTable, \r
+ int columnNumber, \r
+ boolean isNullOkay)\r
+ throws StandardException\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns TRUE if the type of this node will be determined from the\r
+ * context in which it is getting used. If true is returned then\r
+ * after bindExpression() is called on the node, its type\r
+ * must be set (from the relevant context) using setType().\r
+ *\r
+ * @return Whether this node's type will be determined from the context\r
+ */\r
+ public boolean requiresTypeFromContext()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns TRUE if this is a parameter node. We do lots of special things\r
+ * with Parameter Nodes.\r
+ *\r
+ */\r
+ public boolean isParameterNode()\r
+ {\r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * Tests if this node is equivalent to the specified ValueNode. Two \r
+ * ValueNodes are considered equivalent if they will evaluate to the same\r
+ * value during query execution. \r
+ * <p> \r
+ * This method provides basic expression matching facility for the derived \r
+ * class of ValueNode and it is used by the language layer to compare the \r
+ * node structural form of the two expressions for equivalence at bind \r
+ * phase. \r
+ * <p>\r
+ * Note that it is not comparing the actual row values at runtime to produce \r
+ * a result; hence, when comparing SQL NULLs, they are considered to be \r
+ * equivalent and not unknown. \r
+ * <p>\r
+ * One usage case of this method in this context is to compare the select \r
+ * column expression against the group by expression to check if they are \r
+ * equivalent. e.g.:\r
+ * <p>\r
+ * SELECT c1+c2 FROM t1 GROUP BY c1+c2 \r
+ * <p>\r
+ * In general, node equivalence is determined by the derived class of \r
+ * ValueNode. But they generally abide to the rules below:\r
+ * <ul>\r
+ * <li>The two ValueNodes must be of the same node type to be considered \r
+ * equivalent. e.g.: CastNode vs. CastNode - equivalent (if their args \r
+ * also match), ColumnReference vs CastNode - not equivalent.\r
+ * \r
+ * <li>If node P contains other ValueNode(s) and so on, those node(s) must \r
+ * also be of the same node type to be considered equivalent.\r
+ * \r
+ * <li>If node P takes a parameter list, then the number of arguments and its \r
+ * arguments for the two nodes must also match to be considered \r
+ * equivalent. e.g.: CAST(c1 as INTEGER) vs CAST(c1 as SMALLINT), they \r
+ * are not equivalent.\r
+ * \r
+ * <li>When comparing SQL NULLs in this context, they are considered to be \r
+ * equivalent.\r
+ * \r
+ * <li>If this does not apply or it is determined that the two nodes are not \r
+ * equivalent then the derived class of this method should return false; \r
+ * otherwise, return true.\r
+ * </ul> \r
+ * \r
+ * @param other the node to compare this ValueNode against.\r
+ * @return <code>true</code> if the two nodes are equivalent, \r
+ * <code>false</code> otherwise.\r
+ * \r
+ * @throws StandardException \r
+ */\r
+ protected abstract boolean isEquivalent(ValueNode other)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Tests if this node is of the same type as the specified node as\r
+ * reported by {@link QueryTreeNode#getNodeType()}.\r
+ * \r
+ * @param other the node to compare this value node against. \r
+ * \r
+ * @return <code>true</code> if the two nodes are of the same type. \r
+ */\r
+ protected final boolean isSameNodeType(ValueNode other)\r
+ {\r
+ if (other != null) {\r
+ return other.getNodeType() == getNodeType();\r
+ }\r
+ return false;\r
+ }\r
+ \r
+}\r