001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.SetOpResultSet
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.error.StandardException;
025:
026: import org.apache.derby.iapi.services.loader.GeneratedMethod;
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028:
029: import org.apache.derby.iapi.sql.Activation;
030: import org.apache.derby.iapi.sql.ResultDescription;
031:
032: import org.apache.derby.iapi.sql.execute.CursorResultSet;
033: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
034: import org.apache.derby.iapi.sql.execute.ExecRow;
035: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
036:
037: import org.apache.derby.iapi.types.DataValueDescriptor;
038: import org.apache.derby.iapi.types.Orderable;
039: import org.apache.derby.iapi.types.RowLocation;
040:
041: import org.apache.derby.impl.sql.compile.IntersectOrExceptNode;
042:
043: /**
044: * Takes the result set produced by an ordered UNION ALL of two tagged result sets and produces
045: * the INTERSECT or EXCEPT of the two input result sets. This also projects out the tag, the last column
046: * of the input rows.
047: */
048: class SetOpResultSet extends NoPutResultSetImpl implements
049: CursorResultSet {
050: private final NoPutResultSet leftSource;
051: private final NoPutResultSet rightSource;
052: private final Activation activation;
053: private final int opType;
054: private final boolean all;
055: private final int resultSetNumber;
056: private DataValueDescriptor[] prevCols; /* Used to remove duplicates in the EXCEPT DISTINCT case.
057: * It is equal to the previously output columns.
058: */
059: private int rightDuplicateCount; // Number of duplicates of the current row from the right input
060: private ExecRow leftInputRow;
061: private ExecRow rightInputRow;
062:
063: private final int[] intermediateOrderByColumns;
064: private final int[] intermediateOrderByDirection;
065:
066: /* Run time statistics variables */
067: private int rowsSeenLeft;
068: private int rowsSeenRight;
069: private int rowsReturned;
070:
071: SetOpResultSet(NoPutResultSet leftSource,
072: NoPutResultSet rightSource, Activation activation,
073: int resultSetNumber, long optimizerEstimatedRowCount,
074: double optimizerEstimatedCost, int opType, boolean all,
075: int intermediateOrderByColumnsSavedObject,
076: int intermediateOrderByDirectionSavedObject) {
077: super (activation, resultSetNumber, optimizerEstimatedRowCount,
078: optimizerEstimatedCost);
079: this .leftSource = leftSource;
080: this .rightSource = rightSource;
081: this .activation = activation;
082: this .resultSetNumber = resultSetNumber;
083: this .opType = opType;
084: this .all = all;
085:
086: ExecPreparedStatement eps = activation.getPreparedStatement();
087: intermediateOrderByColumns = (int[]) eps
088: .getSavedObject(intermediateOrderByColumnsSavedObject);
089: intermediateOrderByDirection = (int[]) eps
090: .getSavedObject(intermediateOrderByDirectionSavedObject);
091: constructorTime += getElapsedMillis(beginTime);
092: }
093:
094: /**
095: * open the first source.
096: * @exception StandardException thrown on failure
097: */
098: public void openCore() throws StandardException {
099: beginTime = getCurrentTimeMillis();
100: if (SanityManager.DEBUG)
101: SanityManager
102: .ASSERT(!isOpen, "SetOpResultSet already open");
103:
104: isOpen = true;
105: leftSource.openCore();
106: rightSource.openCore();
107: rightInputRow = rightSource.getNextRowCore();
108: if (rightInputRow != null) {
109: rowsSeenRight++;
110: }
111:
112: numOpens++;
113:
114: openTime += getElapsedMillis(beginTime);
115: } // end of openCore
116:
117: /**
118: * @return the next row of the intersect or except, null if there is none
119: * @exception StandardException thrown on failure
120: */
121: public ExecRow getNextRowCore() throws StandardException {
122: beginTime = getCurrentTimeMillis();
123: if (isOpen) {
124: while ((leftInputRow = leftSource.getNextRowCore()) != null) {
125: rowsSeenLeft++;
126:
127: DataValueDescriptor[] leftColumns = leftInputRow
128: .getRowArray();
129: if (!all) {
130: if (isDuplicate(leftColumns))
131: continue; // Get the next left row
132: prevCols = leftInputRow.getRowArrayClone();
133: }
134: int compare = 0;
135: // Advance the right until there are no more right rows or leftRow <= rightRow
136: while (rightInputRow != null
137: && (compare = compare(leftColumns,
138: rightInputRow.getRowArray())) > 0) {
139: rightInputRow = rightSource.getNextRowCore();
140: if (rightInputRow != null) {
141: rowsSeenRight++;
142: }
143: }
144:
145: if (rightInputRow == null || compare < 0) {
146: // The left row is not in the right source.
147: if (opType == IntersectOrExceptNode.EXCEPT_OP)
148: // Output this row
149: break;
150: } else {
151: // The left and right rows are the same
152: if (SanityManager.DEBUG)
153: SanityManager
154: .ASSERT(rightInputRow != null
155: && compare == 0,
156: "Intersect/Except execution has gotten confused.");
157: if (all) {
158: // Just advance the right input by one row.
159: rightInputRow = rightSource.getNextRowCore();
160: if (rightInputRow != null) {
161: rowsSeenRight++;
162: }
163: }
164:
165: // If !all then we will skip past duplicates on the left at the top of this loop,
166: // which will then force us to skip past any right duplicates.
167: if (opType == IntersectOrExceptNode.INTERSECT_OP)
168: break; // output this row
169:
170: // opType == IntersectOrExceptNode.EXCEPT_OP
171: // This row should not be ouput
172: }
173: }
174: }
175: currentRow = leftInputRow;
176: setCurrentRow(currentRow);
177:
178: if (currentRow != null) {
179: rowsReturned++;
180: }
181:
182: nextTime += getElapsedMillis(beginTime);
183: return currentRow;
184: } // end of getNextRowCore
185:
186: private void advanceRightPastDuplicates(
187: DataValueDescriptor[] leftColumns) throws StandardException {
188: while ((rightInputRow = rightSource.getNextRowCore()) != null) {
189: rowsSeenRight++;
190:
191: if (compare(leftColumns, rightInputRow.getRowArray()) == 0)
192: continue;
193: }
194: } // end of advanceRightPastDuplicates
195:
196: private int compare(DataValueDescriptor[] leftCols,
197: DataValueDescriptor[] rightCols) throws StandardException {
198: for (int i = 0; i < intermediateOrderByColumns.length; i++) {
199: int colIdx = intermediateOrderByColumns[i];
200: if (leftCols[colIdx].compare(Orderable.ORDER_OP_LESSTHAN,
201: rightCols[colIdx], true, // nulls sort high
202: false))
203: return -1 * intermediateOrderByDirection[i];
204: if (!leftCols[colIdx].compare(Orderable.ORDER_OP_EQUALS,
205: rightCols[colIdx], true, // nulls sort high
206: false))
207: return intermediateOrderByDirection[i];
208: }
209: return 0;
210: } // end of compare
211:
212: private boolean isDuplicate(DataValueDescriptor[] curColumns)
213: throws StandardException {
214: if (prevCols == null)
215: return false;
216: /* Note that intermediateOrderByColumns.length can be less than prevCols.length if we know that a
217: * subset of the columns is a unique key. In that case we only need to look at the unique key.
218: */
219: for (int i = 0; i < intermediateOrderByColumns.length; i++) {
220: int colIdx = intermediateOrderByColumns[i];
221: if (!curColumns[colIdx].compare(Orderable.ORDER_OP_EQUALS,
222: prevCols[colIdx], true, false))
223: return false;
224: }
225: return true;
226: }
227:
228: public ExecRow getCurrentRow() {
229: return currentRow;
230: }
231:
232: /**
233: * If the result set has been opened,
234: * close the currently open source.
235: *
236: * @exception StandardException thrown on error
237: */
238: public void close() throws StandardException {
239: beginTime = getCurrentTimeMillis();
240: if (isOpen) {
241: clearCurrentRow();
242: prevCols = null;
243: leftSource.close();
244: rightSource.close();
245: super .close();
246: } else if (SanityManager.DEBUG)
247: SanityManager.DEBUG("CloseRepeatInfo",
248: "Close of SetOpResultSet repeated");
249:
250: closeTime += getElapsedMillis(beginTime);
251: } // end of close
252:
253: public void finish() throws StandardException {
254: leftSource.finish();
255: rightSource.finish();
256: finishAndRTS();
257: }
258:
259: /**
260: * Return the total amount of time spent in this ResultSet
261: *
262: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
263: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
264: *
265: * @return long The total amount of time spent (in milliseconds).
266: */
267: public long getTimeSpent(int type) {
268: long totTime = constructorTime + openTime + nextTime
269: + closeTime;
270:
271: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
272: return totTime
273: - leftSource.getTimeSpent(ENTIRE_RESULTSET_TREE)
274: - rightSource.getTimeSpent(ENTIRE_RESULTSET_TREE);
275: } else {
276: return totTime;
277: }
278: } // end of getTimeSpent
279:
280: /**
281: * @see CursorResultSet
282: *
283: * @return the row location of the current cursor row.
284: * @exception StandardException thrown on failure
285: */
286: public RowLocation getRowLocation() throws StandardException {
287: // RESOLVE: What is the row location of an INTERSECT supposed to be: the location from the
288: // left side, the right side, or null?
289: return ((CursorResultSet) leftSource).getRowLocation();
290: }
291:
292: /**
293: * Return the set operation of this <code>SetOpResultSet</code>
294: *
295: * @return the set operation of this ResultSet, the value is either
296: * <code>IntersectOrExceptNode.INTERSECT_OP</code> for
297: * Intersect operation or <code>IntersectOrExceptNode.EXCEPT_OP
298: * </code> for Except operation
299: *
300: * @see org.apache.derby.impl.sql.compile.IntersectOrExceptNode
301: */
302: public int getOpType() {
303: return opType;
304: }
305:
306: /**
307: * Return the result set number
308: *
309: * @return the result set number
310: */
311: public int getResultSetNumber() {
312: return resultSetNumber;
313: }
314:
315: /**
316: * Return the left source input of this <code>SetOpResultSet</code>
317: *
318: * @return the left source input of this <code>SetOpResultSet</code>
319: * @see org.apache.derby.iapi.sql.execute.NoPutResultSet
320: */
321: public NoPutResultSet getLeftSourceInput() {
322: return leftSource;
323: }
324:
325: /**
326: * Return the right source input of this <code>SetOpResultSet</code>
327: *
328: * @return the right source input of this <code>SetOpResultSet</code>
329: * @see org.apache.derby.iapi.sql.execute.NoPutResultSet
330: */
331: public NoPutResultSet getRightSourceInput() {
332: return rightSource;
333: }
334:
335: /**
336: * Return the number of rows seen on the left source input
337: *
338: * @return the number of rows seen on the left source input
339: */
340: public int getRowsSeenLeft() {
341: return rowsSeenLeft;
342: }
343:
344: /**
345: * Return the number of rows seen on the right source input
346: *
347: * @return the number of rows seen on the right source input
348: */
349: public int getRowsSeenRight() {
350: return rowsSeenRight;
351: }
352:
353: /**
354: * Return the number of rows returned from the result set
355: *
356: * @return the number of rows returned from the result set
357: */
358: public int getRowsReturned() {
359: return rowsReturned;
360: }
361: }
|