--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.IndexToBaseRowNode\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.RequiredRowOrdering;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+\r
+import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This node type translates an index row to a base row. It takes a\r
+ * FromBaseTable as its source ResultSetNode, and generates an\r
+ * IndexRowToBaseRowResultSet that takes a TableScanResultSet on an\r
+ * index conglomerate as its source.\r
+ */\r
+public class IndexToBaseRowNode extends FromTable\r
+{\r
+ protected FromBaseTable source;\r
+ protected ConglomerateDescriptor baseCD;\r
+ protected boolean cursorTargetTable;\r
+ protected PredicateList restrictionList;\r
+ protected boolean forUpdate;\r
+ private FormatableBitSet heapReferencedCols;\r
+ private FormatableBitSet indexReferencedCols;\r
+ private FormatableBitSet allReferencedCols;\r
+ private FormatableBitSet heapOnlyReferencedCols;\r
+\r
+ public void init(\r
+ Object source,\r
+ Object baseCD,\r
+ Object resultColumns,\r
+ Object cursorTargetTable,\r
+ Object heapReferencedCols,\r
+ Object indexReferencedCols,\r
+ Object restrictionList,\r
+ Object forUpdate,\r
+ Object tableProperties)\r
+ {\r
+ super.init(null, tableProperties);\r
+ this.source = (FromBaseTable) source;\r
+ this.baseCD = (ConglomerateDescriptor) baseCD;\r
+ this.resultColumns = (ResultColumnList) resultColumns;\r
+ this.cursorTargetTable = ((Boolean) cursorTargetTable).booleanValue();\r
+ this.restrictionList = (PredicateList) restrictionList;\r
+ this.forUpdate = ((Boolean) forUpdate).booleanValue();\r
+ this.heapReferencedCols = (FormatableBitSet) heapReferencedCols;\r
+ this.indexReferencedCols = (FormatableBitSet) indexReferencedCols;\r
+\r
+ if (this.indexReferencedCols == null) {\r
+ this.allReferencedCols = this.heapReferencedCols;\r
+ heapOnlyReferencedCols = this.heapReferencedCols;\r
+ }\r
+ else {\r
+ this.allReferencedCols =\r
+ new FormatableBitSet(this.heapReferencedCols);\r
+ this.allReferencedCols.or(this.indexReferencedCols);\r
+ heapOnlyReferencedCols =\r
+ new FormatableBitSet(allReferencedCols);\r
+ heapOnlyReferencedCols.xor(this.indexReferencedCols);\r
+ }\r
+ }\r
+\r
+ /** @see Optimizable#forUpdate */\r
+ public boolean forUpdate()\r
+ {\r
+ return source.forUpdate();\r
+ }\r
+\r
+ /** @see Optimizable#getTrulyTheBestAccessPath */\r
+ public AccessPath getTrulyTheBestAccessPath()\r
+ {\r
+ // Get AccessPath comes from base table.\r
+ return ((Optimizable) source).getTrulyTheBestAccessPath();\r
+ }\r
+\r
+ public CostEstimate getCostEstimate()\r
+ {\r
+ return source.getTrulyTheBestAccessPath().getCostEstimate();\r
+ }\r
+\r
+ public CostEstimate getFinalCostEstimate()\r
+ {\r
+ return source.getFinalCostEstimate();\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 source.isOrderedOn(crs, permuteOrdering, fbtVector);\r
+ }\r
+\r
+ /**\r
+ * Generation of an IndexToBaseRowNode creates an\r
+ * IndexRowToBaseRowResultSet, which uses the RowLocation in the last\r
+ * column of an index row to get the row from the base conglomerate (heap).\r
+ *\r
+ * @param acb The ActivationClassBuilder for the class being built\r
+ * @param mb the method for the method to be built\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generate(ActivationClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ ValueNode restriction = null;\r
+\r
+ /*\r
+ ** Get the next ResultSet #, so that we can number this ResultSetNode,\r
+ ** its ResultColumnList and ResultSet.\r
+ */\r
+ assignResultSetNumber();\r
+\r
+ // Get the CostEstimate info for the underlying scan\r
+ costEstimate = getFinalCostEstimate();\r
+\r
+ /* Put the predicates back into the tree */\r
+ if (restrictionList != null)\r
+ {\r
+ restriction = restrictionList.restorePredicates();\r
+ /* Allow the restrictionList to get garbage collected now\r
+ * that we're done with it.\r
+ */\r
+ restrictionList = null;\r
+ }\r
+\r
+ // for the restriction, we generate an exprFun\r
+ // that evaluates the expression of the clause\r
+ // against the current row of the child's result.\r
+ // if the restriction is empty, simply pass null\r
+ // to optimize for run time performance.\r
+\r
+ // generate the function and initializer:\r
+ // Note: Boolean lets us return nulls (boolean would not)\r
+ // private Boolean exprN()\r
+ // {\r
+ // return <<restriction.generate(ps)>>;\r
+ // }\r
+ // static Method exprN = method pointer to exprN;\r
+\r
+\r
+\r
+ int heapColRefItem = -1;\r
+ if (heapReferencedCols != null)\r
+ {\r
+ heapColRefItem = acb.addItem(heapReferencedCols);\r
+ }\r
+ int allColRefItem = -1;\r
+ if (allReferencedCols != null)\r
+ {\r
+ allColRefItem = acb.addItem(allReferencedCols);\r
+ }\r
+ int heapOnlyColRefItem = -1;\r
+ if (heapOnlyReferencedCols != null)\r
+ {\r
+ heapOnlyColRefItem = acb.addItem(heapOnlyReferencedCols);\r
+ }\r
+\r
+ /* Create the ReferencedColumnsDescriptorImpl which tells which columns\r
+ * come from the index.\r
+ */\r
+ int indexColMapItem = acb.addItem(new ReferencedColumnsDescriptorImpl(getIndexColMapping()));\r
+ long heapConglomNumber = baseCD.getConglomerateNumber();\r
+ StaticCompiledOpenConglomInfo scoci = getLanguageConnectionContext().\r
+ getTransactionCompile().\r
+ getStaticCompiledConglomInfo(heapConglomNumber);\r
+\r
+ acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+ mb.push(heapConglomNumber);\r
+ mb.push(acb.addItem(scoci));\r
+ source.generate(acb, mb);\r
+ \r
+ mb.upCast(ClassName.NoPutResultSet);\r
+\r
+ resultColumns.generateHolder(acb, mb, heapReferencedCols, indexReferencedCols);\r
+ mb.push(resultSetNumber);\r
+ mb.push(source.getBaseTableName());\r
+ mb.push(heapColRefItem);\r
+\r
+ mb.push(allColRefItem);\r
+ mb.push(heapOnlyColRefItem);\r
+\r
+ mb.push(indexColMapItem);\r
+\r
+ // if there is no restriction, we just want to pass null.\r
+ if (restriction == null)\r
+ {\r
+ mb.pushNull(ClassName.GeneratedMethod);\r
+ }\r
+ else\r
+ {\r
+ // this sets up the method and the static field.\r
+ // generates:\r
+ // Object userExprFun { }\r
+ MethodBuilder userExprFun = acb.newUserExprFun();\r
+\r
+ // restriction knows it is returning its value;\r
+ \r
+ /* generates:\r
+ * return <restriction.generate(acb)>;\r
+ * and adds it to userExprFun\r
+ * NOTE: The explicit cast to DataValueDescriptor is required\r
+ * since the restriction may simply be a boolean column or subquery\r
+ * which returns a boolean. For example:\r
+ * where booleanColumn\r
+ */\r
+ restriction.generate(acb, userExprFun);\r
+ userExprFun.methodReturn();\r
+\r
+ // we are done modifying userExprFun, complete it.\r
+ userExprFun.complete();\r
+\r
+ // restriction is used in the final result set as an access of the new static\r
+ // field holding a reference to this new method.\r
+ // generates:\r
+ // ActivationClass.userExprFun\r
+ // which is the static field that "points" to the userExprFun\r
+ // that evaluates the where clause.\r
+ acb.pushMethodReference(mb, userExprFun);\r
+ }\r
+\r
+ mb.push(forUpdate);\r
+ mb.push(costEstimate.rowCount());\r
+ mb.push(costEstimate.getEstimatedCost());\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getIndexRowToBaseRowResultSet",\r
+ ClassName.NoPutResultSet, 14);\r
+\r
+ /* The IndexRowToBaseRowResultSet generator is what we return */\r
+\r
+ /*\r
+ ** Remember if this result set is the cursor target table, so we\r
+ ** can know which table to use when doing positioned update and delete.\r
+ */\r
+ if (cursorTargetTable)\r
+ {\r
+ acb.rememberCursorTarget(mb);\r
+ }\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 source.isOneRowResultSet();\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the underlying FBT is for NOT EXISTS.\r
+ *\r
+ * @return Whether or not the underlying FBT is for NOT EXISTS.\r
+ */\r
+ public boolean isNotExists()\r
+ {\r
+ return source.isNotExists();\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
+ source.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 source.updateTargetLockMode();\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#adjustForSortElimination\r
+ */\r
+ void adjustForSortElimination()\r
+ {\r
+ /* NOTE: We use a different method to tell a FBT that\r
+ * it cannot do a bulk fetch as the ordering issues are\r
+ * specific to a FBT being under an IRTBR as opposed to a\r
+ * FBT being under a PRN, etc.\r
+ */\r
+ source.disableBulkFetch();\r
+ }\r
+\r
+ /**\r
+ * @see ResultSetNode#adjustForSortElimination\r
+ */\r
+ void adjustForSortElimination(RequiredRowOrdering rowOrdering)\r
+ throws StandardException\r
+ {\r
+ /* rowOrdering is not important to this specific node, so\r
+ * just call the no-arg version of the method.\r
+ */\r
+ adjustForSortElimination();\r
+\r
+ /* Now pass the rowOrdering down to source, which may\r
+ * need to do additional work. DERBY-3279.\r
+ */\r
+ source.adjustForSortElimination(rowOrdering);\r
+ }\r
+\r
+ /** \r
+ * Fill in the column mapping for those columns coming from the index.\r
+ *\r
+ * @return The int[] with the mapping.\r
+ */\r
+ private int[] getIndexColMapping()\r
+ {\r
+ int rclSize = resultColumns.size();\r
+ int[] indexColMapping = new int[rclSize];\r
+\r
+ for (int index = 0; index < rclSize; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) resultColumns.elementAt(index);\r
+ if (indexReferencedCols != null && rc.getExpression() instanceof VirtualColumnNode)\r
+ {\r
+ // Column is coming from index\r
+ VirtualColumnNode vcn = (VirtualColumnNode) rc.getExpression();\r
+ indexColMapping[index] =\r
+ vcn.getSourceColumn().getVirtualColumnId() - 1;\r
+ }\r
+ else\r
+ {\r
+ // Column is not coming from index\r
+ indexColMapping[index] = -1;\r
+ }\r
+ }\r
+\r
+ return indexColMapping;\r
+ }\r
+\r
+}\r