--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.BaseTableNumbersVisitor\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.Visitable; \r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+/**\r
+ * Walk through a subtree and build a list of the assigned numbers for\r
+ * all tables that exist in that subtree. We do this by looking for any\r
+ * column references in the subtree and, for each column reference, we\r
+ * walk down the ColumnReference-ResultColumn chain until we find the\r
+ * the bottom-most table number, which should correspond to a base\r
+ * table.\r
+ */\r
+public class BaseTableNumbersVisitor implements Visitor\r
+{\r
+ // JBitSet to hold the table numbers that we find.\r
+ private JBitSet tableMap;\r
+\r
+ /* Column number of the ColumnReference or ResultColumn\r
+ * for which we most recently found a base table number. \r
+ * In cases where this visitor is only expected to find\r
+ * a single base table number, this field is useful for\r
+ * determining what the column position w.r.t. the found\r
+ * base table was.\r
+ */\r
+ private int columnNumber;\r
+\r
+ /**\r
+ * Constructor: takes a JBitSet to use as the holder for any base table\r
+ * numbers found while walking the subtree.\r
+ *\r
+ * @param tableMap JBitSet into which we put the table numbers we find.\r
+ */\r
+ public BaseTableNumbersVisitor(JBitSet tableMap)\r
+ {\r
+ this.tableMap = tableMap;\r
+ columnNumber = -1;\r
+ }\r
+\r
+ /**\r
+ * Set a new JBitSet to serve as the holder for base table numbers\r
+ * we find while walking.\r
+ *\r
+ * @param tableMap JBitSet into which we put the table numbers we find.\r
+ */\r
+ protected void setTableMap(JBitSet tableMap)\r
+ {\r
+ this.tableMap = tableMap;\r
+ }\r
+\r
+ /**\r
+ * Reset the state of this visitor.\r
+ */\r
+ protected void reset()\r
+ {\r
+ tableMap.clearAll();\r
+ columnNumber = -1;\r
+ }\r
+\r
+ /**\r
+ * Retrieve the the position of the ColumnReference or\r
+ * ResultColumn for which we most recently found a base\r
+ * table number.\r
+ */\r
+ protected int getColumnNumber()\r
+ {\r
+ return columnNumber;\r
+ }\r
+\r
+ ////////////////////////////////////////////////\r
+ //\r
+ // VISITOR INTERFACE\r
+ //\r
+ ////////////////////////////////////////////////\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.Visitor#visit\r
+ */\r
+ public Visitable visit(Visitable node)\r
+ throws StandardException\r
+ {\r
+ ResultColumn rc = null;\r
+ if (node instanceof ColumnReference)\r
+ {\r
+ // Start by seeing if this column reference is the\r
+ // bottom-most one, meaning that there are no column\r
+ // references beneath this one.\r
+ rc = ((ColumnReference)node).getSource();\r
+\r
+ if (rc == null) {\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
+ return node;\r
+ }\r
+ }\r
+ else if (node instanceof ResultColumn)\r
+ rc = (ResultColumn)node;\r
+ else if (node instanceof SelectNode)\r
+ {\r
+ // If the node is a SelectNode we just need to look at its\r
+ // FROM list.\r
+ ((SelectNode)node).getFromList().accept(this);\r
+ }\r
+ else if (node instanceof FromBaseTable) {\r
+ // just grab the FBT's table number.\r
+ tableMap.set(((FromBaseTable)node).getTableNumber());\r
+ }\r
+\r
+ if (rc != null)\r
+ {\r
+ // This next call will walk through the ResultColumn tree\r
+ // until it finds another ColumnReference, and then will\r
+ // return the table number for that column reference. We\r
+ // can't stop there, though, because the column reference\r
+ // that we found might in turn have column references beneath\r
+ // it, and we only want the table number of the bottom-most\r
+ // column reference. So once we find the column reference,\r
+ // we have to recurse.\r
+\r
+ int baseTableNumber = rc.getTableNumber();\r
+ if (baseTableNumber >= 0) {\r
+ // Move down to the column reference that has the table\r
+ // number that we just found. There may be one or more\r
+ // VirtualColumnNode-to-ResultColumnNode links between\r
+ // the current ResultColumn and the column reference we're\r
+ // looking for, so we have to walk past those until we find\r
+ // the desired column reference.\r
+\r
+ ValueNode rcExpr = rc.getExpression();\r
+ while (rcExpr instanceof VirtualColumnNode) {\r
+ rc = ((VirtualColumnNode)rcExpr).getSourceColumn();\r
+ rcExpr = rc.getExpression();\r
+ }\r
+\r
+ if (rcExpr instanceof ColumnReference)\r
+ // we found our column reference; recurse using that.\r
+ rcExpr.accept(this);\r
+ else {\r
+ // Else we must have found the table number someplace\r
+ // other than within a ColumnReference (ex. we may\r
+ // have pulled it from a VirtualColumnNode's source\r
+ // table); so just set the number.\r
+ tableMap.set(baseTableNumber);\r
+ columnNumber = rc.getColumnPosition();\r
+ }\r
+ }\r
+ else if (node instanceof ColumnReference) {\r
+ // we couldn't find any other table numbers beneath the\r
+ // ColumnReference, so just use the table number for\r
+ // that reference.\r
+ ColumnReference cr = (ColumnReference)node;\r
+ cr.getTablesReferenced(tableMap);\r
+ columnNumber = cr.getColumnNumber();\r
+ }\r
+ }\r
+\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.Visitor#skipChildren\r
+ */\r
+ public boolean skipChildren(Visitable node)\r
+ {\r
+ /* A SelectNode's children can include a where clause in the\r
+ * form of either a PredicateList or an AndNode. In either\r
+ * case we don't want to descend into the where clause because\r
+ * it's possible that it references a base table that is not\r
+ * in the subtree we're walking. So we skip the children of\r
+ * a SelectNode. Similarly, any other PredicateList may contain\r
+ * references to base tables that we don't want to include, so\r
+ * we skip a PredicateList's children as well. Note, though,\r
+ * that if this visitor is specifically targeted for a particular\r
+ * Predicate or AndNode (i.e. a call is directly made to\r
+ * Predicate.accept() or AndNode.accept()) then we _will_ descend\r
+ * into that predicate's operands and retrieve referenced base\r
+ * table numbers.\r
+ *\r
+ * And finally, if we visit a FromBaseTable we can just grab\r
+ * it's number and that's it--there's no need to go any further.\r
+ */\r
+ return (node instanceof FromBaseTable) ||\r
+ (node instanceof SelectNode) ||\r
+ (node instanceof PredicateList);\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.Visitor#stopTraversal\r
+ */\r
+ public boolean stopTraversal()\r
+ {\r
+ return false;\r
+ }\r
+\r
+} \r