001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.VTIResultSet
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.loader.ClassFactory;
025: import org.apache.derby.iapi.services.loader.ClassInspector;
026:
027: import org.apache.derby.iapi.services.monitor.Monitor;
028:
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030:
031: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
032: import org.apache.derby.iapi.services.stream.InfoStreams;
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.sql.Activation;
039: import org.apache.derby.iapi.sql.ResultDescription;
040: import org.apache.derby.iapi.types.DataValueDescriptor;
041: import org.apache.derby.iapi.sql.execute.ExecutionContext;
042:
043: import org.apache.derby.iapi.store.access.Qualifier;
044:
045: import org.apache.derby.iapi.error.StandardException;
046:
047: import org.apache.derby.iapi.services.loader.GeneratedMethod;
048:
049: import org.apache.derby.iapi.types.RowLocation;
050: import org.apache.derby.iapi.reference.SQLState;
051:
052: import org.apache.derby.iapi.services.io.FormatableBitSet;
053: import org.apache.derby.iapi.services.io.FormatableHashtable;
054:
055: import org.apache.derby.vti.DeferModification;
056: import org.apache.derby.vti.IFastPath;
057: import org.apache.derby.vti.VTIEnvironment;
058:
059: import java.sql.PreparedStatement;
060: import java.sql.ResultSet;
061: import java.sql.SQLException;
062: import java.sql.ResultSetMetaData;
063:
064: /**
065: */
066: class VTIResultSet extends NoPutResultSetImpl implements
067: CursorResultSet, VTIEnvironment {
068:
069: /* Run time statistics variables */
070: public int rowsReturned;
071: public String javaClassName;
072:
073: private boolean next;
074: private ClassInspector classInspector;
075: private GeneratedMethod row;
076: private GeneratedMethod constructor;
077: private PreparedStatement userPS;
078: private ResultSet userVTI;
079: private ExecRow allocatedRow;
080: private FormatableBitSet referencedColumns;
081: private boolean version2;
082: private boolean reuseablePs;
083: private boolean isTarget;
084: private FormatableHashtable compileTimeConstants;
085: private int ctcNumber;
086:
087: private boolean pushedProjection;
088: private IFastPath fastPath;
089:
090: private Qualifier[][] pushedQualifiers;
091:
092: private boolean[] runtimeNullableColumn;
093:
094: /**
095: Specified isolation level of SELECT (scan). If not set or
096: not application, it will be set to ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL
097: */
098: private int scanIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;
099:
100: //
101: // class interface
102: //
103: VTIResultSet(Activation activation, GeneratedMethod row,
104: int resultSetNumber, GeneratedMethod constructor,
105: String javaClassName, Qualifier[][] pushedQualifiers,
106: int erdNumber, boolean version2, boolean reuseablePs,
107: int ctcNumber, boolean isTarget, int scanIsolationLevel,
108: double optimizerEstimatedRowCount,
109: double optimizerEstimatedCost) throws StandardException {
110: super (activation, resultSetNumber, optimizerEstimatedRowCount,
111: optimizerEstimatedCost);
112: this .row = row;
113: this .constructor = constructor;
114: this .javaClassName = javaClassName;
115: this .version2 = version2;
116: this .reuseablePs = reuseablePs;
117: this .isTarget = isTarget;
118: this .pushedQualifiers = pushedQualifiers;
119: this .scanIsolationLevel = scanIsolationLevel;
120:
121: if (erdNumber != -1) {
122: this .referencedColumns = (FormatableBitSet) (activation
123: .getPreparedStatement().getSavedObject(erdNumber));
124: }
125:
126: this .ctcNumber = ctcNumber;
127: compileTimeConstants = (FormatableHashtable) (activation
128: .getPreparedStatement().getSavedObject(ctcNumber));
129:
130: constructorTime += getElapsedMillis(beginTime);
131: }
132:
133: //
134: // ResultSet interface (leftover from NoPutResultSet)
135: //
136:
137: /**
138: * Sets state to 'open'.
139: *
140: * @exception StandardException thrown if activation closed.
141: */
142: public void openCore() throws StandardException {
143: beginTime = getCurrentTimeMillis();
144: if (SanityManager.DEBUG)
145: SanityManager.ASSERT(!isOpen, "VTIResultSet already open");
146:
147: isOpen = true;
148: numOpens++;
149:
150: /* We need to Instantiate the user's ResultSet on the each open since
151: * there is no way to close and then reopen a java.sql.ResultSet.
152: * For Version 2 VTIs, we may be able to skip instantiated their
153: * PreparedStatement here.
154: */
155: try {
156: if (version2) {
157: userPS = (PreparedStatement) constructor
158: .invoke(activation);
159:
160: if (userPS instanceof org.apache.derby.vti.Pushable) {
161: org.apache.derby.vti.Pushable p = (org.apache.derby.vti.Pushable) userPS;
162: if (referencedColumns != null) {
163: pushedProjection = p.pushProjection(this ,
164: getProjectedColList());
165: }
166: }
167:
168: if (userPS instanceof org.apache.derby.vti.IQualifyable) {
169: org.apache.derby.vti.IQualifyable q = (org.apache.derby.vti.IQualifyable) userPS;
170:
171: q.setQualifiers(this , pushedQualifiers);
172: }
173: fastPath = userPS instanceof IFastPath ? (IFastPath) userPS
174: : null;
175:
176: if (isTarget
177: && userPS instanceof DeferModification
178: && activation.getConstantAction() instanceof UpdatableVTIConstantAction) {
179: UpdatableVTIConstantAction constants = (UpdatableVTIConstantAction) activation
180: .getConstantAction();
181: ((DeferModification) userPS)
182: .modificationNotify(
183: constants.statementType,
184: constants.deferred);
185: }
186:
187: if ((fastPath != null) && fastPath.executeAsFastPath())
188: ;
189: else
190: userVTI = userPS.executeQuery();
191:
192: /* Save off the target VTI */
193: if (isTarget) {
194: activation.setTargetVTI(userVTI);
195: }
196:
197: } else {
198: userVTI = (ResultSet) constructor.invoke(activation);
199: }
200:
201: // Set up the nullablity of the runtime columns, may be delayed
202: setNullableColumnList();
203: } catch (Throwable t) {
204: throw StandardException.unexpectedUserException(t);
205: }
206:
207: openTime += getElapsedMillis(beginTime);
208: }
209:
210: private boolean[] setNullableColumnList() throws SQLException {
211:
212: if (runtimeNullableColumn != null)
213: return runtimeNullableColumn;
214:
215: if (userVTI == null)
216: return null;
217:
218: ResultSetMetaData rsmd = userVTI.getMetaData();
219: boolean[] nullableColumn = new boolean[rsmd.getColumnCount() + 1];
220: for (int i = 1; i < nullableColumn.length; i++) {
221: nullableColumn[i] = rsmd.isNullable(i) != ResultSetMetaData.columnNoNulls;
222: }
223:
224: return runtimeNullableColumn = nullableColumn;
225: }
226:
227: /**
228: * If the VTI is a version2 vti that does not
229: * need to be instantiated multiple times then
230: * we simply close the current ResultSet and
231: * create a new one via a call to
232: * PreparedStatement.executeQuery().
233: *
234: * @see NoPutResultSet#openCore
235: * @exception StandardException thrown if cursor finished.
236: */
237: public void reopenCore() throws StandardException {
238: if (reuseablePs) {
239: /* close the user ResultSet.
240: */
241: if (userVTI != null) {
242: try {
243: userVTI.close();
244: userVTI = userPS.executeQuery();
245:
246: /* Save off the target VTI */
247: if (isTarget) {
248: activation.setTargetVTI(userVTI);
249: }
250: } catch (SQLException se) {
251: throw StandardException.unexpectedUserException(se);
252: }
253: }
254: } else {
255: close();
256: openCore();
257: }
258: }
259:
260: /**
261: * If open and not returned yet, returns the row
262: * after plugging the parameters into the expressions.
263: *
264: * @exception StandardException thrown on failure.
265: */
266: public ExecRow getNextRowCore() throws StandardException {
267: ExecRow result = null;
268:
269: beginTime = getCurrentTimeMillis();
270:
271: if (isOpen) {
272: try {
273: if ((userVTI == null) && (fastPath != null)) {
274: result = getAllocatedRow();
275: int action = fastPath.nextRow(result.getRowArray());
276: if (action == IFastPath.GOT_ROW)
277: ;
278: else if (action == IFastPath.SCAN_COMPLETED)
279: result = null;
280: else if (action == IFastPath.NEED_RS) {
281: userVTI = userPS.executeQuery();
282: }
283: }
284: if ((userVTI != null)) {
285: if (!userVTI.next()) {
286: if (null != fastPath)
287: fastPath.rowsDone();
288: result = null;
289: } else {
290: // Get the cached row and fill it up
291: result = getAllocatedRow();
292: populateFromResultSet(result);
293: if (fastPath != null)
294: fastPath.currentRow(userVTI, result
295: .getRowArray());
296: }
297: }
298: } catch (Throwable t) {
299: throw StandardException.unexpectedUserException(t);
300: }
301:
302: }
303:
304: setCurrentRow(result);
305: if (result != null) {
306: rowsReturned++;
307: rowsSeen++;
308: }
309:
310: nextTime += getElapsedMillis(beginTime);
311: return result;
312: }
313:
314: /**
315: * @see org.apache.derby.iapi.sql.ResultSet#close
316: *
317: * @exception StandardException thrown on error
318: */
319: public void close() throws StandardException {
320: beginTime = getCurrentTimeMillis();
321: if (isOpen) {
322:
323: // we don't want to keep around a pointer to the
324: // row ... so it can be thrown away.
325: // REVISIT: does this need to be in a finally
326: // block, to ensure that it is executed?
327: clearCurrentRow();
328: next = false;
329:
330: /* close the user ResultSet. We have to eat any exception here
331: * since our close() method cannot throw an exception.
332: */
333: if (userVTI != null) {
334: try {
335: userVTI.close();
336: } catch (SQLException se) {
337: throw StandardException.unexpectedUserException(se);
338: } finally {
339: userVTI = null;
340: }
341: }
342: if ((userPS != null) && !reuseablePs) {
343: try {
344: userPS.close();
345: } catch (SQLException se) {
346: throw StandardException.unexpectedUserException(se);
347: } finally {
348: userPS = null;
349: }
350: }
351: super .close();
352: } else if (SanityManager.DEBUG)
353: SanityManager.DEBUG("CloseRepeatInfo",
354: "Close of VTIResultSet repeated");
355:
356: closeTime += getElapsedMillis(beginTime);
357: }
358:
359: public void finish() throws StandardException {
360:
361: // for a reusablePS it will be closed by the activation
362: // when it is closed.
363: if ((userPS != null) && !reuseablePs) {
364: try {
365: userPS.close();
366: userPS = null;
367: } catch (SQLException se) {
368: throw StandardException.unexpectedUserException(se);
369: }
370: }
371:
372: finishAndRTS();
373:
374: }
375:
376: /**
377: * Return the total amount of time spent in this ResultSet
378: *
379: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
380: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
381: *
382: * @return long The total amount of time spent (in milliseconds).
383: */
384: public long getTimeSpent(int type) {
385: long totTime = constructorTime + openTime + nextTime
386: + closeTime;
387: return totTime;
388: }
389:
390: //
391: // CursorResultSet interface
392: //
393:
394: /**
395: * This is not operating against a stored table,
396: * so it has no row location to report.
397: *
398: * @see CursorResultSet
399: *
400: * @return a null.
401: */
402: public RowLocation getRowLocation() {
403: if (SanityManager.DEBUG)
404: SanityManager
405: .THROWASSERT("RowResultSet used in positioned update/delete");
406: return null;
407: }
408:
409: /**
410: * This is not used in positioned update and delete,
411: * so just return a null.
412: *
413: * @see CursorResultSet
414: *
415: * @return a null.
416: */
417: public ExecRow getCurrentRow() {
418: if (SanityManager.DEBUG)
419: SanityManager
420: .THROWASSERT("RowResultSet used in positioned update/delete");
421: return null;
422: }
423:
424: // Class implementation
425:
426: /**
427: * Return the GeneratedMethod for instantiating the VTI.
428: *
429: * @return The GeneratedMethod for instantiating the VTI.
430: */
431: GeneratedMethod getVTIConstructor() {
432: return constructor;
433: }
434:
435: boolean isReuseablePs() {
436: return reuseablePs;
437: }
438:
439: /**
440: * Cache the ExecRow for this result set.
441: *
442: * @return The cached ExecRow for this ResultSet
443: *
444: * @exception StandardException thrown on failure.
445: */
446: private ExecRow getAllocatedRow() throws StandardException {
447: if (allocatedRow == null) {
448: allocatedRow = (ExecRow) row.invoke(activation);
449: }
450:
451: return allocatedRow;
452: }
453:
454: private int[] getProjectedColList() {
455:
456: FormatableBitSet refs = referencedColumns;
457: int size = refs.size();
458: int arrayLen = 0;
459: for (int i = 0; i < size; i++) {
460: if (refs.isSet(i))
461: arrayLen++;
462: }
463:
464: int[] colList = new int[arrayLen];
465: int offset = 0;
466: for (int i = 0; i < size; i++) {
467: if (refs.isSet(i))
468: colList[offset++] = i + 1;
469: }
470:
471: return colList;
472: }
473:
474: /**
475: * @exception StandardException thrown on failure to open
476: */
477: public void populateFromResultSet(ExecRow row)
478: throws StandardException {
479: try {
480: boolean[] nullableColumn = setNullableColumnList();
481: DataValueDescriptor[] columns = row.getRowArray();
482: // ExecRows are 0-based, ResultSets are 1-based
483: int rsColNumber = 1;
484: for (int index = 0; index < columns.length; index++) {
485: // Skip over unreferenced columns
486: if (referencedColumns != null
487: && (!referencedColumns.get(index))) {
488: if (!pushedProjection)
489: rsColNumber++;
490:
491: continue;
492: }
493:
494: columns[index].setValueFromResultSet(userVTI,
495: rsColNumber,
496: /* last parameter is whether or
497: * not the column is nullable
498: */
499: nullableColumn[rsColNumber]);
500: rsColNumber++;
501: }
502:
503: } catch (StandardException se) {
504: throw se;
505: } catch (Throwable t) {
506: throw StandardException.unexpectedUserException(t);
507: }
508: }
509:
510: public final int getScanIsolationLevel() {
511: return scanIsolationLevel;
512: }
513:
514: /*
515: ** VTIEnvironment
516: */
517: public final boolean isCompileTime() {
518: return false;
519: }
520:
521: public final String getOriginalSQL() {
522: return activation.getPreparedStatement().getSource();
523: }
524:
525: public final int getStatementIsolationLevel() {
526: return ExecutionContext.CS_TO_JDBC_ISOLATION_LEVEL_MAP[getScanIsolationLevel()];
527: }
528:
529: public final void setSharedState(String key,
530: java.io.Serializable value) {
531: if (key == null)
532: return;
533:
534: if (compileTimeConstants == null) {
535:
536: Object[] savedObjects = activation.getPreparedStatement()
537: .getSavedObjects();
538:
539: synchronized (savedObjects) {
540:
541: compileTimeConstants = (FormatableHashtable) savedObjects[ctcNumber];
542: if (compileTimeConstants == null) {
543: compileTimeConstants = new FormatableHashtable();
544: savedObjects[ctcNumber] = compileTimeConstants;
545: }
546: }
547: }
548:
549: if (value == null)
550: compileTimeConstants.remove(key);
551: else
552: compileTimeConstants.put(key, value);
553:
554: }
555:
556: public Object getSharedState(String key) {
557: if ((key == null) || (compileTimeConstants == null))
558: return null;
559:
560: return compileTimeConstants.get(key);
561: }
562: }
|