--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.NestedLoopJoinResultSet\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.execute;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+\r
+/**\r
+ * Takes 2 NoPutResultSets and a join filter and returns\r
+ * the join's rows satisfying the filter as a result set.\r
+ */\r
+class NestedLoopJoinResultSet extends JoinResultSet\r
+{\r
+ private boolean returnedRowMatchingRightSide = false;\r
+ private ExecRow rightTemplate;\r
+\r
+ //\r
+ // ResultSet interface (leftover from NoPutResultSet)\r
+ //\r
+\r
+ /**\r
+ * Clear any private state that changes during scans.\r
+ * This includes things like the last row seen, etc.\r
+ * THis does not include immutable things that are\r
+ * typically set up in the constructor.\r
+ * <p>\r
+ * This method is called on open()/close() and reopen()\r
+ * <p>\r
+ * WARNING: this should be implemented in every sub\r
+ * class and it should always call super.clearScanState().\r
+ */\r
+ void clearScanState()\r
+ {\r
+ returnedRowMatchingRightSide = false;\r
+ super.clearScanState();\r
+ }\r
+\r
+ /**\r
+ * Return the requested values computed\r
+ * from the next row (if any) for which\r
+ * the restriction evaluates to true.\r
+ * <p>\r
+ * restriction parameters\r
+ * are evaluated for each row.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ * @exception StandardException ResultSetNotOpen thrown if closed\r
+ * @return the next row in the join result\r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException\r
+ {\r
+ ExecRow result = null;\r
+ boolean haveRow = false;\r
+ boolean restrict = false;\r
+ int colInCtr;\r
+ int colOutCtr;\r
+ DataValueDescriptor restrictBoolean;\r
+\r
+ beginTime = getCurrentTimeMillis();\r
+ if (! isOpen)\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");\r
+\r
+ /* If we have a row from the left side and the right side is not open, \r
+ * then we get an error on the previous next, either on the next on\r
+ * the left or the open on the right. So, we do a next on the left\r
+ * and then open the right if that succeeds.\r
+ */\r
+ if (! isRightOpen && leftRow != null)\r
+ { \r
+ leftRow = leftResultSet.getNextRowCore();\r
+ if (leftRow == null)\r
+ {\r
+ closeRight();\r
+ }\r
+ else\r
+ {\r
+ rowsSeenLeft++;\r
+ openRight();\r
+ }\r
+ }\r
+\r
+ while (leftRow != null && !haveRow)\r
+ {\r
+ if (oneRowRightSide && returnedRowMatchingRightSide)\r
+ {\r
+ rightRow = null;\r
+ returnedRowMatchingRightSide = false;\r
+ }\r
+ else\r
+ {\r
+ rightRow = rightResultSet.getNextRowCore();\r
+\r
+ /* If this is a NOT EXISTS join, we just need to reverse the logic\r
+ * of EXISTS join. To make the implementation simple, we create a\r
+ * right side template, which is never really needed. (beetle 5173)\r
+ */\r
+ if (notExistsRightSide)\r
+ {\r
+ if (rightRow == null) //none satisfied\r
+ rightRow = rightTemplate; //then we are\r
+ else\r
+ rightRow = null;\r
+ }\r
+\r
+ returnedRowMatchingRightSide = (rightRow != null);\r
+ }\r
+\r
+ if (rightRow == null)\r
+ {\r
+ /* Current scan on right is exhausted. Need to close old scan \r
+ * and open new scan with new "parameters". openRight() \r
+ * will reopen if already open.\r
+ */\r
+ leftRow = leftResultSet.getNextRowCore();\r
+ if (leftRow == null)\r
+ {\r
+ closeRight();\r
+ }\r
+ else\r
+ {\r
+ rowsSeenLeft++;\r
+ openRight();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ rowsSeenRight++;\r
+\r
+ if (restriction != null)\r
+ {\r
+ restrictBoolean =\r
+ (DataValueDescriptor) restriction.invoke(activation);\r
+\r
+ // if the result is null, we make it false --\r
+ // so the row won't be returned.\r
+ restrict = (! restrictBoolean.isNull()) &&\r
+ restrictBoolean.getBoolean();\r
+\r
+ if (! restrict)\r
+ {\r
+ /* Update the run time statistics */\r
+ rowsFiltered++;\r
+ continue;\r
+ }\r
+ }\r
+\r
+ /* Merge the rows, doing just in time allocation for mergedRow.\r
+ * (By convention, left Row is to left of right Row.)\r
+ */\r
+ if (mergedRow == null)\r
+ {\r
+ mergedRow = getExecutionFactory().getValueRow(leftNumCols + rightNumCols);\r
+ }\r
+\r
+ for (colInCtr = 1, colOutCtr = 1; colInCtr <= leftNumCols;\r
+ colInCtr++, colOutCtr++)\r
+ {\r
+ mergedRow.setColumn(colOutCtr, \r
+ leftRow.getColumn(colInCtr));\r
+ }\r
+ if (! notExistsRightSide)\r
+ {\r
+ for (colInCtr = 1; colInCtr <= rightNumCols; \r
+ colInCtr++, colOutCtr++)\r
+ {\r
+ mergedRow.setColumn(colOutCtr, \r
+ rightRow.getColumn(colInCtr));\r
+ }\r
+ }\r
+\r
+ setCurrentRow(mergedRow);\r
+ haveRow = true;\r
+ }\r
+ }\r
+\r
+ /* Do we have a row to return? */\r
+ if (haveRow)\r
+ {\r
+ result = mergedRow;\r
+ rowsReturned++;\r
+ }\r
+ else\r
+ {\r
+ clearCurrentRow();\r
+ }\r
+\r
+ nextTime += getElapsedMillis(beginTime);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * If the result set has been opened,\r
+ * close the open scan.\r
+ *\r
+ * @exception StandardException thrown on error\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ if ( isOpen )\r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+\r
+ // we don't want to keep around a pointer to the\r
+ // row ... so it can be thrown away.\r
+ // REVISIT: does this need to be in a finally\r
+ // block, to ensure that it is executed?\r
+ clearCurrentRow();\r
+\r
+ super.close();\r
+ returnedRowMatchingRightSide = false;\r
+ closeTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Return the total amount of time spent in this ResultSet\r
+ *\r
+ * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet\r
+ * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.\r
+ *\r
+ * @return long The total amount of time spent (in milliseconds).\r
+ */\r
+ public long getTimeSpent(int type)\r
+ {\r
+ long totTime = constructorTime + openTime + nextTime + closeTime;\r
+\r
+ if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)\r
+ {\r
+ return totTime - leftResultSet.getTimeSpent(ENTIRE_RESULTSET_TREE) -\r
+ rightResultSet.getTimeSpent(ENTIRE_RESULTSET_TREE);\r
+ }\r
+ else\r
+ {\r
+ return totTime;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * class interface\r
+ *\r
+ */\r
+ NestedLoopJoinResultSet(NoPutResultSet leftResultSet,\r
+ int leftNumCols,\r
+ NoPutResultSet rightResultSet,\r
+ int rightNumCols,\r
+ Activation activation,\r
+ GeneratedMethod restriction,\r
+ int resultSetNumber,\r
+ boolean oneRowRightSide,\r
+ boolean notExistsRightSide,\r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost,\r
+ String userSuppliedOptimizerOverrides)\r
+ {\r
+ super(leftResultSet, leftNumCols, rightResultSet, rightNumCols,\r
+ activation, restriction, resultSetNumber, \r
+ oneRowRightSide, notExistsRightSide, optimizerEstimatedRowCount, \r
+ optimizerEstimatedCost, userSuppliedOptimizerOverrides);\r
+ if (notExistsRightSide)\r
+ rightTemplate = getExecutionFactory().getValueRow(rightNumCols);\r
+ }\r
+}\r