--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.SingleChildResultSetNode\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.services.context.ContextManager;\r
+\r
+import org.apache.derby.iapi.sql.compile.AccessPath;\r
+import org.apache.derby.iapi.sql.compile.CostEstimate;\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+import org.apache.derby.iapi.sql.compile.OptimizableList;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicate;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;\r
+import org.apache.derby.iapi.sql.compile.Optimizer;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A SingleChildResultSetNode represents a result set with a single child.\r
+ *\r
+ */\r
+\r
+abstract class SingleChildResultSetNode extends FromTable\r
+{\r
+ /**\r
+ * ResultSetNode under the SingleChildResultSetNode\r
+ */\r
+ ResultSetNode childResult;\r
+\r
+ // Does this node have the truly... for the underlying tree\r
+ protected boolean hasTrulyTheBestAccessPath;\r
+\r
+\r
+ /**\r
+ * Initialilzer for a SingleChildResultSetNode.\r
+ *\r
+ * @param childResult The child ResultSetNode\r
+ * @param tableProperties Properties list associated with the table\r
+ */\r
+\r
+ public void init(Object childResult, Object tableProperties)\r
+ {\r
+ /* correlationName is always null */\r
+ super.init(null, tableProperties);\r
+ this.childResult = (ResultSetNode) childResult;\r
+\r
+ /* Propagate the child's referenced table map, if one exists */\r
+ if (this.childResult.getReferencedTableMap() != null)\r
+ {\r
+ referencedTableMap =\r
+ (JBitSet) this.childResult.getReferencedTableMap().clone();\r
+ }\r
+ }\r
+\r
+ /** @see Optimizable#getTrulyTheBestAccessPath */\r
+ public AccessPath getTrulyTheBestAccessPath()\r
+ {\r
+ if (hasTrulyTheBestAccessPath)\r
+ {\r
+ return super.getTrulyTheBestAccessPath();\r
+ }\r
+\r
+ if (childResult instanceof Optimizable)\r
+ return ((Optimizable) childResult).getTrulyTheBestAccessPath();\r
+\r
+ return super.getTrulyTheBestAccessPath();\r
+ }\r
+\r
+ /**\r
+ * Return the childResult from this node.\r
+ *\r
+ * @return ResultSetNode The childResult from this node.\r
+ */\r
+ public ResultSetNode getChildResult()\r
+ {\r
+ return childResult;\r
+ }\r
+\r
+ /**\r
+ * Set the childResult for this node.\r
+ *\r
+ * @param childResult The new childResult for this node.\r
+ */\r
+ void setChildResult(ResultSetNode childResult)\r
+ {\r
+ this.childResult = childResult;\r
+ }\r
+\r
+ /**\r
+ * @see Optimizable#pullOptPredicates\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void pullOptPredicates(\r
+ OptimizablePredicateList optimizablePredicates)\r
+ throws StandardException\r
+ {\r
+ if (childResult instanceof Optimizable)\r
+ {\r
+ ((Optimizable) childResult).pullOptPredicates(optimizablePredicates);\r
+ }\r
+ }\r
+\r
+ /** @see Optimizable#forUpdate */\r
+ public boolean forUpdate()\r
+ {\r
+ if (childResult instanceof Optimizable)\r
+ {\r
+ return ((Optimizable) childResult).forUpdate();\r
+ }\r
+ else\r
+ {\r
+ return super.forUpdate();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see Optimizable#initAccessPaths\r
+ */\r
+ public void initAccessPaths(Optimizer optimizer)\r
+ {\r
+ super.initAccessPaths(optimizer);\r
+ if (childResult instanceof Optimizable)\r
+ {\r
+ ((Optimizable) childResult).initAccessPaths(optimizer);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see Optimizable#updateBestPlanMap\r
+ *\r
+ * Makes a call to add/load/remove a plan mapping for this node,\r
+ * and then makes the necessary call to recurse on this node's\r
+ * child, in order to ensure that we've handled the full plan\r
+ * all the way down this node's subtree.\r
+ */\r
+ public void updateBestPlanMap(short action,\r
+ Object planKey) throws StandardException\r
+ {\r
+ super.updateBestPlanMap(action, planKey);\r
+\r
+ // Now walk the child. Note that if the child is not an\r
+ // Optimizable and the call to child.getOptimizerImpl()\r
+ // returns null, then that means we haven't tried to optimize\r
+ // the child yet. So in that case there's nothing to\r
+ // add/load.\r
+\r
+ if (childResult instanceof Optimizable)\r
+ {\r
+ ((Optimizable)childResult).\r
+ updateBestPlanMap(action, planKey);\r
+ }\r
+ else if (childResult.getOptimizerImpl() != null)\r
+ {\r
+ childResult.getOptimizerImpl().\r
+ updateBestPlanMaps(action, planKey);\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 (childResult != null)\r
+ {\r
+ printLabel(depth, "childResult: ");\r
+ childResult.treePrint(depth + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Search to see if a query references the specifed table name.\r
+ *\r
+ * @param name Table name (String) to search for.\r
+ * @param baseTable Whether or not name is for a base table\r
+ *\r
+ * @return true if found, else false\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean referencesTarget(String name, boolean baseTable)\r
+ throws StandardException\r
+ {\r
+ return childResult.referencesTarget(name, baseTable);\r
+ }\r
+\r
+ /**\r
+ * Return true if the node references SESSION schema tables (temporary or permanent)\r
+ *\r
+ * @return true if references SESSION schema tables, else false\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean referencesSessionSchema()\r
+ throws StandardException\r
+ {\r
+ return childResult.referencesSessionSchema();\r
+ }\r
+\r
+ /**\r
+ * Set the (query block) level (0-based) for this FromTable.\r
+ *\r
+ * @param level The query block level for this FromTable.\r
+ */\r
+ public void setLevel(int level)\r
+ {\r
+ super.setLevel(level);\r
+ if (childResult instanceof FromTable)\r
+ {\r
+ ((FromTable) childResult).setLevel(level);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this ResultSetNode contains a subquery with a\r
+ * reference to the specified target.\r
+ * \r
+ * @param name The table name.\r
+ * @param baseTable Whether or not the name is for a base table.\r
+ *\r
+ * @return boolean Whether or not a reference to the table was found.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ boolean subqueryReferencesTarget(String name, boolean baseTable)\r
+ throws StandardException\r
+ {\r
+ return childResult.subqueryReferencesTarget(name, baseTable);\r
+ }\r
+\r
+ /** \r
+ * Put a ProjectRestrictNode on top of each FromTable in the FromList.\r
+ * ColumnReferences must continue to point to the same ResultColumn, so\r
+ * that ResultColumn must percolate up to the new PRN. However,\r
+ * that ResultColumn will point to a new expression, a VirtualColumnNode, \r
+ * which points to the FromTable and the ResultColumn that is the source for\r
+ * the ColumnReference. \r
+ * (The new PRN will have the original of the ResultColumnList and\r
+ * the ResultColumns from that list. The FromTable will get shallow copies\r
+ * of the ResultColumnList and its ResultColumns. ResultColumn.expression\r
+ * will remain at the FromTable, with the PRN getting a new \r
+ * VirtualColumnNode for each ResultColumn.expression.)\r
+ * We then project out the non-referenced columns. If there are no referenced\r
+ * columns, then the PRN's ResultColumnList will consist of a single ResultColumn\r
+ * whose expression is 1.\r
+ *\r
+ * @param numTables Number of tables in the DML Statement\r
+ * @param gbl The group by list, if any\r
+ * @param fromList The from list, if any\r
+ *\r
+ * @return The generated ProjectRestrictNode atop the original FromTable.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public ResultSetNode preprocess(int numTables,\r
+ GroupByList gbl,\r
+ FromList fromList) \r
+ throws StandardException\r
+ {\r
+ childResult = childResult.preprocess(numTables, gbl, fromList);\r
+\r
+ /* Build the referenced table map */\r
+ referencedTableMap = (JBitSet) childResult.getReferencedTableMap().clone();\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Add a new predicate to the list. This is useful when doing subquery\r
+ * transformations, when we build a new predicate with the left side of\r
+ * the subquery operator and the subquery's result column.\r
+ *\r
+ * @param predicate The predicate to add\r
+ *\r
+ * @return ResultSetNode The new top of the tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultSetNode addNewPredicate(Predicate predicate)\r
+ throws StandardException\r
+ {\r
+ childResult = childResult.addNewPredicate(predicate);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Push expressions down to the first ResultSetNode which can do expression\r
+ * evaluation and has the same referenced table map.\r
+ * RESOLVE - This means only pushing down single table expressions to\r
+ * DistinctNodes today. Once we have a better understanding of how\r
+ * the optimizer will work, we can push down join clauses.\r
+ *\r
+ * @param predicateList The PredicateList.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void pushExpressions(PredicateList predicateList)\r
+ throws StandardException\r
+ {\r
+ if (childResult instanceof FromTable)\r
+ {\r
+ ((FromTable) childResult).pushExpressions(predicateList);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Evaluate whether or not the subquery in a FromSubquery is flattenable. \r
+ * Currently, a FSqry is flattenable if all of the following are true:\r
+ * o Subquery is a SelectNode. \r
+ * o It contains no top level subqueries. (RESOLVE - we can relax this)\r
+ * o It does not contain a group by or having clause\r
+ * o It does not contain aggregates.\r
+ *\r
+ * @param fromList The outer from list\r
+ *\r
+ * @return boolean Whether or not the FromSubquery is flattenable.\r
+ */\r
+ public boolean flattenableInFromSubquery(FromList fromList)\r
+ {\r
+ /* Flattening currently involves merging predicates and FromLists.\r
+ * We don't have a FromList, so we can't flatten for now.\r
+ */\r
+ /* RESOLVE - this will introduce yet another unnecessary PRN */\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Ensure that the top of the RSN tree has a PredicateList.\r
+ *\r
+ * @param numTables The number of tables in the query.\r
+ * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultSetNode ensurePredicateList(int numTables) \r
+ throws StandardException\r
+ {\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Optimize this SingleChildResultSetNode. \r
+ *\r
+ * @param dataDictionary The DataDictionary to use for optimization\r
+ * @param predicates The PredicateList to optimize. This should\r
+ * be a join predicate.\r
+ * @param outerRows The number of outer joining rows\r
+ *\r
+ * @return ResultSetNode The top of the optimized subtree\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public ResultSetNode optimize(DataDictionary dataDictionary,\r
+ PredicateList predicates,\r
+ double outerRows) \r
+ throws StandardException\r
+ {\r
+ /* We need to implement this method since a NRSN can appear above a\r
+ * SelectNode in a query tree.\r
+ */\r
+ childResult = childResult.optimize(\r
+ dataDictionary,\r
+ predicates,\r
+ outerRows);\r
+\r
+ Optimizer optimizer =\r
+ getOptimizer(\r
+ (FromList) getNodeFactory().getNode(\r
+ C_NodeTypes.FROM_LIST,\r
+ getNodeFactory().doJoinOrderOptimization(),\r
+ getContextManager()),\r
+ predicates,\r
+ dataDictionary,\r
+ (RequiredRowOrdering) null);\r
+ costEstimate = optimizer.newCostEstimate();\r
+ costEstimate.setCost(childResult.getCostEstimate().getEstimatedCost(),\r
+ childResult.getCostEstimate().rowCount(),\r
+ childResult.getCostEstimate().singleScanRowCount());\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#modifyAccessPaths\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultSetNode modifyAccessPaths() throws StandardException\r
+ {\r
+ childResult = childResult.modifyAccessPaths();\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#changeAccessPath\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultSetNode changeAccessPath() throws StandardException\r
+ {\r
+ childResult = childResult.changeAccessPath();\r
+ return this;\r
+ }\r
+\r
+ /** \r
+ * Determine whether or not the specified name is an exposed name in\r
+ * the current query block.\r
+ *\r
+ * @param name The specified name to search for as an exposed name.\r
+ * @param schemaName Schema name, if non-null.\r
+ * @param exactMatch Whether or not we need an exact match on specified schema and table\r
+ * names or match on table id.\r
+ *\r
+ * @return The FromTable, if any, with the exposed name.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected FromTable getFromTableByName(String name, String schemaName, boolean exactMatch)\r
+ throws StandardException\r
+ {\r
+ return childResult.getFromTableByName(name, schemaName, exactMatch);\r
+ }\r
+\r
+ /**\r
+ * Decrement (query block) level (0-based) for this FromTable.\r
+ * This is useful when flattening a subquery.\r
+ *\r
+ * @param decrement The amount to decrement by.\r
+ */\r
+ void decrementLevel(int decrement)\r
+ {\r
+ super.decrementLevel(decrement);\r
+ childResult.decrementLevel(decrement);\r
+ }\r
+\r
+ /**\r
+ * Get the lock mode for the target of an update statement\r
+ * (a delete or update). The update mode will always be row for\r
+ * CurrentOfNodes. It will be table if there is no where clause.\r
+ *\r
+ * @return The lock mode\r
+ */\r
+ public int updateTargetLockMode()\r
+ {\r
+ return childResult.updateTargetLockMode();\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the underlying ResultSet tree\r
+ * is ordered on the specified columns.\r
+ * RESOLVE - This method currently only considers the outermost table \r
+ * of the query block.\r
+ *\r
+ * @param crs The specified ColumnReference[]\r
+ * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted\r
+ * @param fbtVector Vector that is to be filled with the FromBaseTable \r
+ *\r
+ * @return Whether the underlying ResultSet tree\r
+ * is ordered on the specified column.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering, Vector fbtVector)\r
+ throws StandardException\r
+ {\r
+ return childResult.isOrderedOn(crs, permuteOrdering, fbtVector);\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the underlying ResultSet tree will return\r
+ * a single row, at most.\r
+ * This is important for join nodes where we can save the extra next\r
+ * on the right side if we know that it will return at most 1 row.\r
+ *\r
+ * @return Whether or not the underlying ResultSet tree will return a single row.\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean isOneRowResultSet() throws StandardException\r
+ {\r
+ // Default is false\r
+ return childResult.isOneRowResultSet();\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the underlying ResultSet tree is for a NOT EXISTS join.\r
+ *\r
+ * @return Whether or not the underlying ResultSet tree is for a NOT EXISTS.\r
+ */\r
+ public boolean isNotExists()\r
+ {\r
+ return childResult.isNotExists();\r
+ }\r
+\r
+ /**\r
+ * Determine whether we need to do reflection in order to do the projection. \r
+ * Reflection is only needed if there is at least 1 column which is not\r
+ * simply selecting the source column.\r
+ *\r
+ * @return Whether or not we need to do reflection in order to do\r
+ * the projection.\r
+ */\r
+ protected boolean reflectionNeededForProjection()\r
+ {\r
+ return ! (resultColumns.allExpressionsAreColumns(childResult));\r
+ }\r
+\r
+ /**\r
+ * Replace any DEFAULTs with the associated tree for the default.\r
+ *\r
+ * @param ttd The TableDescriptor for the target table.\r
+ * @param tcl The RCL for the target table.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl) \r
+ throws StandardException\r
+ {\r
+ childResult.replaceDefaults(ttd, tcl);\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#adjustForSortElimination\r
+ */\r
+ void adjustForSortElimination()\r
+ {\r
+ childResult.adjustForSortElimination();\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#adjustForSortElimination\r
+ */\r
+ void adjustForSortElimination(RequiredRowOrdering rowOrdering)\r
+ throws StandardException\r
+ {\r
+ childResult.adjustForSortElimination(rowOrdering);\r
+ }\r
+\r
+ /**\r
+ * Get the final CostEstimate for this node.\r
+ *\r
+ * @return The final CostEstimate for this node, which is\r
+ * the final cost estimate for the child node.\r
+ */\r
+ public CostEstimate getFinalCostEstimate()\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** The cost estimate will be set here if either optimize() or\r
+ ** optimizeIt() was called on this node. It's also possible\r
+ ** that optimization was done directly on the child node,\r
+ ** in which case the cost estimate will be null here.\r
+ */\r
+ if (costEstimate == null)\r
+ return childResult.getFinalCostEstimate();\r
+ else\r
+ {\r
+ return costEstimate;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Accept a visitor, and call v.visit()\r
+ * on child nodes as necessary. \r
+ * \r
+ * @param v the visitor\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public Visitable accept(Visitor v) \r
+ throws StandardException\r
+ {\r
+ if (v.skipChildren(this))\r
+ {\r
+ return v.visit(this);\r
+ }\r
+\r
+ Visitable returnNode = super.accept(v);\r
+\r
+ if (childResult != null && !v.stopTraversal())\r
+ {\r
+ childResult = (ResultSetNode)childResult.accept(v);\r
+ }\r
+\r
+ return returnNode;\r
+ }\r
+}\r