001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.UnionResultSet
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.execute;
023:
024: import org.apache.derby.iapi.services.monitor.Monitor;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
029: import org.apache.derby.iapi.services.stream.InfoStreams;
030:
031: import org.apache.derby.iapi.sql.execute.CursorResultSet;
032: import org.apache.derby.iapi.sql.execute.ExecRow;
033: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
034:
035: import org.apache.derby.iapi.error.StandardException;
036:
037: import org.apache.derby.iapi.sql.Activation;
038: import org.apache.derby.iapi.sql.ResultSet;
039: import org.apache.derby.iapi.sql.ResultDescription;
040:
041: import org.apache.derby.iapi.services.loader.GeneratedMethod;
042:
043: import org.apache.derby.iapi.types.RowLocation;
044:
045: /**
046: * Takes two result sets and returns their union (all).
047: * (Any duplicate elimination is performed above this ResultSet.)
048: *
049: * @author ames
050: */
051: class UnionResultSet extends NoPutResultSetImpl implements
052: CursorResultSet {
053:
054: /* Run time statistics variables */
055: public int rowsSeenLeft;
056: public int rowsSeenRight;
057: public int rowsReturned;
058:
059: private int whichSource = 1; // 1 or 2, == the source we are currently on.
060: private int source1FinalRowCount = -1;
061:
062: // these are set in the constructor and never altered
063: public NoPutResultSet source1;
064: public NoPutResultSet source2;
065:
066: //
067: // class interface
068: //
069: /*
070: * implementation alternative: an array of sources,
071: * using whichSource to index into the current source.
072: */
073: public UnionResultSet(NoPutResultSet source1,
074: NoPutResultSet source2, Activation activation,
075: int resultSetNumber, double optimizerEstimatedRowCount,
076: double optimizerEstimatedCost) {
077:
078: super (activation, resultSetNumber, optimizerEstimatedRowCount,
079: optimizerEstimatedCost);
080: this .source1 = source1;
081: this .source2 = source2;
082: constructorTime += getElapsedMillis(beginTime);
083: }
084:
085: //
086: // ResultSet interface (leftover from NoPutResultSet)
087: //
088:
089: /**
090: * Returns the description of the first source.
091: * Assumes the compiler ensured both sources
092: * had the same description.
093: */
094: public ResultDescription getResultDescription() {
095: return source1.getResultDescription();
096: }
097:
098: /**
099: * open the first source.
100: * @exception StandardException thrown on failure
101: */
102: public void openCore() throws StandardException {
103: beginTime = getCurrentTimeMillis();
104: if (SanityManager.DEBUG)
105: SanityManager
106: .ASSERT(!isOpen, "UnionResultSet already open");
107:
108: isOpen = true;
109: source1.openCore();
110: numOpens++;
111:
112: openTime += getElapsedMillis(beginTime);
113: }
114:
115: /**
116: * If there are rows still on the first source, return the
117: * next one; otherwise, switch to the second source and
118: * return a row from there.
119: * @exception StandardException thrown on failure
120: */
121: public ExecRow getNextRowCore() throws StandardException {
122: ExecRow result = null;
123:
124: beginTime = getCurrentTimeMillis();
125: if (isOpen) {
126: switch (whichSource) {
127: case 1:
128: result = source1.getNextRowCore();
129: if (result == (ExecRow) null) {
130: //source1FinalRowCount = source1.rowCount();
131: source1.close();
132: whichSource = 2;
133: source2.openCore();
134: result = source2.getNextRowCore();
135: if (result != null) {
136: rowsSeenRight++;
137: }
138: } else {
139: rowsSeenLeft++;
140: }
141: break;
142: case 2:
143: result = source2.getNextRowCore();
144: if (result != null) {
145: rowsSeenRight++;
146: }
147: break;
148: default:
149: if (SanityManager.DEBUG)
150: SanityManager
151: .THROWASSERT("Bad source number in union");
152: break;
153: }
154: }
155:
156: currentRow = result;
157: setCurrentRow(result);
158: if (result != null) {
159: rowsReturned++;
160: }
161:
162: nextTime += getElapsedMillis(beginTime);
163: return result;
164: }
165:
166: /**
167: * If the result set has been opened,
168: * close the currently open source.
169: *
170: * @exception StandardException thrown on error
171: */
172: public void close() throws StandardException {
173: beginTime = getCurrentTimeMillis();
174: if (isOpen) {
175:
176: // we don't want to keep around a pointer to the
177: // row ... so it can be thrown away.
178: // REVISIT: does this need to be in a finally
179: // block, to ensure that it is executed?
180: clearCurrentRow();
181:
182: switch (whichSource) {
183: case 1:
184: source1.close();
185: break;
186: case 2:
187: source2.close();
188: source1FinalRowCount = -1;
189: whichSource = 1;
190: break;
191: default:
192: if (SanityManager.DEBUG)
193: SanityManager
194: .THROWASSERT("Bad source number in union");
195: break;
196: }
197:
198: super .close();
199: } else if (SanityManager.DEBUG)
200: SanityManager.DEBUG("CloseRepeatInfo",
201: "Close of UnionResultSet repeated");
202:
203: closeTime += getElapsedMillis(beginTime);
204: }
205:
206: public void finish() throws StandardException {
207: source1.finish();
208: source2.finish();
209: finishAndRTS();
210: }
211:
212: /**
213: * Return the total amount of time spent in this ResultSet
214: *
215: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
216: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
217: *
218: * @return long The total amount of time spent (in milliseconds).
219: */
220: public long getTimeSpent(int type) {
221: long totTime = constructorTime + openTime + nextTime
222: + closeTime;
223:
224: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
225: return totTime
226: - source1.getTimeSpent(ENTIRE_RESULTSET_TREE)
227: - source2.getTimeSpent(ENTIRE_RESULTSET_TREE);
228: } else {
229: return totTime;
230: }
231: }
232:
233: //
234: // CursorResultSet interface
235: //
236:
237: /**
238: A union has a single underlying row at a time, although
239: from one of several sources.
240:
241: @see CursorResultSet
242:
243: @return the row location of the current cursor row.
244: @exception StandardException thrown on failure
245: */
246: public RowLocation getRowLocation() throws StandardException {
247: switch (whichSource) {
248: case 1:
249: if (SanityManager.DEBUG)
250: SanityManager.ASSERT(
251: source1 instanceof CursorResultSet,
252: "source not CursorResultSet");
253: return ((CursorResultSet) source1).getRowLocation();
254: case 2:
255: if (SanityManager.DEBUG)
256: SanityManager.ASSERT(
257: source2 instanceof CursorResultSet,
258: "source2 not CursorResultSet");
259: return ((CursorResultSet) source2).getRowLocation();
260: default:
261: if (SanityManager.DEBUG)
262: SanityManager.THROWASSERT("Bad source number in union");
263: return null;
264: }
265: }
266:
267: /**
268: A union has a single underlying row at a time, although
269: from one of several sources.
270:
271: @see CursorResultSet
272:
273: @return the current row.
274: * @exception StandardException thrown on failure.
275: */
276: /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
277: * once there is such a method. (currentRow is redundant)
278: */
279: public ExecRow getCurrentRow() throws StandardException {
280: ExecRow result = null;
281:
282: if (SanityManager.DEBUG) {
283: SanityManager.ASSERT(isOpen, "TSRS expected to be open");
284: if (!(whichSource == 1 || whichSource == 2)) {
285: SanityManager
286: .THROWASSERT("whichSource expected to be 1 or 2, not "
287: + whichSource);
288: }
289: }
290:
291: switch (whichSource) {
292: case 1:
293: result = ((CursorResultSet) source1).getCurrentRow();
294: break;
295:
296: case 2:
297: result = ((CursorResultSet) source2).getCurrentRow();
298: break;
299: }
300:
301: currentRow = result;
302: setCurrentRow(result);
303: return result;
304: }
305:
306: }
|