001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.ProjectRestrictResultSet
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.conn.LanguageConnectionContext;
032: import org.apache.derby.iapi.sql.conn.StatementContext;
033:
034: import org.apache.derby.iapi.sql.execute.CursorResultSet;
035: import org.apache.derby.iapi.sql.execute.ExecRow;
036: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
037:
038: import org.apache.derby.iapi.types.DataValueDescriptor;
039: import org.apache.derby.iapi.sql.Activation;
040: import org.apache.derby.iapi.sql.ResultSet;
041:
042: import org.apache.derby.iapi.services.loader.GeneratedMethod;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.iapi.types.RowLocation;
047:
048: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
049:
050: /**
051: * Takes a table and a table filter and returns
052: * the table's rows satisfying the filter as a result set.
053: *
054: * @author ames
055: */
056: class ProjectRestrictResultSet extends NoPutResultSetImpl implements
057: CursorResultSet {
058: /* Run time statistics variables */
059: public long restrictionTime;
060: public long projectionTime;
061:
062: // set in constructor and not altered during
063: // life of object.
064: public NoPutResultSet source;
065: public GeneratedMethod constantRestriction;
066: public GeneratedMethod restriction;
067: public boolean doesProjection;
068: private GeneratedMethod projection;
069: private int[] projectMapping;
070: private boolean runTimeStatsOn;
071: private ExecRow mappedResultRow;
072: public boolean reuseResult;
073:
074: private boolean shortCircuitOpen;
075:
076: private ExecRow projRow;
077:
078: //
079: // class interface
080: //
081: ProjectRestrictResultSet(NoPutResultSet s, Activation a,
082: GeneratedMethod r, GeneratedMethod p, int resultSetNumber,
083: GeneratedMethod cr, int mapRefItem, boolean reuseResult,
084: boolean doesProjection, double optimizerEstimatedRowCount,
085: double optimizerEstimatedCost) throws StandardException {
086: super (a, resultSetNumber, optimizerEstimatedRowCount,
087: optimizerEstimatedCost);
088: source = s;
089: // source expected to be non-null, mystery stress test bug
090: // - sometimes get NullPointerException in openCore().
091: if (SanityManager.DEBUG) {
092: SanityManager.ASSERT(source != null,
093: "PRRS(), source expected to be non-null");
094: }
095: restriction = r;
096: projection = p;
097: constantRestriction = cr;
098: projectMapping = ((ReferencedColumnsDescriptorImpl) a
099: .getPreparedStatement().getSavedObject(mapRefItem))
100: .getReferencedColumnPositions();
101: this .reuseResult = reuseResult;
102: this .doesProjection = doesProjection;
103:
104: // Allocate a result row if all of the columns are mapped from the source
105: if (projection == null) {
106: mappedResultRow = activation.getExecutionFactory()
107: .getValueRow(projectMapping.length);
108: }
109:
110: /* Remember whether or not RunTimeStatistics is on */
111: runTimeStatsOn = getLanguageConnectionContext()
112: .getRunTimeStatisticsMode();
113: constructorTime += getElapsedMillis(beginTime);
114: }
115:
116: //
117: // NoPutResultSet interface
118: //
119:
120: /**
121: * open a scan on the table. scan parameters are evaluated
122: * at each open, so there is probably some way of altering
123: * their values...
124: *
125: * @exception StandardException thrown if cursor finished.
126: */
127: public void openCore() throws StandardException {
128: boolean constantEval = true;
129:
130: beginTime = getCurrentTimeMillis();
131:
132: // source expected to be non-null, mystery stress test bug
133: // - sometimes get NullPointerException in openCore().
134: if (SanityManager.DEBUG) {
135: SanityManager
136: .ASSERT(source != null,
137: "PRRS().openCore(), source expected to be non-null");
138: }
139:
140: // REVISIT: through the direct DB API, this needs to be an
141: // error, not an ASSERT; users can open twice. Only through JDBC
142: // is access to open controlled and ensured valid.
143: if (SanityManager.DEBUG)
144: SanityManager.ASSERT(!isOpen,
145: "ProjectRestrictResultSet already open");
146:
147: if (constantRestriction != null) {
148: DataValueDescriptor restrictBoolean;
149: restrictBoolean = (DataValueDescriptor) constantRestriction
150: .invoke(activation);
151:
152: // if the result is null, we make it false --
153: // so the row won't be returned.
154: constantEval = (restrictBoolean == null)
155: || ((!restrictBoolean.isNull()) && restrictBoolean
156: .getBoolean());
157: }
158:
159: if (constantEval) {
160: source.openCore();
161: } else {
162: shortCircuitOpen = true;
163: }
164: isOpen = true;
165:
166: numOpens++;
167:
168: openTime += getElapsedMillis(beginTime);
169: }
170:
171: /**
172: * reopen a scan on the table. scan parameters are evaluated
173: * at each open, so there is probably some way of altering
174: * their values...
175: *
176: * @exception StandardException thrown if cursor finished.
177: */
178: public void reopenCore() throws StandardException {
179: boolean constantEval = true;
180:
181: beginTime = getCurrentTimeMillis();
182:
183: if (SanityManager.DEBUG)
184: SanityManager.ASSERT(isOpen,
185: "ProjectRestrictResultSet not open, cannot reopen");
186:
187: if (constantRestriction != null) {
188: DataValueDescriptor restrictBoolean;
189: restrictBoolean = (DataValueDescriptor) constantRestriction
190: .invoke(activation);
191:
192: // if the result is null, we make it false --
193: // so the row won't be returned.
194: constantEval = (restrictBoolean == null)
195: || ((!restrictBoolean.isNull()) && restrictBoolean
196: .getBoolean());
197: }
198:
199: if (constantEval) {
200: source.reopenCore();
201: } else {
202: shortCircuitOpen = true;
203: }
204: isOpen = true;
205:
206: numOpens++;
207:
208: openTime += getElapsedMillis(beginTime);
209: }
210:
211: /**
212: * Return the requested values computed
213: * from the next row (if any) for which
214: * the restriction evaluates to true.
215: * <p>
216: * restriction and projection parameters
217: * are evaluated for each row.
218: *
219: * @exception StandardException thrown on failure.
220: * @exception StandardException ResultSetNotOpen thrown if not yet open.
221: *
222: * @return the next row in the result
223: */
224: public ExecRow getNextRowCore() throws StandardException {
225:
226: ExecRow candidateRow = null;
227: ExecRow result = null;
228: boolean restrict = false;
229: DataValueDescriptor restrictBoolean;
230: long beginRT = 0;
231:
232: /* Return null if open was short circuited by false constant expression */
233: if (shortCircuitOpen) {
234: return result;
235: }
236:
237: beginTime = getCurrentTimeMillis();
238: do {
239: candidateRow = source.getNextRowCore();
240: if (candidateRow != null) {
241: beginRT = getCurrentTimeMillis();
242: /* If restriction is null, then all rows qualify */
243: if (restriction == null) {
244: restrict = true;
245: } else {
246: setCurrentRow(candidateRow);
247: restrictBoolean = (DataValueDescriptor) restriction
248: .invoke(activation);
249: restrictionTime += getElapsedMillis(beginRT);
250:
251: // if the result is null, we make it false --
252: // so the row won't be returned.
253: restrict = ((!restrictBoolean.isNull()) && restrictBoolean
254: .getBoolean());
255: if (!restrict) {
256: rowsFiltered++;
257: }
258: }
259:
260: /* Update the run time statistics */
261: rowsSeen++;
262: }
263: } while ((candidateRow != null) && (!restrict));
264:
265: if (candidateRow != null) {
266: beginRT = getCurrentTimeMillis();
267:
268: result = doProjection(candidateRow);
269:
270: projectionTime += getElapsedMillis(beginRT);
271: }
272: /* Clear the current row, if null */
273: else {
274: clearCurrentRow();
275: }
276:
277: currentRow = result;
278:
279: if (runTimeStatsOn) {
280: if (!isTopResultSet) {
281: /* This is simply for RunTimeStats */
282: /* We first need to get the subquery tracking array via the StatementContext */
283: StatementContext sc = activation
284: .getLanguageConnectionContext()
285: .getStatementContext();
286: subqueryTrackingArray = sc.getSubqueryTrackingArray();
287: }
288: nextTime += getElapsedMillis(beginTime);
289: }
290: return result;
291: }
292:
293: /**
294: * Return the total amount of time spent in this ResultSet
295: *
296: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
297: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
298: *
299: * @return long The total amount of time spent (in milliseconds).
300: */
301: public long getTimeSpent(int type) {
302: long totTime = constructorTime + openTime + nextTime
303: + closeTime;
304:
305: if (type == CURRENT_RESULTSET_ONLY) {
306: return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);
307: } else {
308: return totTime;
309: }
310: }
311:
312: // ResultSet interface
313:
314: /**
315: * If the result set has been opened,
316: * close the open scan.
317: *
318: * @exception StandardException thrown on error
319: */
320: public void close() throws StandardException {
321: /* Nothing to do if open was short circuited by false constant expression */
322: if (shortCircuitOpen) {
323: isOpen = false;
324: shortCircuitOpen = false;
325: return;
326: }
327:
328: beginTime = getCurrentTimeMillis();
329: if (isOpen) {
330:
331: // we don't want to keep around a pointer to the
332: // row ... so it can be thrown away.
333: // REVISIT: does this need to be in a finally
334: // block, to ensure that it is executed?
335: clearCurrentRow();
336:
337: source.close();
338:
339: super .close();
340: } else if (SanityManager.DEBUG)
341: SanityManager.DEBUG("CloseRepeatInfo",
342: "Close of ProjectRestrictResultSet repeated");
343:
344: closeTime += getElapsedMillis(beginTime);
345: }
346:
347: public void finish() throws StandardException {
348: source.finish();
349: finishAndRTS();
350: }
351:
352: //
353: // CursorResultSet interface
354: //
355:
356: /**
357: * Gets information from its source. We might want
358: * to have this take a CursorResultSet in its constructor some day,
359: * instead of doing a cast here?
360: *
361: * @see CursorResultSet
362: *
363: * @return the row location of the current cursor row.
364: * @exception StandardException thrown on failure.
365: */
366: public RowLocation getRowLocation() throws StandardException {
367: if (SanityManager.DEBUG)
368: SanityManager.ASSERT(source instanceof CursorResultSet,
369: "source is not CursorResultSet");
370: return ((CursorResultSet) source).getRowLocation();
371: }
372:
373: /**
374: * Gets last row returned.
375: *
376: * @see CursorResultSet
377: *
378: * @return the last row returned.
379: * @exception StandardException thrown on failure.
380: */
381: /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
382: * once there is such a method. (currentRow is redundant)
383: */
384: public ExecRow getCurrentRow() throws StandardException {
385: ExecRow candidateRow = null;
386: ExecRow result = null;
387: boolean restrict = false;
388: DataValueDescriptor restrictBoolean;
389:
390: if (SanityManager.DEBUG)
391: SanityManager.ASSERT(isOpen, "PRRS is expected to be open");
392:
393: /* Nothing to do if we're not currently on a row */
394: if (currentRow == null) {
395: return null;
396: }
397:
398: /* Call the child result set to get it's current row.
399: * If no row exists, then return null, else requalify it
400: * before returning.
401: */
402: candidateRow = ((CursorResultSet) source).getCurrentRow();
403: if (candidateRow != null) {
404: setCurrentRow(candidateRow);
405: /* If restriction is null, then all rows qualify */
406: restrictBoolean = (DataValueDescriptor) ((restriction == null) ? null
407: : restriction.invoke(activation));
408:
409: // if the result is null, we make it false --
410: // so the row won't be returned.
411: restrict = (restrictBoolean == null)
412: || ((!restrictBoolean.isNull()) && restrictBoolean
413: .getBoolean());
414: }
415:
416: if (candidateRow != null && restrict) {
417: result = doProjection(candidateRow);
418: }
419:
420: currentRow = result;
421: /* Clear the current row, if null */
422: if (result == null) {
423: clearCurrentRow();
424: }
425:
426: return currentRow;
427: }
428:
429: /**
430: * Do the projection against the source row. Use reflection
431: * where necessary, otherwise get the source column into our
432: * result row.
433: *
434: * @param sourceRow The source row.
435: *
436: * @return The result of the projection
437: *
438: * @exception StandardException thrown on failure.
439: */
440: private ExecRow doProjection(ExecRow sourceRow)
441: throws StandardException {
442: // No need to use reflection if reusing the result
443: if (reuseResult && projRow != null) {
444: return projRow;
445: }
446:
447: ExecRow result;
448:
449: // Use reflection to do as much of projection as required
450: if (projection != null) {
451: result = (ExecRow) projection.invoke(activation);
452: } else {
453: result = mappedResultRow;
454: }
455:
456: // Copy any mapped columns from the source
457: for (int index = 0; index < projectMapping.length; index++) {
458: if (projectMapping[index] != -1) {
459: result.setColumn(index + 1, sourceRow
460: .getColumn(projectMapping[index]));
461: }
462: }
463:
464: /* We need to reSet the current row after doing the projection */
465: setCurrentRow(result);
466:
467: /* Remember the result if reusing it */
468: if (reuseResult) {
469: projRow = result;
470: }
471: return result;
472: }
473:
474: /**
475: * Do the projection against the sourceRow. If the source of the result set
476: * is of type ProjectRestrictResultSet, the projection by that result set
477: * will also be performed.
478: *
479: * @param sourceRow row to be projected
480: *
481: * @return The result of the projection
482: *
483: * @exception StandardException thrown on failure.
484: */
485: public ExecRow doBaseRowProjection(ExecRow sourceRow)
486: throws StandardException {
487: final ExecRow result;
488: if (source instanceof ProjectRestrictResultSet) {
489: ProjectRestrictResultSet prs = (ProjectRestrictResultSet) source;
490: result = prs.doBaseRowProjection(sourceRow);
491: } else {
492: result = sourceRow.getNewNullRow();
493: result.setRowArray(sourceRow.getRowArray());
494: }
495: return doProjection(result);
496: }
497:
498: /**
499: * Get projection mapping array. The array consist of indexes which
500: * maps the column in a row array to another position in the row array.
501: * If the value is projected out of the row, the value is negative.
502: * @return projection mapping array.
503: */
504: public int[] getBaseProjectMapping() {
505: final int[] result;
506: if (source instanceof ProjectRestrictResultSet) {
507: result = new int[projectMapping.length];
508: final ProjectRestrictResultSet prs = (ProjectRestrictResultSet) source;
509: final int[] sourceMap = prs.getBaseProjectMapping();
510: for (int i = 0; i < projectMapping.length; i++) {
511: if (projectMapping[i] > 0) {
512: result[i] = sourceMap[projectMapping[i] - 1];
513: }
514: }
515: } else {
516: result = projectMapping;
517: }
518: return result;
519: }
520:
521: /**
522: * Is this ResultSet or it's source result set for update
523: *
524: * @return Whether or not the result set is for update.
525: */
526: public boolean isForUpdate() {
527: return source.isForUpdate();
528: }
529:
530: /**
531: * @see NoPutResultSet#updateRow
532: */
533: public void updateRow(ExecRow row) throws StandardException {
534: source.updateRow(row);
535: }
536:
537: /**
538: * @see NoPutResultSet#markRowAsDeleted
539: */
540: public void markRowAsDeleted() throws StandardException {
541: source.markRowAsDeleted();
542: }
543:
544: }
|