--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.UnionResultSet\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.services.monitor.Monitor;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.services.stream.InfoStreams;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+\r
+/**\r
+ * Takes two result sets and returns their union (all).\r
+ * (Any duplicate elimination is performed above this ResultSet.)\r
+ *\r
+ */\r
+class UnionResultSet extends NoPutResultSetImpl\r
+ implements CursorResultSet {\r
+\r
+ /* Run time statistics variables */\r
+ public int rowsSeenLeft;\r
+ public int rowsSeenRight;\r
+ public int rowsReturned;\r
+\r
+ private int whichSource = 1; // 1 or 2, == the source we are currently on.\r
+ private int source1FinalRowCount = -1;\r
+\r
+ // these are set in the constructor and never altered\r
+ public NoPutResultSet source1;\r
+ public NoPutResultSet source2;\r
+\r
+\r
+ //\r
+ // class interface\r
+ //\r
+ /*\r
+ * implementation alternative: an array of sources,\r
+ * using whichSource to index into the current source.\r
+ */\r
+ public UnionResultSet(NoPutResultSet source1, NoPutResultSet source2, \r
+ Activation activation, \r
+ int resultSetNumber, \r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost) \r
+ {\r
+ \r
+ super(activation, resultSetNumber, \r
+ optimizerEstimatedRowCount, optimizerEstimatedCost);\r
+ this.source1 = source1;\r
+ this.source2 = source2;\r
+ constructorTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ //\r
+ // ResultSet interface (leftover from NoPutResultSet)\r
+ //\r
+\r
+ /**\r
+ * Returns the description of the first source.\r
+ * Assumes the compiler ensured both sources\r
+ * had the same description.\r
+ */\r
+ public ResultDescription getResultDescription() {\r
+ return source1.getResultDescription();\r
+ }\r
+\r
+ /**\r
+ * open the first source.\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void openCore() throws StandardException \r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT( ! isOpen, "UnionResultSet already open");\r
+\r
+ isOpen = true;\r
+ source1.openCore();\r
+ numOpens++;\r
+\r
+ openTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ /**\r
+ * If there are rows still on the first source, return the\r
+ * next one; otherwise, switch to the second source and\r
+ * return a row from there.\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException {\r
+ ExecRow result = null;\r
+\r
+ beginTime = getCurrentTimeMillis();\r
+ if ( isOpen ) {\r
+ switch (whichSource) {\r
+ case 1 : result = source1.getNextRowCore();\r
+ if ( result == (ExecRow) null ) {\r
+ //source1FinalRowCount = source1.rowCount();\r
+ source1.close();\r
+ whichSource = 2;\r
+ source2.openCore();\r
+ result = source2.getNextRowCore();\r
+ if (result != null)\r
+ {\r
+ rowsSeenRight++;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ rowsSeenLeft++;\r
+ }\r
+ break;\r
+ case 2 : result = source2.getNextRowCore();\r
+ if (result != null)\r
+ {\r
+ rowsSeenRight++;\r
+ }\r
+ break;\r
+ default: \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT( "Bad source number in union" );\r
+ break;\r
+ }\r
+ }\r
+\r
+ currentRow = result;\r
+ setCurrentRow(result);\r
+ if (result != null)\r
+ {\r
+ rowsReturned++;\r
+ }\r
+\r
+ nextTime += getElapsedMillis(beginTime);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * If the result set has been opened,\r
+ * close the currently open source.\r
+ *\r
+ * @exception StandardException thrown on error\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if ( isOpen ) {\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
+ switch (whichSource) {\r
+ case 1 : source1.close();\r
+ break;\r
+ case 2 : source2.close();\r
+ source1FinalRowCount = -1;\r
+ whichSource = 1;\r
+ break;\r
+ default: \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT( "Bad source number in union" );\r
+ break;\r
+ }\r
+\r
+ super.close();\r
+ }\r
+ else\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.DEBUG("CloseRepeatInfo","Close of UnionResultSet repeated");\r
+\r
+ closeTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ public void finish() throws StandardException\r
+ {\r
+ source1.finish();\r
+ source2.finish();\r
+ finishAndRTS();\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 - source1.getTimeSpent(ENTIRE_RESULTSET_TREE) -\r
+ source2.getTimeSpent(ENTIRE_RESULTSET_TREE);\r
+ }\r
+ else\r
+ {\r
+ return totTime;\r
+ }\r
+ }\r
+\r
+ //\r
+ // CursorResultSet interface\r
+ //\r
+\r
+ /**\r
+ A union has a single underlying row at a time, although\r
+ from one of several sources.\r
+ \r
+ @see CursorResultSet\r
+ \r
+ @return the row location of the current cursor row.\r
+ @exception StandardException thrown on failure\r
+ */\r
+ public RowLocation getRowLocation() throws StandardException {\r
+ switch (whichSource) {\r
+ case 1 : \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(source1 instanceof CursorResultSet, "source not CursorResultSet");\r
+ return ((CursorResultSet)source1).getRowLocation();\r
+ case 2 : \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(source2 instanceof CursorResultSet, "source2 not CursorResultSet");\r
+ return ((CursorResultSet)source2).getRowLocation();\r
+ default: \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT( "Bad source number in union" );\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ A union has a single underlying row at a time, although\r
+ from one of several sources.\r
+ \r
+ @see CursorResultSet\r
+ \r
+ @return the current row.\r
+ * @exception StandardException thrown on failure.\r
+ */\r
+ /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),\r
+ * once there is such a method. (currentRow is redundant)\r
+ */\r
+ public ExecRow getCurrentRow() throws StandardException{\r
+ ExecRow result = null;\r
+\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(isOpen, "TSRS expected to be open");\r
+ if (!(whichSource == 1 || whichSource == 2))\r
+ {\r
+ SanityManager.THROWASSERT("whichSource expected to be 1 or 2, not " \r
+ + whichSource);\r
+ }\r
+ }\r
+\r
+ switch (whichSource) \r
+ {\r
+ case 1: \r
+ result = ((CursorResultSet) source1).getCurrentRow();\r
+ break;\r
+\r
+ case 2: \r
+ result = ((CursorResultSet) source2).getCurrentRow();\r
+ break;\r
+ }\r
+\r
+\r
+ currentRow = result;\r
+ setCurrentRow(result);\r
+ return result;\r
+ }\r
+\r
+}\r