001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.NoRowsResultSetImpl
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.services.i18n.MessageService;
033:
034: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
035: import org.apache.derby.iapi.sql.conn.StatementContext;
036:
037: import org.apache.derby.iapi.reference.SQLState;
038:
039: import org.apache.derby.iapi.sql.execute.ExecRow;
040: import org.apache.derby.iapi.sql.execute.ExecutionContext;
041: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
042: import org.apache.derby.iapi.sql.execute.ResultSetStatisticsFactory;
043:
044: import org.apache.derby.iapi.sql.Activation;
045: import org.apache.derby.iapi.sql.ResultSet;
046: import org.apache.derby.iapi.sql.ResultDescription;
047: import org.apache.derby.iapi.sql.Row;
048:
049: import org.apache.derby.iapi.services.loader.GeneratedMethod;
050:
051: import org.apache.derby.iapi.types.DataValueDescriptor;
052:
053: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
054: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
055: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
056: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
057:
058: import java.sql.Timestamp;
059: import java.sql.SQLWarning;
060:
061: /**
062: * This implementation of ResultSet
063: * is meant to be overridden by subtypes
064: * in the execution engine. Its primary users
065: * will be DDL, which only need to define a
066: * constructor to create the DDL object being
067: * defined. All other ResultSet operations will
068: * be handled by this superclass -- i.e., nothing
069: * is allowed to be done to a DDL Result Set, since
070: * it has no rows to provide.
071: * <p>
072: * This abstract class does not define the entire ResultSet
073: * interface, but leaves the 'get' half of the interface
074: * for subtypes to implement. It is package-visible only,
075: * with its methods being public for exposure by its subtypes.
076: * <p>
077: *
078: * @author ames
079: */
080: abstract class NoRowsResultSetImpl implements ResultSet {
081: final Activation activation;
082: private boolean dumpedStats;
083: NoPutResultSet[] subqueryTrackingArray;
084:
085: private final boolean statisticsTimingOn;
086: private boolean isClosed;
087:
088: /* fields used for formating run time statistics output */
089: protected String indent;
090: protected String subIndent;
091: protected int sourceDepth;
092:
093: /* Run time statistics variables */
094: final LanguageConnectionContext lcc;
095: protected long beginTime;
096: protected long endTime;
097: protected long beginExecutionTime;
098: protected long endExecutionTime;
099:
100: NoRowsResultSetImpl(Activation activation) throws StandardException {
101: this .activation = activation;
102:
103: if (SanityManager.DEBUG) {
104: if (activation == null)
105: SanityManager
106: .THROWASSERT("activation is null in result set "
107: + getClass());
108: }
109:
110: lcc = activation.getLanguageConnectionContext();
111: statisticsTimingOn = lcc.getStatisticsTiming();
112:
113: /* NOTE - We can't get the current time until after setting up the
114: * activation, as we end up using the activation to get the
115: * LanguageConnectionContext.
116: */
117: beginTime = getCurrentTimeMillis();
118: beginExecutionTime = beginTime;
119:
120: StatementContext sc = lcc.getStatementContext();
121: sc.setTopResultSet(this , (NoPutResultSet[]) null);
122:
123: // Pick up any materialized subqueries
124: if (subqueryTrackingArray == null) {
125: subqueryTrackingArray = sc.getSubqueryTrackingArray();
126: }
127: }
128:
129: /**
130: * Returns FALSE
131: */
132: public final boolean returnsRows() {
133: return false;
134: }
135:
136: /**
137: * Returns zero.
138: */
139: public int modifiedRowCount() {
140: return 0;
141: }
142:
143: /**
144: * Returns null.
145: */
146: public ResultDescription getResultDescription() {
147: return (ResultDescription) null;
148: }
149:
150: public final Activation getActivation() {
151: return activation;
152: }
153:
154: /**
155: * Returns the row at the absolute position from the query,
156: * and returns NULL when there is no such position.
157: * (Negative position means from the end of the result set.)
158: * Moving the cursor to an invalid position leaves the cursor
159: * positioned either before the first row (negative position)
160: * or after the last row (positive position).
161: * NOTE: An exception will be thrown on 0.
162: *
163: * @param row The position.
164: * @return The row at the absolute position, or NULL if no such position.
165: *
166: * @exception StandardException Thrown on failure
167: * @see Row
168: */
169: public ExecRow getAbsoluteRow(int row) throws StandardException {
170: /*
171: The JDBC use of this class will never call here.
172: Only the DB API used directly can get this exception.
173: */
174: throw StandardException.newException(
175: SQLState.LANG_DOES_NOT_RETURN_ROWS, "absolute");
176: }
177:
178: /**
179: * Returns the row at the relative position from the current
180: * cursor position, and returns NULL when there is no such position.
181: * (Negative position means toward the beginning of the result set.)
182: * Moving the cursor to an invalid position leaves the cursor
183: * positioned either before the first row (negative position)
184: * or after the last row (positive position).
185: * NOTE: 0 is valid.
186: * NOTE: An exception is thrown if the cursor is not currently
187: * positioned on a row.
188: *
189: * @param row The position.
190: * @return The row at the relative position, or NULL if no such position.
191: *
192: * @exception StandardException Thrown on failure
193: * @see Row
194: */
195: public ExecRow getRelativeRow(int row) throws StandardException {
196: /*
197: The JDBC use of this class will never call here.
198: Only the DB API used directly can get this exception.
199: */
200: throw StandardException.newException(
201: SQLState.LANG_DOES_NOT_RETURN_ROWS, "relative");
202: }
203:
204: /**
205: * Sets the current position to before the first row and returns NULL
206: * because there is no current row.
207: *
208: * @return NULL.
209: *
210: * @exception StandardException Thrown on failure
211: * @see Row
212: */
213: public ExecRow setBeforeFirstRow() throws StandardException {
214: /*
215: The JDBC use of this class will never call here.
216: Only the DB API used directly can get this exception.
217: */
218: throw StandardException.newException(
219: SQLState.LANG_DOES_NOT_RETURN_ROWS, "beforeFirst");
220: }
221:
222: /**
223: * Returns the first row from the query, and returns NULL when there
224: * are no rows.
225: *
226: * @return The first row, or NULL if no rows.
227: *
228: * @exception StandardException Thrown on failure
229: * @see Row
230: */
231: public ExecRow getFirstRow() throws StandardException {
232: /*
233: The JDBC use of this class will never call here.
234: Only the DB API used directly can get this exception.
235: */
236: throw StandardException.newException(
237: SQLState.LANG_DOES_NOT_RETURN_ROWS, "first");
238: }
239:
240: /**
241: * No rows to return, so throw an exception.
242: *
243: * @exception StandardException Always throws a
244: * StandardException to indicate
245: * that this method is not intended to
246: * be used.
247: */
248: public ExecRow getNextRow() throws StandardException {
249: /*
250: The JDBC use of this class will never call here.
251: Only the DB API used directly can get this exception.
252: */
253: throw StandardException.newException(
254: SQLState.LANG_DOES_NOT_RETURN_ROWS, "next");
255: }
256:
257: /**
258: * Returns the previous row from the query, and returns NULL when there
259: * are no more previous rows.
260: *
261: * @return The previous row, or NULL if no more previous rows.
262: *
263: * @exception StandardException Thrown on failure
264: * @see Row
265: */
266: public ExecRow getPreviousRow() throws StandardException {
267: /*
268: The JDBC use of this class will never call here.
269: Only the DB API used directly can get this exception.
270: */
271: throw StandardException.newException(
272: SQLState.LANG_DOES_NOT_RETURN_ROWS, "previous");
273: }
274:
275: /**
276: * Returns the last row from the query, and returns NULL when there
277: * are no rows.
278: *
279: * @return The last row, or NULL if no rows.
280: *
281: * @exception StandardException Thrown on failure
282: * @see Row
283: */
284: public ExecRow getLastRow() throws StandardException {
285: /*
286: The JDBC use of this class will never call here.
287: Only the DB API used directly can get this exception.
288: */
289: throw StandardException.newException(
290: SQLState.LANG_DOES_NOT_RETURN_ROWS, "last");
291: }
292:
293: /**
294: * Sets the current position to after the last row and returns NULL
295: * because there is no current row.
296: *
297: * @return NULL.
298: *
299: * @exception StandardException Thrown on failure
300: * @see Row
301: */
302: public ExecRow setAfterLastRow() throws StandardException {
303: /*
304: The JDBC use of this class will never call here.
305: Only the DB API used directly can get this exception.
306: */
307: throw StandardException.newException(
308: SQLState.LANG_DOES_NOT_RETURN_ROWS, "afterLast");
309: }
310:
311: /**
312: * Clear the current row. This is done after a commit on holdable
313: * result sets.
314: * This is a no-op on result set which do not provide rows.
315: */
316: public final void clearCurrentRow() {
317:
318: }
319:
320: /**
321: * Determine if the cursor is before the first row in the result
322: * set.
323: *
324: * @return true if before the first row, false otherwise. Returns
325: * false when the result set contains no rows.
326: */
327: public boolean checkRowPosition(int isType) {
328: return false;
329: }
330:
331: /**
332: * Returns the row number of the current row. Row
333: * numbers start from 1 and go to 'n'. Corresponds
334: * to row numbering used to position current row
335: * in the result set (as per JDBC).
336: *
337: * @return the row number, or 0 if not on a row
338: *
339: */
340: public int getRowNumber() {
341: return 0;
342: }
343:
344: /**
345: * No rows to return, does nothing
346: *
347: * @exception StandardException thrown on error
348: */
349: public void close() throws StandardException {
350: isClosed = true;
351: }
352:
353: /**
354: Just report that it is always closed.
355: RESOLVE: if we don't report that we are closed,
356: then we will wind up with a dependency problem when
357: we send an invalidateFor on our own Statement. It
358: will call lcc.verifyNoOpenResultSets(), which is really
359: supposed to be verify that there are no read only
360: result sets that are open.
361: */
362: public boolean isClosed() {
363: return isClosed;
364: //return true;
365: }
366:
367: /**
368: * doesn't need to do anything, as no calls
369: * are made that need to be restricted once
370: * the result set is 'finished'.
371: *
372: * @exception StandardException on error
373: */
374: public void finish() throws StandardException {
375: if (!dumpedStats) {
376: /*
377: ** If run time statistics tracing is turned on, then now is the
378: ** time to dump out the information.
379: ** NOTE - We make a special exception for commit. If autocommit
380: ** is on, then the run time statistics from the autocommit is the
381: ** only one that the user would ever see. So, we don't overwrite
382: ** the run time statistics object for a commit.
383: */
384: if (lcc.getRunTimeStatisticsMode() && !doesCommit()) {
385: endExecutionTime = getCurrentTimeMillis();
386:
387: ExecutionContext ec = lcc.getExecutionContext();
388: ResultSetStatisticsFactory rssf;
389: rssf = ec.getResultSetStatisticsFactory();
390:
391: lcc.setRunTimeStatisticsObject(rssf
392: .getRunTimeStatistics(activation, this ,
393: subqueryTrackingArray));
394:
395: HeaderPrintWriter istream = lcc.getLogQueryPlan() ? Monitor
396: .getStream()
397: : null;
398: if (istream != null) {
399: istream
400: .printlnWithHeader(LanguageConnectionContext.xidStr
401: + lcc.getTransactionExecute()
402: .getTransactionIdString()
403: + "), "
404: + LanguageConnectionContext.lccStr
405: + lcc.getInstanceNumber()
406: + "), "
407: + lcc.getRunTimeStatisticsObject()
408: .getStatementText()
409: + " ******* "
410: + lcc
411: .getRunTimeStatisticsObject()
412: .getStatementExecutionPlanText());
413: }
414: }
415: dumpedStats = true;
416: }
417:
418: /* This is the top ResultSet,
419: * close all of the open subqueries.
420: */
421: int staLength = (subqueryTrackingArray == null) ? 0
422: : subqueryTrackingArray.length;
423:
424: for (int index = 0; index < staLength; index++) {
425: if (subqueryTrackingArray[index] == null) {
426: continue;
427: }
428: if (subqueryTrackingArray[index].isClosed()) {
429: continue;
430: }
431: subqueryTrackingArray[index].close();
432: }
433: }
434:
435: /**
436: * Get the execution time in milliseconds.
437: *
438: * @return long The execution time in milliseconds.
439: */
440: public long getExecuteTime() {
441: return endTime - beginTime;
442: }
443:
444: /**
445: * Get the Timestamp for the beginning of execution.
446: *
447: * @return Timestamp The Timestamp for the beginning of execution.
448: */
449: public Timestamp getBeginExecutionTimestamp() {
450: if (beginExecutionTime == 0) {
451: return null;
452: } else {
453: return new Timestamp(beginExecutionTime);
454: }
455: }
456:
457: /**
458: * Get the Timestamp for the end of execution.
459: *
460: * @return Timestamp The Timestamp for the end of execution.
461: */
462: public Timestamp getEndExecutionTimestamp() {
463: if (endExecutionTime == 0) {
464: return null;
465: } else {
466: return new Timestamp(endExecutionTime);
467: }
468: }
469:
470: /**
471: * RESOLVE - This method will go away once it is overloaded in all subclasses.
472: * Return the query plan as a String.
473: *
474: * @param depth Indentation level.
475: *
476: * @return String The query plan as a String.
477: */
478: public String getQueryPlanText(int depth) {
479: return MessageService.getTextMessage(
480: SQLState.LANG_GQPT_NOT_SUPPORTED, getClass().getName());
481: }
482:
483: /**
484: * Return the total amount of time spent in this ResultSet
485: *
486: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
487: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
488: *
489: * @return long The total amount of time spent (in milliseconds).
490: */
491: public long getTimeSpent(int type) {
492: /* RESOLVE - this should be overloaded in all subclasses */
493: return 0;
494: }
495:
496: /**
497: * @see ResultSet#getSubqueryTrackingArray
498: */
499: public final NoPutResultSet[] getSubqueryTrackingArray(
500: int numSubqueries) {
501: if (subqueryTrackingArray == null) {
502: subqueryTrackingArray = new NoPutResultSet[numSubqueries];
503: }
504:
505: return subqueryTrackingArray;
506: }
507:
508: /**
509: * @see ResultSet#getAutoGeneratedKeysResultset
510: */
511: public ResultSet getAutoGeneratedKeysResultset() {
512: //A non-null resultset would be returned only for an insert statement
513: return (ResultSet) null;
514: }
515:
516: /**
517: Return the cursor name, null in this case.
518:
519: @see ResultSet#getCursorName
520: */
521: public String getCursorName() {
522: return null;
523: }
524:
525: // class implementation
526:
527: /**
528: * Return the current time in milliseconds, if DEBUG and RunTimeStats is
529: * on, else return 0. (Only pay price of system call if need to.)
530: *
531: * @return long Current time in milliseconds.
532: */
533: protected final long getCurrentTimeMillis() {
534: if (statisticsTimingOn) {
535: return System.currentTimeMillis();
536: } else {
537: return 0;
538: }
539: }
540:
541: /**
542: * Run a check constraint against the current row. Raise an error if
543: * the check constraint is violated.
544: *
545: * @param checkGM Generated code to run the check constraint.
546: * @param checkName Name of the constraint to check.
547: * @param heapConglom Number of heap conglomerate.
548: * @param activation Class in which checkGM lives.
549: *
550: * @exception StandardException thrown on error
551: */
552: public static void evaluateACheckConstraint(
553: GeneratedMethod checkGM, String checkName,
554: long heapConglom, Activation activation)
555: throws StandardException {
556: if (checkGM != null) {
557: DataValueDescriptor checkBoolean;
558:
559: checkBoolean = (DataValueDescriptor) checkGM
560: .invoke(activation);
561:
562: /* Throw exception if check constraint is violated.
563: * (Only if check constraint evaluates to false.)
564: */
565: if ((checkBoolean != null) && (!checkBoolean.isNull())
566: && (!checkBoolean.getBoolean())) {
567: /* Now we have a lot of painful work to get the
568: * table name for the error message. All we have
569: * is the conglomerate number to work with.
570: */
571: DataDictionary dd = activation
572: .getLanguageConnectionContext()
573: .getDataDictionary();
574: ConglomerateDescriptor cd = dd
575: .getConglomerateDescriptor(heapConglom);
576: TableDescriptor td = dd.getTableDescriptor(cd
577: .getTableID());
578:
579: StandardException se = StandardException.newException(
580: SQLState.LANG_CHECK_CONSTRAINT_VIOLATED, td
581: .getQualifiedName(), checkName);
582:
583: throw se;
584: }
585: }
586:
587: }
588:
589: /**
590: * Run check constraints against the current row. Raise an error if
591: * a check constraint is violated.
592: *
593: * @param checkGM Generated code to run the check constraint.
594: * @param activation Class in which checkGM lives.
595: *
596: * @exception StandardException thrown on error
597: */
598: public static void evaluateCheckConstraints(
599: GeneratedMethod checkGM, Activation activation)
600: throws StandardException {
601: if (checkGM != null) {
602: // Evaluate the expression containing the check constraints.
603: // This expression will throw an exception if there is a
604: // violation, so there is no need to check the result.
605: checkGM.invoke(activation);
606: }
607:
608: }
609:
610: /**
611: * Does this ResultSet cause a commit or rollback.
612: *
613: * @return Whether or not this ResultSet cause a commit or rollback.
614: */
615: public boolean doesCommit() {
616: return false;
617: }
618:
619: public java.sql.SQLWarning getWarnings() {
620: return null;
621: }
622:
623: }
|