001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.NestedLoopJoinResultSet
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.error.StandardException;
032: import org.apache.derby.iapi.sql.Activation;
033: import org.apache.derby.iapi.sql.ResultSet;
034: import org.apache.derby.iapi.types.DataValueDescriptor;
035: import org.apache.derby.iapi.reference.SQLState;
036:
037: import org.apache.derby.iapi.sql.execute.ExecRow;
038: import org.apache.derby.iapi.sql.execute.ExecutionContext;
039: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
040:
041: import org.apache.derby.iapi.services.loader.GeneratedMethod;
042:
043: /**
044: * Takes 2 NoPutResultSets and a join filter and returns
045: * the join's rows satisfying the filter as a result set.
046: */
047: class NestedLoopJoinResultSet extends JoinResultSet {
048: private boolean returnedRowMatchingRightSide = false;
049: private ExecRow rightTemplate;
050:
051: //
052: // ResultSet interface (leftover from NoPutResultSet)
053: //
054:
055: /**
056: * Clear any private state that changes during scans.
057: * This includes things like the last row seen, etc.
058: * THis does not include immutable things that are
059: * typically set up in the constructor.
060: * <p>
061: * This method is called on open()/close() and reopen()
062: * <p>
063: * WARNING: this should be implemented in every sub
064: * class and it should always call super.clearScanState().
065: */
066: void clearScanState() {
067: returnedRowMatchingRightSide = false;
068: super .clearScanState();
069: }
070:
071: /**
072: * Return the requested values computed
073: * from the next row (if any) for which
074: * the restriction evaluates to true.
075: * <p>
076: * restriction parameters
077: * are evaluated for each row.
078: *
079: * @exception StandardException Thrown on error
080: * @exception StandardException ResultSetNotOpen thrown if closed
081: * @return the next row in the join result
082: */
083: public ExecRow getNextRowCore() throws StandardException {
084: ExecRow result = null;
085: boolean haveRow = false;
086: boolean restrict = false;
087: int colInCtr;
088: int colOutCtr;
089: DataValueDescriptor restrictBoolean;
090:
091: beginTime = getCurrentTimeMillis();
092: if (!isOpen)
093: throw StandardException.newException(
094: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
095:
096: /* If we have a row from the left side and the right side is not open,
097: * then we get an error on the previous next, either on the next on
098: * the left or the open on the right. So, we do a next on the left
099: * and then open the right if that succeeds.
100: */
101: if (!isRightOpen && leftRow != null) {
102: leftRow = leftResultSet.getNextRowCore();
103: if (leftRow == null) {
104: closeRight();
105: } else {
106: rowsSeenLeft++;
107: openRight();
108: }
109: }
110:
111: while (leftRow != null && !haveRow) {
112: if (oneRowRightSide && returnedRowMatchingRightSide) {
113: rightRow = null;
114: returnedRowMatchingRightSide = false;
115: } else {
116: rightRow = rightResultSet.getNextRowCore();
117:
118: /* If this is a NOT EXISTS join, we just need to reverse the logic
119: * of EXISTS join. To make the implementation simple, we create a
120: * right side template, which is never really needed. (beetle 5173)
121: */
122: if (notExistsRightSide) {
123: if (rightRow == null) //none satisfied
124: rightRow = rightTemplate; //then we are
125: else
126: rightRow = null;
127: }
128:
129: returnedRowMatchingRightSide = (rightRow != null);
130: }
131:
132: if (rightRow == null) {
133: /* Current scan on right is exhausted. Need to close old scan
134: * and open new scan with new "parameters". openRight()
135: * will reopen if already open.
136: */
137: leftRow = leftResultSet.getNextRowCore();
138: if (leftRow == null) {
139: closeRight();
140: } else {
141: rowsSeenLeft++;
142: openRight();
143: }
144: } else {
145: rowsSeenRight++;
146:
147: if (restriction != null) {
148: restrictBoolean = (DataValueDescriptor) restriction
149: .invoke(activation);
150:
151: // if the result is null, we make it false --
152: // so the row won't be returned.
153: restrict = (!restrictBoolean.isNull())
154: && restrictBoolean.getBoolean();
155:
156: if (!restrict) {
157: /* Update the run time statistics */
158: rowsFiltered++;
159: continue;
160: }
161: }
162:
163: /* Merge the rows, doing just in time allocation for mergedRow.
164: * (By convention, left Row is to left of right Row.)
165: */
166: if (mergedRow == null) {
167: mergedRow = getExecutionFactory().getValueRow(
168: leftNumCols + rightNumCols);
169: }
170:
171: for (colInCtr = 1, colOutCtr = 1; colInCtr <= leftNumCols; colInCtr++, colOutCtr++) {
172: mergedRow.setColumn(colOutCtr, leftRow
173: .getColumn(colInCtr));
174: }
175: if (!notExistsRightSide) {
176: for (colInCtr = 1; colInCtr <= rightNumCols; colInCtr++, colOutCtr++) {
177: mergedRow.setColumn(colOutCtr, rightRow
178: .getColumn(colInCtr));
179: }
180: }
181:
182: setCurrentRow(mergedRow);
183: haveRow = true;
184: }
185: }
186:
187: /* Do we have a row to return? */
188: if (haveRow) {
189: result = mergedRow;
190: rowsReturned++;
191: } else {
192: clearCurrentRow();
193: }
194:
195: nextTime += getElapsedMillis(beginTime);
196: return result;
197: }
198:
199: /**
200: * If the result set has been opened,
201: * close the open scan.
202: *
203: * @exception StandardException thrown on error
204: */
205: public void close() throws StandardException {
206: if (isOpen) {
207: beginTime = getCurrentTimeMillis();
208:
209: // we don't want to keep around a pointer to the
210: // row ... so it can be thrown away.
211: // REVISIT: does this need to be in a finally
212: // block, to ensure that it is executed?
213: clearCurrentRow();
214:
215: super .close();
216: returnedRowMatchingRightSide = false;
217: closeTime += getElapsedMillis(beginTime);
218: }
219:
220: }
221:
222: /**
223: * Return the total amount of time spent in this ResultSet
224: *
225: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
226: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
227: *
228: * @return long The total amount of time spent (in milliseconds).
229: */
230: public long getTimeSpent(int type) {
231: long totTime = constructorTime + openTime + nextTime
232: + closeTime;
233:
234: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
235: return totTime
236: - leftResultSet.getTimeSpent(ENTIRE_RESULTSET_TREE)
237: - rightResultSet
238: .getTimeSpent(ENTIRE_RESULTSET_TREE);
239: } else {
240: return totTime;
241: }
242: }
243:
244: /*
245: * class interface
246: *
247: */
248: NestedLoopJoinResultSet(NoPutResultSet leftResultSet,
249: int leftNumCols, NoPutResultSet rightResultSet,
250: int rightNumCols, Activation activation,
251: GeneratedMethod restriction, int resultSetNumber,
252: boolean oneRowRightSide, boolean notExistsRightSide,
253: double optimizerEstimatedRowCount,
254: double optimizerEstimatedCost,
255: String userSuppliedOptimizerOverrides) {
256: super(leftResultSet, leftNumCols, rightResultSet, rightNumCols,
257: activation, restriction, resultSetNumber,
258: oneRowRightSide, notExistsRightSide,
259: optimizerEstimatedRowCount, optimizerEstimatedCost,
260: userSuppliedOptimizerOverrides);
261: if (notExistsRightSide)
262: rightTemplate = getExecutionFactory().getValueRow(
263: rightNumCols);
264: }
265: }
|