001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.NestedLoopLeftOuterJoinResultSet
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: * plus the rows from the left joined with a null row from
047: * the right when there is no matching row in the right
048: * result set.
049: */
050: class NestedLoopLeftOuterJoinResultSet extends NestedLoopJoinResultSet {
051: protected GeneratedMethod emptyRowFun;
052: /* Was this originally a right outer join? */
053: private boolean wasRightOuterJoin;
054:
055: /* Have we found a matching row from the right yet? */
056: private boolean matchRight = false;
057: private boolean returnedEmptyRight = false;
058: private ExecRow rightEmptyRow = null;
059:
060: public int emptyRightRowsReturned = 0;
061:
062: //
063: // ResultSet interface (leftover from NoPutResultSet)
064: //
065:
066: /**
067: * Return the requested values computed
068: * from the next row (if any) for which
069: * the restriction evaluates to true.
070: * <p>
071: * restriction parameters
072: * are evaluated for each row.
073: *
074: * @exception StandardException Thrown on error
075: * @exception StandardException ResultSetNotOpen thrown if closed
076: * @return the next row in the join result
077: */
078: public ExecRow getNextRowCore() throws StandardException {
079: ExecRow result = null;
080: boolean haveRow = false;
081: boolean restrict = false;
082: DataValueDescriptor restrictBoolean;
083:
084: beginTime = getCurrentTimeMillis();
085: if (!isOpen)
086: throw StandardException.newException(
087: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
088:
089: /* Close right and advance left if we found no match
090: * on right on last next().
091: */
092: if (returnedEmptyRight) {
093: /* Current scan on right is exhausted. Need to close old scan
094: * and open new scan with new "parameters". openRight will
095: * reopen the scan.
096: */
097: leftRow = leftResultSet.getNextRowCore();
098: if (leftRow == null) {
099: closeRight();
100: } else {
101: rowsSeenLeft++;
102: openRight();
103: }
104: returnedEmptyRight = false;
105: }
106:
107: while (leftRow != null && !haveRow) {
108: rightRow = rightResultSet.getNextRowCore();
109:
110: if (rightRow == null) {
111: /* If we haven't found a match on the right, then
112: * we join the left with a row of nulls from the
113: * right.
114: */
115: if (!matchRight) {
116: haveRow = true;
117: returnedEmptyRight = true;
118: if (rightEmptyRow == null) {
119: rightEmptyRow = (ExecRow) emptyRowFun
120: .invoke(activation);
121: }
122:
123: getMergedRow(leftRow, rightEmptyRow);
124: emptyRightRowsReturned++;
125: continue;
126: }
127:
128: /* Current scan on right is exhausted. Need to close old scan
129: * and open new scan with new "parameters". openRight()
130: * will reopen the scan.
131: */
132: matchRight = false;
133: leftRow = leftResultSet.getNextRowCore();
134: if (leftRow == null) {
135: closeRight();
136: } else {
137: rowsSeenLeft++;
138: openRight();
139: }
140: } else {
141: rowsSeenRight++;
142:
143: if (restriction != null) {
144: restrictBoolean = (DataValueDescriptor) restriction
145: .invoke(activation);
146:
147: // if the result is null, we make it false --
148: // so the row won't be returned.
149: restrict = (!restrictBoolean.isNull())
150: && restrictBoolean.getBoolean();
151:
152: if (!restrict) {
153: /* Update the run time statistics */
154: rowsFiltered++;
155: continue;
156: }
157: }
158:
159: matchRight = true;
160:
161: getMergedRow(leftRow, rightRow);
162: haveRow = true;
163: }
164: }
165:
166: /* Do we have a row to return? */
167: if (haveRow) {
168: result = mergedRow;
169: setCurrentRow(mergedRow);
170: rowsReturned++;
171: } else {
172: clearCurrentRow();
173: }
174:
175: nextTime += getElapsedMillis(beginTime);
176: return result;
177: }
178:
179: protected void getMergedRow(ExecRow leftRow, ExecRow rightRow)
180: throws StandardException {
181: int colInCtr;
182: int colOutCtr;
183: int leftNumCols;
184: int rightNumCols;
185:
186: /* Reverse left and right for return of row if this was originally
187: * a right outer join. (Result columns ordered according to
188: * original query.)
189: */
190: if (wasRightOuterJoin) {
191: ExecRow tmp;
192:
193: tmp = leftRow;
194: leftRow = rightRow;
195: rightRow = tmp;
196: leftNumCols = this .rightNumCols;
197: rightNumCols = this .leftNumCols;
198: } else {
199: leftNumCols = this .leftNumCols;
200: rightNumCols = this .rightNumCols;
201: }
202:
203: /* Merge the rows, doing just in time allocation for mergedRow.
204: * (By convention, left Row is to left of right Row.)
205: */
206: if (mergedRow == null) {
207: mergedRow = getExecutionFactory().getValueRow(
208: leftNumCols + rightNumCols);
209: }
210:
211: for (colInCtr = 1, colOutCtr = 1; colInCtr <= leftNumCols; colInCtr++, colOutCtr++) {
212: mergedRow.setColumn(colOutCtr, leftRow.getColumn(colInCtr));
213: }
214: for (colInCtr = 1; colInCtr <= rightNumCols; colInCtr++, colOutCtr++) {
215: mergedRow
216: .setColumn(colOutCtr, rightRow.getColumn(colInCtr));
217: }
218: }
219:
220: /**
221: * Clear any private state that changes during scans.
222: * This includes things like the last row seen, etc.
223: * THis does not include immutable things that are
224: * typically set up in the constructor.
225: * <p>
226: * This method is called on open()/close() and reopen()
227: * <p>
228: * WARNING: this should be implemented in every sub
229: * class and it should always call super.clearScanState().
230: */
231: void clearScanState() {
232: matchRight = false;
233: returnedEmptyRight = false;
234: rightEmptyRow = null;
235: emptyRightRowsReturned = 0;
236: super .clearScanState();
237: }
238:
239: /*
240: * class interface
241: *
242: */
243: NestedLoopLeftOuterJoinResultSet(NoPutResultSet leftResultSet,
244: int leftNumCols, NoPutResultSet rightResultSet,
245: int rightNumCols, Activation activation,
246: GeneratedMethod restriction, int resultSetNumber,
247: GeneratedMethod emptyRowFun, boolean wasRightOuterJoin,
248: boolean oneRowRightSide, boolean notExistsRightSide,
249: double optimizerEstimatedRowCount,
250: double optimizerEstimatedCost,
251: String userSuppliedOptimizerOverrides) {
252: super(leftResultSet, leftNumCols, rightResultSet, rightNumCols,
253: activation, restriction, resultSetNumber,
254: oneRowRightSide, notExistsRightSide,
255: optimizerEstimatedRowCount, optimizerEstimatedCost,
256: userSuppliedOptimizerOverrides);
257: this.emptyRowFun = emptyRowFun;
258: this.wasRightOuterJoin = wasRightOuterJoin;
259: }
260: }
|