001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.MergeJoinResultSet
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.NoPutResultSet;
039:
040: import org.apache.derby.iapi.services.loader.GeneratedMethod;
041:
042: /////////////////////////////////////////////
043: // WARNING: THIS HAS NOT BEEN TESTED (OR USED)
044: // YET, SO USE AT YOUR OWN RISK
045: /////////////////////////////////////////////
046:
047: /**
048: * Merge two result sets. The left result set (the outer
049: * result set) MUST be unique for this to work correctly.
050: *
051: */
052: class MergeJoinResultSet extends JoinResultSet {
053: private static final int GREATER_THAN = 1;
054: private static final int EQUAL = 0;
055: private static final int LESS_THAN = -1;
056:
057: private GeneratedMethod leftGreaterThanRight;
058:
059: /**
060: * Create a MergeJoinResultSet
061: *
062: * @param leftResultSet the left (outer) result set
063: * @param leftNumCols columns in left row
064: * @param rightResultSet the right (outer) result set
065: * @param rightNumCols columns in right row
066: * @param activation activation
067: * @param leftGreaterThanRight a generated method that is used to
068: * ascertain whether the row from the left result set is
069: * greater than the row from the right result set. returns
070: * 1,0,or -1 to indicate greater than, equal, or less than,
071: * respectively
072: * @param restriction generated method for additional qualification
073: * @param resultSetNumber the result set number
074: * @param oneRowRightSide ignored
075: * @param optimizerEstimatedRowCount self-explanatory
076: * @param optimizerEstimatedCost self-explanatory
077: */
078: MergeJoinResultSet(NoPutResultSet leftResultSet, int leftNumCols,
079: NoPutResultSet rightResultSet, int rightNumCols,
080: Activation activation,
081: GeneratedMethod leftGreaterThanRight,
082: GeneratedMethod restriction, int resultSetNumber,
083: boolean oneRowRightSide, boolean notExistsRightSide,
084: double optimizerEstimatedRowCount,
085: double optimizerEstimatedCost) {
086: super (leftResultSet, leftNumCols, rightResultSet, rightNumCols,
087: activation, restriction, resultSetNumber,
088: oneRowRightSide, notExistsRightSide,
089: optimizerEstimatedRowCount, optimizerEstimatedCost,
090: null);
091:
092: this .leftGreaterThanRight = leftGreaterThanRight;
093: }
094:
095: //////////////////////////////////////////////////////////////////////
096: //
097: // ResultSet interface (leftover from NoPutResultSet)
098: //
099: //////////////////////////////////////////////////////////////////////
100: /**
101: * Return the requested values computed
102: * from the next row (if any) for which
103: * the restriction evaluates to true.
104: * <p>
105: * restriction parameters
106: * are evaluated for each row.
107: *
108: * @exception StandardException Thrown on error
109: * @exception StandardException ResultSetNotOpen thrown if closed
110: * @return the next row in the join result
111: */
112: public ExecRow getNextRowCore() throws StandardException {
113: beginTime = getCurrentTimeMillis();
114: if (!isOpen)
115: throw StandardException.newException(
116: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
117:
118: if (!isRightOpen) {
119: openRight();
120: }
121:
122: int compareResult;
123:
124: /*
125: ** For each row in the outer table
126: */
127: while (leftRow != null) {
128: /*
129: ** If outer > inner, then go to the
130: ** next row in the inner table
131: */
132: while ((compareResult = ((Integer) leftGreaterThanRight
133: .invoke(activation)).intValue()) == GREATER_THAN) {
134: rightRow = rightResultSet.getNextRowCore();
135: rowsSeenRight++;
136:
137: /*
138: ** If there are no more rows in the right
139: ** result set, then done.
140: */
141: if (rightRow == null) {
142: clearCurrentRow();
143: return (ExecRow) null;
144: }
145: }
146:
147: /*
148: ** If they match and the restriction passes,
149: ** then return the row.
150: */
151: if ((compareResult == EQUAL) && restrictionIsTrue()) {
152: ExecRow returnRow = getReturnRow(leftRow, rightRow);
153:
154: /*
155: ** Move the left scan up one for the next
156: ** getNextRowCore() call.
157: */
158: leftRow = leftResultSet.getNextRowCore();
159:
160: return returnRow;
161: }
162:
163: /*
164: ** Next row left
165: */
166: leftRow = leftResultSet.getNextRowCore();
167: rowsSeenLeft++;
168: }
169:
170: clearCurrentRow();
171: return (ExecRow) null;
172: }
173:
174: /**
175: * If the result set has been opened,
176: * close the open scan.
177: *
178: * @exception StandardException thrown on error
179: */
180: public void close() throws StandardException {
181: beginTime = getCurrentTimeMillis();
182: if (SanityManager.DEBUG)
183: SanityManager.ASSERT(isOpen, "MergeJoinResultSet not open");
184:
185: if (isOpen) {
186:
187: // we don't want to keep around a pointer to the
188: // row ... so it can be thrown away.
189: // REVISIT: does this need to be in a finally
190: // block, to ensure that it is executed?
191: clearCurrentRow();
192:
193: super .close();
194: }
195:
196: closeTime += getElapsedMillis(beginTime);
197: }
198:
199: /**
200: * Return the total amount of time spent in this ResultSet
201: *
202: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
203: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
204: *
205: * @return long The total amount of time spent (in milliseconds).
206: */
207: public long getTimeSpent(int type) {
208: long totTime = constructorTime + openTime + nextTime
209: + closeTime;
210:
211: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
212: return totTime
213: - leftResultSet.getTimeSpent(ENTIRE_RESULTSET_TREE)
214: - rightResultSet
215: .getTimeSpent(ENTIRE_RESULTSET_TREE);
216: } else {
217: return totTime;
218: }
219: }
220:
221: //////////////////////////////////////////////////////////////////
222: //
223: // SERVILE METHODS
224: //
225: //////////////////////////////////////////////////////////////////
226: private ExecRow getReturnRow(ExecRow leftRow, ExecRow rightRow)
227: throws StandardException {
228: int colInCtr;
229: int colOutCtr;
230:
231: /* Merge the rows, doing just in time allocation for mergedRow.
232: * (By convention, left Row is to left of right Row.)
233: */
234: if (mergedRow == null) {
235: mergedRow = getExecutionFactory().getValueRow(
236: leftNumCols + rightNumCols);
237: }
238:
239: for (colInCtr = 1, colOutCtr = 1; colInCtr <= leftNumCols; colInCtr++, colOutCtr++) {
240: mergedRow.setColumn(colOutCtr, leftRow.getColumn(colInCtr));
241: }
242:
243: for (colInCtr = 1; colInCtr <= rightNumCols; colInCtr++, colOutCtr++) {
244: mergedRow
245: .setColumn(colOutCtr, rightRow.getColumn(colInCtr));
246: }
247:
248: setCurrentRow(mergedRow);
249: rowsReturned++;
250: nextTime += getElapsedMillis(beginTime);
251:
252: return mergedRow;
253: }
254:
255: private boolean restrictionIsTrue() throws StandardException {
256: if (restriction != null) {
257: DataValueDescriptor restrictBoolean = (DataValueDescriptor) restriction
258: .invoke(activation);
259:
260: /*
261: ** if the result is null, we make it false --
262: ** so the row won't be returned.
263: */
264: if (restrictBoolean.isNull()
265: || !restrictBoolean.getBoolean()) {
266: /* Update the run time statistics */
267: rowsFiltered++;
268: return false;
269: }
270: }
271: return true;
272: }
273:
274: }
|