--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.ColumnReference\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.compile.NodeFactory;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A ColumnReference represents a column in the query tree. The parser generates a\r
+ * ColumnReference for each column reference. A column refercence could be a column in\r
+ * a base table, a column in a view (which could expand into a complex\r
+ * expression), or a column in a subquery in the FROM clause.\r
+ *\r
+ */\r
+\r
+public class ColumnReference extends ValueNode\r
+{\r
+ String columnName;\r
+\r
+ /*\r
+ ** This is the user-specified table name. It will be null if the\r
+ ** user specifies a column without a table name. Leave it null even\r
+ ** when the column is bound as it is only used in binding.\r
+ */\r
+ TableName tableName;\r
+ /* The table this column reference is bound to */\r
+ private int tableNumber; \r
+ /* The column number in the underlying base table */\r
+ private int columnNumber; \r
+ /* This is where the value for this column reference will be coming from */\r
+ private ResultColumn source;\r
+\r
+ /* For unRemapping */\r
+ ResultColumn origSource;\r
+ private String origName;\r
+ int origTableNumber = -1;\r
+ int origColumnNumber = -1;\r
+\r
+ /* Reuse generated code where possible */\r
+ //Expression genResult;\r
+\r
+ private boolean replacesAggregate;\r
+\r
+ private int nestingLevel = -1;\r
+ private int sourceLevel = -1;\r
+\r
+ /* Whether or not this column reference been scoped for the\r
+ sake of predicate pushdown.\r
+ */\r
+ private boolean scoped;\r
+\r
+ /* List of saved remap data if this ColumnReference is scoped\r
+ and has been remapped multiple times.\r
+ */\r
+ private java.util.ArrayList remaps;\r
+\r
+ /**\r
+ * Initializer.\r
+ * This one is called by the parser where we could\r
+ * be dealing with delimited identifiers.\r
+ *\r
+ * @param columnName The name of the column being referenced\r
+ * @param tableName The qualification for the column\r
+ * @param tokBeginOffset begin position of token for the column name \r
+ * identifier from parser.\r
+ * @param tokEndOffset end position of token for the column name \r
+ * identifier from parser.\r
+ */\r
+\r
+ public void init(Object columnName, \r
+ Object tableName,\r
+ Object tokBeginOffset,\r
+ Object tokEndOffset\r
+ )\r
+ {\r
+ this.columnName = (String) columnName;\r
+ this.tableName = (TableName) tableName;\r
+ this.setBeginOffset(((Integer) tokBeginOffset).intValue());\r
+ this.setEndOffset(((Integer) tokEndOffset).intValue());\r
+ tableNumber = -1;\r
+ remaps = null;\r
+ }\r
+\r
+ /**\r
+ * Initializer.\r
+ *\r
+ * @param columnName The name of the column being referenced\r
+ * @param tableName The qualification for the column\r
+ */\r
+\r
+ public void init(Object columnName, Object tableName)\r
+ {\r
+ this.columnName = (String) columnName;\r
+ this.tableName = (TableName) tableName;\r
+ tableNumber = -1;\r
+ remaps = null;\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 "columnName: " + columnName + "\n" +\r
+ "tableNumber: " + tableNumber + "\n" +\r
+ "columnNumber: " + columnNumber + "\n" +\r
+ "replacesAggregate: " + replacesAggregate + "\n" +\r
+ "tableName: " + ( ( tableName != null) ? tableName.toString() : "null") + "\n" +\r
+ "nestingLevel: " + nestingLevel + "\n" +\r
+ "sourceLevel: " + sourceLevel + "\n" +\r
+ super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Prints the sub-nodes of this object. See QueryTreeNode.java for\r
+ * how tree printing is supposed to work.\r
+ *\r
+ * @param depth The depth of this node in the tree\r
+ */\r
+\r
+ public void printSubNodes(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ super.printSubNodes(depth);\r
+\r
+ if (source != null)\r
+ {\r
+ printLabel(depth, "source: ");\r
+ source.treePrint(depth + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this CR is correlated.\r
+ *\r
+ * @return Whether or not this CR is correlated.\r
+ */\r
+ boolean getCorrelated()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(nestingLevel != -1,\r
+ "nestingLevel on "+columnName+" is not expected to be -1");\r
+ SanityManager.ASSERT(sourceLevel != -1,\r
+ "sourceLevel on "+columnName+" is not expected to be -1");\r
+ }\r
+ return sourceLevel != nestingLevel;\r
+ }\r
+\r
+ /**\r
+ * Set the nesting level for this CR. (The nesting level\r
+ * at which the CR appears.)\r
+ *\r
+ * @param nestingLevel The Nesting level at which the CR appears.\r
+ */\r
+ void setNestingLevel(int nestingLevel)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(nestingLevel != -1,\r
+ "nestingLevel is not expected to be -1");\r
+ }\r
+ this.nestingLevel = nestingLevel;\r
+ }\r
+\r
+ /**\r
+ * Get the nesting level for this CR.\r
+ *\r
+ * @return The nesting level for this CR.\r
+ */\r
+ private int getNestingLevel()\r
+ {\r
+ return nestingLevel;\r
+ }\r
+\r
+ /**\r
+ * Set the source level for this CR. (The nesting level\r
+ * of the source of the CR.)\r
+ *\r
+ * @param sourceLevel The Nesting level of the source of the CR.\r
+ */\r
+ void setSourceLevel(int sourceLevel)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(sourceLevel != -1,\r
+ "sourceLevel is not expected to be -1");\r
+ }\r
+ this.sourceLevel = sourceLevel;\r
+ }\r
+\r
+ /**\r
+ * Get the source level for this CR.\r
+ *\r
+ * @return The source level for this CR.\r
+ */\r
+ int getSourceLevel()\r
+ {\r
+ return sourceLevel;\r
+ }\r
+\r
+ /**\r
+ * Mark this node as being generated to replace an aggregate.\r
+ * (Useful for replacing aggregates in the HAVING clause with \r
+ * column references to the matching aggregate in the \r
+ * user's SELECT.\r
+ */\r
+ public void markGeneratedToReplaceAggregate()\r
+ {\r
+ replacesAggregate = true;\r
+ }\r
+\r
+ /**\r
+ * Determine whether or not this node was generated to\r
+ * replace an aggregate in the user's SELECT.\r
+ *\r
+ * @return boolean Whether or not this node was generated to replace\r
+ * an aggregate in the user's SELECT.\r
+ */\r
+ public boolean getGeneratedToReplaceAggregate()\r
+ {\r
+ return replacesAggregate;\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()\r
+ throws StandardException\r
+ {\r
+ ColumnReference newCR = (ColumnReference) getNodeFactory().getNode(\r
+ C_NodeTypes.COLUMN_REFERENCE,\r
+ columnName,\r
+ tableName,\r
+ getContextManager());\r
+\r
+ newCR.copyFields(this);\r
+ return newCR;\r
+ }\r
+\r
+ /**\r
+ * Copy all of the "appropriate fields" for a shallow copy.\r
+ *\r
+ * @param oldCR The ColumnReference to copy from.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void copyFields(ColumnReference oldCR)\r
+ throws StandardException\r
+ {\r
+ super.copyFields(oldCR);\r
+\r
+ tableName = oldCR.getTableNameNode();\r
+ tableNumber = oldCR.getTableNumber();\r
+ columnNumber = oldCR.getColumnNumber();\r
+ source = oldCR.getSource();\r
+ nestingLevel = oldCR.getNestingLevel();\r
+ sourceLevel = oldCR.getSourceLevel();\r
+ replacesAggregate = oldCR.getGeneratedToReplaceAggregate();\r
+ scoped = oldCR.isScoped();\r
+ }\r
+\r
+ /**\r
+ * Bind this expression. This means binding the sub-expressions,\r
+ * as well as figuring out what the return type is for this expression.\r
+ *\r
+ * NOTE: We must explicitly check for a null FromList here, column reference\r
+ * without a FROM list, as the grammar allows the following:\r
+ * insert into t1 values(c1)\r
+ *\r
+ * @param fromList The FROM list for the query this\r
+ * expression is in, for binding columns.\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(FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector) \r
+ throws StandardException\r
+ {\r
+ ResultColumn matchingRC;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(fromList != null, "fromList is expected to be non-null");\r
+ }\r
+\r
+ if (fromList.size() == 0)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_ILLEGAL_COLUMN_REFERENCE, columnName);\r
+ }\r
+\r
+ matchingRC = fromList.bindColumnReference(this);\r
+\r
+ /* Error if no match found in fromList */\r
+ if (matchingRC == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, getSQLColumnName());\r
+ }\r
+\r
+ /* Set the columnNumber from the base table.\r
+ * Useful for optimizer and generation.\r
+ */\r
+ columnNumber = matchingRC.getColumnPosition();\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Get the column name for purposes of error\r
+ * messages or debugging. This returns the column\r
+ * name as used in the SQL statement. Thus if it was qualified\r
+ * with a table, alias name that will be included.\r
+ *\r
+ * @return The column name in the form [[schema.]table.]column\r
+ */\r
+\r
+ public String getSQLColumnName()\r
+ {\r
+ if (tableName == null)\r
+ return columnName;\r
+ \r
+ return tableName.toString() + "." + columnName;\r
+ }\r
+\r
+ /**\r
+ * Get the name of this column\r
+ *\r
+ * @return The name of this column\r
+ */\r
+\r
+ public String getColumnName()\r
+ {\r
+ return columnName;\r
+ }\r
+\r
+ /**\r
+ * Set the name of this column\r
+ *\r
+ * @param columnName The name of this column\r
+ */\r
+\r
+ public void setColumnName(String columnName)\r
+ {\r
+ this.columnName = columnName;\r
+ }\r
+\r
+ /**\r
+ * Get the table number for this ColumnReference.\r
+ *\r
+ * @return int The table number for this ColumnReference\r
+ */\r
+\r
+ public int getTableNumber()\r
+ {\r
+ return tableNumber;\r
+ }\r
+\r
+ /**\r
+ * Set this ColumnReference to refer to the given table number.\r
+ *\r
+ * @param tableNumber The table number this ColumnReference will refer to\r
+ */\r
+\r
+ public void setTableNumber(int tableNumber)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(tableNumber != -1,\r
+ "tableNumber not expected to be -1");\r
+ }\r
+ this.tableNumber = tableNumber;\r
+ }\r
+\r
+ /**\r
+ * Get the user-supplied table name of this column. This will be null\r
+ * if the user did not supply a name (for example, select a from t).\r
+ * The method will return B for this example, select b.a from t as b\r
+ * The method will return T for this example, select t.a from t\r
+ *\r
+ * @return The user-supplied name of this column. Null if no user-\r
+ * supplied name.\r
+ */\r
+\r
+ public String getTableName()\r
+ {\r
+ return ( ( tableName != null) ? tableName.getTableName() : null );\r
+ }\r
+\r
+ /**\r
+ * Get the name of the underlying(base) table this column comes from, if any.\r
+ * Following example queries will all return T\r
+ * select a from t\r
+ * select b.a from t as b\r
+ * select t.a from t\r
+ *\r
+ * @return The name of the base table that this column comes from.\r
+ * Null if not a ColumnReference.\r
+ */\r
+\r
+ public String getSourceTableName()\r
+ {\r
+ return ((source != null) ? source.getTableName() : null);\r
+ }\r
+\r
+ /**\r
+ * Get the name of the schema for the Column's base table, if any.\r
+ * Following example queries will all return APP (assuming user is in schema APP)\r
+ * select t.a from t\r
+ * select b.a from t as b\r
+ * select app.t.a from t\r
+ *\r
+ * @return The name of the schema for Column's base table. If the column\r
+ * is not in a schema (i.e. is a derived column), it returns NULL.\r
+ */\r
+ public String getSourceSchemaName() throws StandardException\r
+ {\r
+ return ((source != null) ? source.getSchemaName() : null);\r
+ }\r
+\r
+ /**\r
+ * Is the column wirtable by the cursor or not. (ie, is it in the list of FOR UPDATE columns list)\r
+ *\r
+ * @return TRUE, if the column is a base column of a table and is \r
+ * writable by cursor.\r
+ */\r
+ public boolean updatableByCursor()\r
+ {\r
+ return ((source != null) ? source.updatableByCursor() : false);\r
+ }\r
+\r
+ /**\r
+ Return the table name as the node it is.\r
+ @return the column's table name.\r
+ */\r
+ public TableName getTableNameNode()\r
+ {\r
+ return tableName;\r
+ }\r
+\r
+ public void setTableNameNode(TableName tableName)\r
+ {\r
+ this.tableName = tableName;\r
+ }\r
+\r
+ /**\r
+ * Get the column number for this ColumnReference.\r
+ *\r
+ * @return int The column number for this ColumnReference\r
+ */\r
+\r
+ public int getColumnNumber()\r
+ {\r
+ return columnNumber;\r
+ }\r
+\r
+ /**\r
+ * Set the column number for this ColumnReference. This is\r
+ * used when scoping predicates for pushdown.\r
+ *\r
+ * @param colNum The new column number.\r
+ */\r
+\r
+ public void setColumnNumber(int colNum)\r
+ {\r
+ this.columnNumber = colNum;\r
+ }\r
+\r
+ /**\r
+ * Get the source this columnReference\r
+ *\r
+ * @return The source of this columnReference\r
+ */\r
+\r
+ public ResultColumn getSource()\r
+ {\r
+ return source;\r
+ }\r
+\r
+ /**\r
+ * Set the source this columnReference\r
+ *\r
+ * @param source The source of this columnReference\r
+ */\r
+\r
+ public void setSource(ResultColumn source)\r
+ {\r
+ this.source = source;\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
+ BinaryComparisonOperatorNode equalsNode;\r
+ BooleanConstantNode trueNode;\r
+ NodeFactory nodeFactory = getNodeFactory();\r
+ ValueNode andNode;\r
+\r
+ trueNode = (BooleanConstantNode) nodeFactory.getNode(\r
+ C_NodeTypes.BOOLEAN_CONSTANT_NODE,\r
+ Boolean.TRUE,\r
+ getContextManager());\r
+ equalsNode = (BinaryComparisonOperatorNode) \r
+ nodeFactory.getNode(\r
+ C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+ this,\r
+ trueNode,\r
+ getContextManager());\r
+ /* Set type info for the operator node */\r
+ equalsNode.bindComparisonOperator();\r
+ andNode = (ValueNode) nodeFactory.getNode(\r
+ C_NodeTypes.AND_NODE,\r
+ equalsNode,\r
+ trueNode,\r
+ getContextManager());\r
+ ((AndNode) andNode).postBindFixup();\r
+ return andNode;\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
+ *\r
+ * Also, don't allow a predicate to be pushed down if it contains a\r
+ * ColumnReference that replaces an aggregate. This can happen if\r
+ * the aggregate is in the HAVING clause. In this case, we would be\r
+ * pushing the predicate into the SelectNode that evaluates the aggregate,\r
+ * which doesn't make sense, since the having clause is supposed to be\r
+ * applied to the result of the SelectNode.\r
+ *\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 or a ConstantNode.\r
+ */\r
+ public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(tableNumber >= 0,\r
+ "tableNumber is expected to be non-negative");\r
+ referencedTabs.set(tableNumber);\r
+\r
+ return ( ! replacesAggregate ) &&\r
+ ( (source.getExpression() instanceof ColumnReference) ||\r
+ (source.getExpression() instanceof VirtualColumnNode) ||\r
+ (source.getExpression() instanceof ConstantNode));\r
+ }\r
+\r
+ /**\r
+ * Remap all of the ColumnReferences in this expression tree\r
+ * to point to the ResultColumn that is 1 level under their\r
+ * current source ResultColumn.\r
+ * This is useful for pushing down single table predicates.\r
+ *\r
+ * RESOLVE: Once we start pushing join clauses, we will need to walk the\r
+ * ResultColumn/VirtualColumnNode chain for them to remap the references.\r
+ */\r
+ public void remapColumnReferences()\r
+ {\r
+ ValueNode expression = source.getExpression();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // SanityManager.ASSERT(origSource == null,\r
+ // "Trying to remap ColumnReference twice without unremapping it.");\r
+ }\r
+\r
+ if ( ! ( (expression instanceof VirtualColumnNode) ||\r
+ (expression instanceof ColumnReference) )\r
+ )\r
+ {\r
+ return;\r
+ }\r
+\r
+ /* Scoped column references are a special case: they can be\r
+ * remapped several times (once for every ProjectRestrictNode\r
+ * through which the scoped ColumnReference is pushed before\r
+ * reaching its target result set) and will be un-remapped\r
+ * several times, as well (as the scoped predicate is "pulled"\r
+ * back up the query tree to it's original location). So we\r
+ * have to keep track of the "orig" info for every remap\r
+ * operation, not just for the most recent one.\r
+ */\r
+ if (scoped && (origSource != null))\r
+ {\r
+ if (remaps == null)\r
+ remaps = new java.util.ArrayList();\r
+ remaps.add(new RemapInfo(\r
+ columnNumber, tableNumber, columnName, source));\r
+ }\r
+ else\r
+ {\r
+ origSource = source;\r
+ origName = columnName;\r
+ origColumnNumber = columnNumber;\r
+ origTableNumber = tableNumber;\r
+ }\r
+\r
+ /* Find the matching ResultColumn */\r
+ source = getSourceResultColumn();\r
+ columnName = source.getName();\r
+ columnNumber = source.getColumnPosition();\r
+\r
+ if (source.getExpression() instanceof ColumnReference)\r
+ {\r
+ ColumnReference cr = (ColumnReference) source.getExpression();\r
+ tableNumber = cr.getTableNumber();\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // if dummy cr generated to replace aggregate, it may not have table number\r
+ // because underneath can be more than 1 table.\r
+ if (tableNumber == -1 && ! cr.getGeneratedToReplaceAggregate())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "tableNumber not expected to be -1, origName = " + origName);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public void unRemapColumnReferences()\r
+ {\r
+ if (origSource == null)\r
+ return;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // SanityManager.ASSERT(origSource != null,\r
+ // "Trying to unremap a ColumnReference that was not remapped.");\r
+ }\r
+\r
+ if ((remaps == null) || (remaps.size() == 0))\r
+ {\r
+ source = origSource;\r
+ origSource = null;\r
+ columnName = origName;\r
+ origName = null;\r
+ tableNumber = origTableNumber;\r
+ columnNumber = origColumnNumber;\r
+ }\r
+ else\r
+ {\r
+ // This CR is multiply-remapped, so undo the most\r
+ // recent (and only the most recent) remap operation.\r
+ RemapInfo rI = (RemapInfo)remaps.remove(remaps.size() - 1);\r
+ source = rI.getSource();\r
+ columnName = rI.getColumnName();\r
+ tableNumber = rI.getTableNumber();\r
+ columnNumber = rI.getColumnNumber();\r
+ rI = null;\r
+ if (remaps.size() == 0)\r
+ remaps = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns true if this ColumnReference has been remapped; false\r
+ * otherwise.\r
+ *\r
+ * @return Whether or not this ColumnReference has been remapped.\r
+ */\r
+ protected boolean hasBeenRemapped()\r
+ {\r
+ return (origSource != null);\r
+ }\r
+\r
+ /*\r
+ * Get the ResultColumn that the source points to. This is useful for\r
+ * getting what the source will be after this ColumnReference is remapped.\r
+ */\r
+ public ResultColumn getSourceResultColumn()\r
+ {\r
+ /* RESOLVE - If expression is a ColumnReference, then we are hitting\r
+ * the top of a query block (derived table or view.)\r
+ * In order to be able to push the expression down into the next\r
+ * query block, it looks like we should reset the contents of the\r
+ * current ColumnReference to be the same as expression. (This probably\r
+ * only means names and tableNumber.) We would then "rebind" the top\r
+ * level predicate somewhere up the call stack and see if we could push\r
+ * the predicate through.\r
+ */\r
+ \r
+ return source.getExpression().getSourceResultColumn();\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()\r
+ throws StandardException\r
+ {\r
+ ResultColumn rc;\r
+ ResultColumn sourceRC = source;\r
+\r
+ /* Nothing to do if we are not pointing to a redundant RC */\r
+ if (! source.isRedundant())\r
+ {\r
+ return this;\r
+ }\r
+\r
+ /* Find the last redundant RC in the chain. We\r
+ * want to clone its expression.\r
+ */\r
+ for (rc = source; rc != null && rc.isRedundant(); )\r
+ {\r
+ /* Find the matching ResultColumn */\r
+ ResultColumn nextRC = rc.getExpression().getSourceResultColumn();\r
+\r
+ if (nextRC != null && nextRC.isRedundant())\r
+ {\r
+ sourceRC = nextRC;\r
+ }\r
+ rc = nextRC;\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (sourceRC == null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "sourceRC is expected to be non-null for " +\r
+ columnName);\r
+ }\r
+\r
+ if ( ! sourceRC.isRedundant())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "sourceRC is expected to be redundant for " +\r
+ columnName);\r
+ }\r
+ }\r
+\r
+ /* If last expression is a VCN, then we can't clone it.\r
+ * Instead, we just reset our source to point to the\r
+ * source of the VCN, those chopping out the layers.\r
+ * Otherwise, we return a clone of the underlying expression.\r
+ */\r
+ if (sourceRC.getExpression() instanceof VirtualColumnNode)\r
+ {\r
+ VirtualColumnNode vcn =\r
+ (VirtualColumnNode) (sourceRC.getExpression());\r
+ ResultSetNode rsn = vcn.getSourceResultSet();\r
+ if (rsn instanceof FromTable)\r
+ {\r
+ FromTable ft = (FromTable)rsn;\r
+ tableNumber = ft.getTableNumber();\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(tableNumber != -1,\r
+ "tableNumber not expected to be -1");\r
+ }\r
+\r
+ /* It's not enough to just set the table number. Depending\r
+ * on the original query specified and on whether or not\r
+ * subquery flattening has occurred, it's possible that\r
+ * the expression to which we're remapping has a different\r
+ * RCL ordering than the one to which we were mapped before\r
+ * we got here. In that case we also need to update the\r
+ * columnNumber to point to the correct column in "ft".\r
+ * See DERBY-2526 for details.\r
+ */\r
+ ResultColumn ftRC =\r
+ ft.getResultColumns().getResultColumn(columnName);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(ftRC != null,\r
+ "Failed to find column '" + columnName + "' in the " +\r
+ "RCL for '" + ft.getTableName() + "'.");\r
+ }\r
+\r
+ /* Use the virtual column id if the ResultColumn's expression\r
+ * is a virtual column (DERBY-3023).\r
+ */\r
+ columnNumber =\r
+ (ftRC.getExpression() instanceof VirtualColumnNode)\r
+ ? ftRC.getVirtualColumnId()\r
+ : ftRC.getColumnPosition();\r
+ }\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("rsn expected to be a FromTable, but is a " + rsn.getClass().getName());\r
+ }\r
+ }\r
+ source = sourceRC.getExpression().getSourceResultColumn();\r
+ return this;\r
+ }\r
+ else\r
+ {\r
+ return sourceRC.getExpression().getClone();\r
+ }\r
+ }\r
+\r
+ /** \r
+ * Update the table map to reflect the source\r
+ * of this CR.\r
+ *\r
+ * @param refs The table map.\r
+ */\r
+ void getTablesReferenced(JBitSet refs)\r
+ {\r
+ if (refs.size() < tableNumber)\r
+ refs.grow(tableNumber);\r
+\r
+ if (tableNumber != -1) // it may not be set if replacesAggregate is true\r
+ refs.set(tableNumber);\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 true;\r
+ }\r
+\r
+ /** @see ValueNode#constantExpression */\r
+ public boolean constantExpression(PredicateList whereClause)\r
+ {\r
+ return whereClause.constantColumn(this);\r
+ }\r
+\r
+ /**\r
+ * ColumnReference's are to the current row in the system.\r
+ * This lets us generate\r
+ * a faster get that simply returns the column from the\r
+ * current row, rather than getting the value out and\r
+ * returning that, only to have the caller (in the situations\r
+ * needed) stuffing it back into a new column holder object.\r
+ * We will assume the general generate() path is for getting\r
+ * the value out, and use generateColumn() when we want to\r
+ * keep the column wrapped.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ int sourceResultSetNumber = source.getResultSetNumber();\r
+\r
+ //PUSHCOMPILE\r
+ /* Reuse generated code, where possible */\r
+\r
+ /*\r
+ ** If the source is redundant, return the generation of its source.\r
+ ** Most redundant nodes will be flattened out by this point, but\r
+ ** in at least one case (elimination of redundant ProjectRestricts\r
+ ** during generation) we don't do this.\r
+ */\r
+ if (source.isRedundant())\r
+ {\r
+ source.generateExpression(acb, mb);\r
+ return;\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (sourceResultSetNumber < 0)\r
+ {\r
+ SanityManager.THROWASSERT("sourceResultSetNumber expected to be >= 0 for " + getTableName() + "." + getColumnName());\r
+ }\r
+ }\r
+\r
+ /* The ColumnReference is from an immediately underlying ResultSet.\r
+ * The Row for that ResultSet is Activation.row[sourceResultSetNumber], \r
+ * where sourceResultSetNumber is the resultSetNumber for that ResultSet.\r
+ *\r
+ * The generated java is the expression:\r
+ * (<interface>) this.row[sourceResultSetNumber].getColumn(#columnId);\r
+ *\r
+ * where <interface> is the appropriate Datatype protocol interface\r
+ * for the type of the column.\r
+ */\r
+ acb.pushColumnReference(mb, sourceResultSetNumber, \r
+ source.getVirtualColumnId());\r
+\r
+ mb.cast(getTypeCompiler().interfaceName());\r
+\r
+ /* Remember generated code for possible resuse */\r
+ }\r
+\r
+ /**\r
+ * Get the user-supplied schema name of this column. This will be null\r
+ * if the user did not supply a name (for example, select t.a from t).\r
+ * Another example for null return value (for example, select b.a from t as b).\r
+ * But for following query select app.t.a from t, this will return APP\r
+ * Code generation of aggregate functions relies on this method\r
+ *\r
+ * @return The user-supplied schema name of this column. Null if no user-\r
+ * supplied name.\r
+ */\r
+\r
+ public String getSchemaName()\r
+ {\r
+ return ( ( tableName != null) ? tableName.getSchemaName() : null );\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
+ */\r
+ protected int getOrderableVariantType()\r
+ {\r
+ // ColumnReferences are invariant for the life of the scan\r
+ return Qualifier.SCAN_INVARIANT;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the source of this ColumnReference is itself a ColumnReference.\r
+ *\r
+ * @return Whether or not the source of this ColumnReference is itself a ColumnReference.\r
+ */\r
+ boolean pointsToColumnReference()\r
+ { \r
+ return (source.getExpression() instanceof ColumnReference);\r
+ }\r
+\r
+ /**\r
+ * Get the DataTypeServices from this Node.\r
+ *\r
+ * @return The DataTypeServices from this Node. This\r
+ * may be null if the node isn't bound yet.\r
+ */\r
+ public DataTypeDescriptor getTypeServices() throws StandardException\r
+ {\r
+ DataTypeDescriptor dtd = super.getTypeServices();\r
+ if( dtd == null && source != null)\r
+ {\r
+ dtd = source.getTypeServices();\r
+ if( dtd != null)\r
+ setType( dtd);\r
+ }\r
+ return dtd;\r
+ } // end of getTypeServices\r
+\r
+ /**\r
+ * Find the source result set for this ColumnReference and\r
+ * return it. Also, when the source result set is found,\r
+ * return the position (within the source result set's RCL)\r
+ * of the column referenced by this ColumnReference. The\r
+ * position is returned vai the colNum parameter.\r
+ *\r
+ * @param colNum Place to store the position of the column\r
+ * to which this ColumnReference points (position is w.r.t\r
+ * the source result set).\r
+ * @return The source result set for this ColumnReference;\r
+ * null if there is no source result set.\r
+ */\r
+ protected ResultSetNode getSourceResultSet(int [] colNum)\r
+ throws StandardException\r
+ {\r
+ if (source == null)\r
+ {\r
+ /* this can happen if column reference is pointing to a column\r
+ * that is not from a base table. For example, if we have a\r
+ * VALUES clause like\r
+ *\r
+ * (values (1, 2), (3, 4)) V1 (i, j)\r
+ *\r
+ * and then a column reference to VI.i, the column reference\r
+ * won't have a source.\r
+ */\r
+ return null;\r
+ }\r
+\r
+ ValueNode rcExpr = null;\r
+ ResultColumn rc = getSource();\r
+\r
+ // Walk the ResultColumn->ColumnReference chain until we\r
+ // find a ResultColumn whose expression is a VirtualColumnNode.\r
+\r
+ rcExpr = rc.getExpression();\r
+ colNum[0] = getColumnNumber();\r
+\r
+ /* We have to make sure we enter this loop if rc is redundant,\r
+ * so that we can navigate down to the actual source result\r
+ * set (DERBY-1777). If rc *is* redundant, then rcExpr is not\r
+ * guaranteed to be a ColumnReference, so we have to check\r
+ * for that case inside the loop.\r
+ */\r
+ while ((rcExpr != null) &&\r
+ (rc.isRedundant() || (rcExpr instanceof ColumnReference)))\r
+ {\r
+ if (rcExpr instanceof ColumnReference)\r
+ {\r
+ colNum[0] = ((ColumnReference)rcExpr).getColumnNumber();\r
+ rc = ((ColumnReference)rcExpr).getSource();\r
+ }\r
+\r
+ /* If "rc" is redundant then that means it points to another\r
+ * ResultColumn that in turn points to the source expression.\r
+ * This can happen in cases where "rc" points to a subquery\r
+ * that has been flattened into the query above it (flattening\r
+ * of subqueries occurs during preprocessing). In that case\r
+ * we want to skip over the redundant rc and find the\r
+ * ResultColumn that actually holds the source expression.\r
+ */\r
+ while (rc.isRedundant())\r
+ {\r
+ rcExpr = rc.getExpression();\r
+ if (rcExpr instanceof VirtualColumnNode)\r
+ rc = rcExpr.getSourceResultColumn();\r
+ else if (rcExpr instanceof ColumnReference)\r
+ {\r
+ colNum[0] = ((ColumnReference)rcExpr).getColumnNumber();\r
+ rc = ((ColumnReference)rcExpr).getSource();\r
+ }\r
+ else\r
+ {\r
+ /* If rc isn't pointing to a VirtualColumnNode nor\r
+ * to a ColumnReference, then it's not pointing to\r
+ * a result set. It could, for example, be pointing\r
+ * to a constant node or to the result of an aggregate\r
+ * or function. Break out of both loops and return\r
+ * null since there is no source result set.\r
+ */\r
+ rcExpr = null;\r
+ break;\r
+ }\r
+ }\r
+ rcExpr = rc.getExpression();\r
+ }\r
+\r
+ // If we found a VirtualColumnNode, return the VirtualColumnNode's\r
+ // sourceResultSet. The column within that sourceResultSet that\r
+ // is referenced by this ColumnReference is also returned, via\r
+ // the colNum parameter, and was set above.\r
+ if ((rcExpr != null) && (rcExpr instanceof VirtualColumnNode))\r
+ return ((VirtualColumnNode)rcExpr).getSourceResultSet();\r
+\r
+ // If we get here then the ColumnReference doesn't reference\r
+ // a result set, so return null.\r
+ colNum[0] = -1;\r
+ return null;\r
+ }\r
+ \r
+ protected boolean isEquivalent(ValueNode o) throws StandardException\r
+ {\r
+ if (!isSameNodeType(o)) {\r
+ return false;\r
+ }\r
+ ColumnReference other = (ColumnReference)o;\r
+ return (tableNumber == other.tableNumber \r
+ && columnName.equals(other.getColumnName()));\r
+ }\r
+\r
+ /**\r
+ * Mark this column reference as "scoped", which means that it\r
+ * was created (as a clone of another ColumnReference) to serve\r
+ * as the left or right operand of a scoped predicate.\r
+ */\r
+ protected void markAsScoped()\r
+ {\r
+ scoped = true;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this ColumnReference is scoped.\r
+ */\r
+ protected boolean isScoped()\r
+ {\r
+ return scoped;\r
+ }\r
+\r
+ /**\r
+ * Helper class to keep track of remap data when a ColumnReference\r
+ * is remapped multiple times. This allows the CR to be UN-\r
+ * remapped multiple times, as well.\r
+ */\r
+ private class RemapInfo\r
+ {\r
+ int colNum;\r
+ int tableNum;\r
+ String colName;\r
+ ResultColumn source;\r
+\r
+ RemapInfo(int cNum, int tNum, String cName, ResultColumn rc)\r
+ {\r
+ colNum = cNum;\r
+ tableNum = tNum;\r
+ colName = cName;\r
+ source = rc;\r
+ }\r
+\r
+ int getColumnNumber() { return colNum; }\r
+ int getTableNumber() { return tableNum; }\r
+ String getColumnName() { return colName; }\r
+ ResultColumn getSource() { return source; }\r
+\r
+ void setColNumber(int cNum) { colNum = cNum; }\r
+ void setTableNumber(int tNum) { tableNum = tNum; }\r
+ void setColName(String cName) { colName = cName; }\r
+ void setSource(ResultColumn rc) { source = rc; }\r
+ }\r
+}\r