0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.execute.BasicNoPutResultSetImpl
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.execute;
0023:
0024: import org.apache.derby.iapi.services.context.ContextService;
0025: import org.apache.derby.iapi.services.monitor.Monitor;
0026: import org.apache.derby.iapi.services.sanity.SanityManager;
0027: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
0028: import org.apache.derby.iapi.services.stream.InfoStreams;
0029: import org.apache.derby.iapi.error.StandardException;
0030: import org.apache.derby.iapi.services.i18n.MessageService;
0031:
0032: import org.apache.derby.iapi.store.access.TransactionController;
0033:
0034: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0035: import org.apache.derby.iapi.sql.conn.StatementContext;
0036:
0037: import org.apache.derby.iapi.reference.SQLState;
0038:
0039: import org.apache.derby.iapi.sql.execute.ExecRow;
0040: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
0041: import org.apache.derby.iapi.sql.execute.ExecutionFactory;
0042: import org.apache.derby.iapi.sql.Activation;
0043:
0044: import org.apache.derby.iapi.sql.ResultDescription;
0045: import org.apache.derby.iapi.sql.ResultSet;
0046: import org.apache.derby.iapi.sql.Row;
0047:
0048: import org.apache.derby.iapi.types.DataValueDescriptor;
0049:
0050: import org.apache.derby.iapi.services.io.FormatableBitSet;
0051:
0052: import java.sql.Timestamp;
0053: import java.sql.SQLWarning;
0054:
0055: /**
0056: * Abstract ResultSet for for operations that return rows but
0057: * do not allow the caller to put data on output pipes. This
0058: * basic implementation does not include support for an Activiation.
0059: * See NoPutResultSetImpl.java for an implementaion with support for
0060: * an activiation.
0061: * <p>
0062: * This abstract class does not define the entire ResultSet
0063: * interface, but leaves the 'get' half of the interface
0064: * for subtypes to implement. It is package-visible only,
0065: * with its methods being public for exposure by its subtypes.
0066: * <p>
0067: */
0068: abstract class BasicNoPutResultSetImpl implements NoPutResultSet {
0069: /* Modified during the life of this object */
0070: protected boolean isOpen;
0071: protected boolean finished;
0072: protected ExecRow currentRow;
0073: protected boolean isTopResultSet;
0074: protected LanguageConnectionContext lcc;
0075: private SQLWarning warnings;
0076:
0077: /* Run time statistics variables */
0078: public int numOpens;
0079: public int rowsSeen;
0080: public int rowsFiltered;
0081: protected long startExecutionTime;
0082: protected long endExecutionTime;
0083: public long beginTime;
0084: public long constructorTime;
0085: public long openTime;
0086: public long nextTime;
0087: public long closeTime;
0088:
0089: public double optimizerEstimatedRowCount;
0090: public double optimizerEstimatedCost;
0091:
0092: // set on demand during execution
0093: private StatementContext statementContext;
0094: public NoPutResultSet[] subqueryTrackingArray;
0095: ExecRow compactRow;
0096:
0097: // Set in the constructor and not modified
0098: protected Activation activation;
0099: private boolean statisticsTimingOn;
0100:
0101: ResultDescription resultDescription;
0102:
0103: private transient ExecutionFactory exFactory;
0104: private transient TransactionController tc;
0105:
0106: private int[] baseColumnMap;
0107:
0108: /**
0109: * Constructor.
0110: <BR>
0111: Sets beginTime for all children to use to measue constructor time.
0112: *
0113: * @param resultDescription the result description. May be null.
0114: * @param activation The activation
0115: * @param optimizerEstimatedRowCount The optimizer's estimate of the
0116: * total number of rows for this
0117: * result set
0118: * @param optimizerEstimatedCost The optimizer's estimated cost for
0119: * this result set
0120: */
0121: BasicNoPutResultSetImpl(ResultDescription resultDescription,
0122: Activation activation, double optimizerEstimatedRowCount,
0123: double optimizerEstimatedCost) {
0124: this .activation = activation;
0125: statisticsTimingOn = (activation != null && getLanguageConnectionContext()
0126: .getStatisticsTiming());
0127: beginTime = startExecutionTime = getCurrentTimeMillis();
0128: this .resultDescription = resultDescription;
0129: this .optimizerEstimatedRowCount = optimizerEstimatedRowCount;
0130: this .optimizerEstimatedCost = optimizerEstimatedCost;
0131: }
0132:
0133: public final Activation getActivation() {
0134: return activation;
0135: }
0136:
0137: // NoPutResultSet interface
0138:
0139: /**
0140: * @see NoPutResultSet#openCore
0141: * @exception StandardException thrown if cursor finished.
0142: */
0143: public abstract void openCore() throws StandardException;
0144:
0145: /**
0146: * This is the default implementation of reopenCore().
0147: * It simply does a close() followed by an open(). If
0148: * there are optimizations to be made (caching, etc), this
0149: * is a good place to do it -- this will be overridden
0150: * by a number of resultSet imlementations. and SHOULD
0151: * be overridden by any node that can get between a base
0152: * table and a join.
0153: *
0154: * @see NoPutResultSet#openCore
0155: * @exception StandardException thrown if cursor finished.
0156: */
0157: public void reopenCore() throws StandardException {
0158: close();
0159: openCore();
0160: }
0161:
0162: /**
0163: * @see NoPutResultSet#getNextRowCore
0164: * @exception StandardException thrown if cursor finished.
0165: */
0166: public abstract ExecRow getNextRowCore() throws StandardException;
0167:
0168: /**
0169: * @see NoPutResultSet#getPointOfAttachment
0170: */
0171: public int getPointOfAttachment() {
0172: if (SanityManager.DEBUG) {
0173: SanityManager
0174: .THROWASSERT("getPointOfAttachment() not expected to be called for "
0175: + getClass().getName());
0176: }
0177: return -1;
0178: }
0179:
0180: /**
0181: * Mark the ResultSet as the topmost one in the ResultSet tree.
0182: * Useful for closing down the ResultSet on an error.
0183: */
0184: public void markAsTopResultSet() {
0185: isTopResultSet = true;
0186: }
0187:
0188: /**
0189: * @see NoPutResultSet#getScanIsolationLevel
0190: */
0191: public int getScanIsolationLevel() {
0192: if (SanityManager.DEBUG) {
0193: SanityManager
0194: .THROWASSERT("getScanIsolationLevel() not expected to be called for "
0195: + getClass().getName());
0196: }
0197: return 0;
0198: }
0199:
0200: /** @see NoPutResultSet#getEstimatedRowCount */
0201: public double getEstimatedRowCount() {
0202: return optimizerEstimatedRowCount;
0203: }
0204:
0205: /**
0206: * @see NoPutResultSet#requiresRelocking
0207: */
0208: public boolean requiresRelocking() {
0209: if (SanityManager.DEBUG) {
0210: SanityManager
0211: .THROWASSERT("requiresRelocking() not expected to be called for "
0212: + getClass().getName());
0213: }
0214: return false;
0215: }
0216:
0217: // ResultSet interface
0218:
0219: /**
0220: * open a scan on the table. scan parameters are evaluated
0221: * at each open, so there is probably some way of altering
0222: * their values...
0223: *
0224: * NOTE: This method should only be called on the top ResultSet
0225: * of a ResultSet tree to ensure that the entire ResultSet
0226: * tree gets closed down on an error. the openCore() method
0227: * will be called for all other ResultSets in the tree.
0228: *
0229: * @exception StandardException thrown if cursor finished.
0230: */
0231: public final void open() throws StandardException {
0232: if (SanityManager.DEBUG) {
0233: if (!isTopResultSet)
0234: SanityManager.THROWASSERT(this
0235: + "expected to be the top ResultSet");
0236: }
0237:
0238: finished = false;
0239:
0240: attachStatementContext();
0241:
0242: try {
0243:
0244: openCore();
0245:
0246: } catch (StandardException se) {
0247: activation.checkStatementValidity();
0248: throw se;
0249: }
0250:
0251: activation.checkStatementValidity();
0252: }
0253:
0254: /**
0255: * Returns the row at the absolute position from the query,
0256: * and returns NULL when there is no such position.
0257: * (Negative position means from the end of the result set.)
0258: * Moving the cursor to an invalid position leaves the cursor
0259: * positioned either before the first row (negative position)
0260: * or after the last row (positive position).
0261: * NOTE: An exception will be thrown on 0.
0262: *
0263: * @param row The position.
0264: * @return The row at the absolute position, or NULL if no such position.
0265: *
0266: * @exception StandardException Thrown on failure
0267: * @see Row
0268: */
0269: public ExecRow getAbsoluteRow(int row) throws StandardException {
0270: if (!isOpen) {
0271: throw StandardException.newException(
0272: SQLState.LANG_RESULT_SET_NOT_OPEN, ABSOLUTE);
0273: }
0274:
0275: attachStatementContext();
0276:
0277: if (SanityManager.DEBUG) {
0278: if (!isTopResultSet) {
0279: SanityManager.THROWASSERT(this
0280: + "expected to be the top ResultSet");
0281: }
0282:
0283: SanityManager
0284: .THROWASSERT("getAbsoluteRow() not expected to be called for "
0285: + getClass().getName());
0286: }
0287:
0288: return null;
0289: }
0290:
0291: /**
0292: * Returns the row at the relative position from the current
0293: * cursor position, and returns NULL when there is no such position.
0294: * (Negative position means toward the beginning of the result set.)
0295: * Moving the cursor to an invalid position leaves the cursor
0296: * positioned either before the first row (negative position)
0297: * or after the last row (positive position).
0298: * NOTE: 0 is valid.
0299: * NOTE: An exception is thrown if the cursor is not currently
0300: * positioned on a row.
0301: *
0302: * @param row The position.
0303: * @return The row at the relative position, or NULL if no such position.
0304: *
0305: * @exception StandardException Thrown on failure
0306: * @see Row
0307: */
0308: public ExecRow getRelativeRow(int row) throws StandardException {
0309: if (!isOpen) {
0310: throw StandardException.newException(
0311: SQLState.LANG_RESULT_SET_NOT_OPEN, RELATIVE);
0312: }
0313:
0314: attachStatementContext();
0315:
0316: if (SanityManager.DEBUG) {
0317: if (!isTopResultSet) {
0318: SanityManager.THROWASSERT(this
0319: + "expected to be the top ResultSet");
0320: }
0321:
0322: SanityManager
0323: .THROWASSERT("getRelativeRow() not expected to be called for "
0324: + getClass().getName());
0325: }
0326:
0327: return null;
0328: }
0329:
0330: /**
0331: * Sets the current position to before the first row and returns NULL
0332: * because there is no current row.
0333: *
0334: * @return NULL.
0335: *
0336: * @exception StandardException Thrown on failure
0337: * @see Row
0338: */
0339: public ExecRow setBeforeFirstRow() throws StandardException {
0340: if (!isOpen) {
0341: throw StandardException.newException(
0342: SQLState.LANG_RESULT_SET_NOT_OPEN, FIRST);
0343: }
0344:
0345: if (SanityManager.DEBUG) {
0346: if (!isTopResultSet) {
0347: SanityManager.THROWASSERT(this
0348: + "expected to be the top ResultSet");
0349: }
0350:
0351: SanityManager
0352: .THROWASSERT("setBeforeFirstRow() not expected to be called for "
0353: + getClass().getName());
0354: }
0355:
0356: return null;
0357: }
0358:
0359: /**
0360: * Determine if the cursor is before the first row in the result
0361: * set.
0362: *
0363: * @return true if before the first row, false otherwise. Returns
0364: * false when the result set contains no rows.
0365: */
0366: public boolean checkRowPosition(int isType)
0367: throws StandardException {
0368: return false;
0369: }
0370:
0371: /**
0372: * Returns the row number of the current row. Row
0373: * numbers start from 1 and go to 'n'. Corresponds
0374: * to row numbering used to position current row
0375: * in the result set (as per JDBC).
0376: *
0377: * @return the row number, or 0 if not on a row
0378: *
0379: */
0380: public int getRowNumber() {
0381: return 0;
0382: }
0383:
0384: /**
0385: * Returns the first row from the query, and returns NULL when there
0386: * are no rows.
0387: *
0388: * @return The first row, or NULL if no rows.
0389: *
0390: * @exception StandardException Thrown on failure
0391: * @see Row
0392: */
0393: public ExecRow getFirstRow() throws StandardException {
0394: if (!isOpen) {
0395: throw StandardException.newException(
0396: SQLState.LANG_RESULT_SET_NOT_OPEN, FIRST);
0397: }
0398:
0399: attachStatementContext();
0400:
0401: if (SanityManager.DEBUG) {
0402: if (!isTopResultSet) {
0403: SanityManager.THROWASSERT(this
0404: + "expected to be the top ResultSet");
0405: }
0406:
0407: SanityManager
0408: .THROWASSERT("getFirstRow() not expected to be called for "
0409: + getClass().getName());
0410: }
0411:
0412: return null;
0413: }
0414:
0415: /**
0416: * Return the requested values computed
0417: * from the next row (if any) for which
0418: * the restriction evaluates to true.
0419: * <p>
0420: * restriction and projection parameters
0421: * are evaluated for each row.
0422: *
0423: * NOTE: This method should only be called on the top ResultSet
0424: * of a ResultSet tree to ensure that the entire ResultSet
0425: * tree gets closed down on an error. the getNextRowCore() method
0426: * will be called for all other ResultSets in the tree.
0427: *
0428: * @exception StandardException thrown on failure.
0429: * @exception StandardException ResultSetNotOpen thrown if not yet open.
0430: *
0431: * @return the next row in the result
0432: */
0433: public final ExecRow getNextRow() throws StandardException {
0434: if (!isOpen) {
0435: throw StandardException.newException(
0436: SQLState.LANG_RESULT_SET_NOT_OPEN, NEXT);
0437: }
0438:
0439: if (SanityManager.DEBUG) {
0440: if (!isTopResultSet)
0441: SanityManager.THROWASSERT(this
0442: + "expected to be the top ResultSet");
0443: }
0444:
0445: attachStatementContext();
0446:
0447: return getNextRowCore();
0448: }
0449:
0450: /**
0451: * Returns the previous row from the query, and returns NULL when there
0452: * are no more previous rows.
0453: *
0454: * @return The previous row, or NULL if no more previous rows.
0455: *
0456: * @exception StandardException Thrown on failure
0457: * @see Row
0458: */
0459: public ExecRow getPreviousRow() throws StandardException {
0460: if (!isOpen) {
0461: throw StandardException.newException(
0462: SQLState.LANG_RESULT_SET_NOT_OPEN, PREVIOUS);
0463: }
0464:
0465: attachStatementContext();
0466:
0467: if (SanityManager.DEBUG) {
0468: if (!isTopResultSet) {
0469: SanityManager.THROWASSERT(this
0470: + "expected to be the top ResultSet");
0471: }
0472:
0473: SanityManager
0474: .THROWASSERT("getPreviousRow() not expected to be called.");
0475: }
0476:
0477: return null;
0478: }
0479:
0480: /**
0481: * Returns the last row from the query, and returns NULL when there
0482: * are no rows.
0483: *
0484: * @return The last row, or NULL if no rows.
0485: *
0486: * @exception StandardException Thrown on failure
0487: * @see Row
0488: */
0489: public ExecRow getLastRow() throws StandardException {
0490: if (!isOpen) {
0491: throw StandardException.newException(
0492: SQLState.LANG_RESULT_SET_NOT_OPEN, LAST);
0493: }
0494:
0495: attachStatementContext();
0496:
0497: if (SanityManager.DEBUG) {
0498: if (!isTopResultSet) {
0499: SanityManager.THROWASSERT(this
0500: + "expected to be the top ResultSet");
0501: }
0502:
0503: SanityManager
0504: .THROWASSERT("getLastRow() not expected to be called.");
0505: }
0506:
0507: return null;
0508: }
0509:
0510: /**
0511: * Sets the current position to after the last row and returns NULL
0512: * because there is no current row.
0513: *
0514: * @return NULL.
0515: *
0516: * @exception StandardException Thrown on failure
0517: * @see Row
0518: */
0519: public ExecRow setAfterLastRow() throws StandardException {
0520: if (!isOpen) {
0521: throw StandardException.newException(
0522: SQLState.LANG_RESULT_SET_NOT_OPEN, LAST);
0523: }
0524:
0525: if (SanityManager.DEBUG) {
0526: if (!isTopResultSet) {
0527: SanityManager.THROWASSERT(this
0528: + "expected to be the top ResultSet");
0529: }
0530:
0531: SanityManager
0532: .THROWASSERT("setAfterLastRow() not expected to be called.");
0533: }
0534:
0535: return null;
0536: }
0537:
0538: /**
0539: * Returns true.
0540: */
0541: public boolean returnsRows() {
0542: return true;
0543: }
0544:
0545: public final int modifiedRowCount() {
0546: return 0;
0547: }
0548:
0549: /**
0550: * Clean up on error
0551: * @exception StandardException Thrown on failure
0552: *
0553: */
0554: public void cleanUp() throws StandardException {
0555: if (isOpen) {
0556: close();
0557: }
0558: }
0559:
0560: /**
0561: Report if closed.
0562: */
0563: public boolean isClosed() {
0564: return (!isOpen);
0565: }
0566:
0567: public void finish() throws StandardException {
0568: finishAndRTS();
0569: }
0570:
0571: /**
0572: * @exception StandardException on error
0573: */
0574: protected final void finishAndRTS() throws StandardException {
0575:
0576: if (!finished) {
0577: /*
0578: ** If run time statistics tracing is turned on, then now is the
0579: ** time to dump out the information.
0580: */
0581: if (isTopResultSet) {
0582:
0583: LanguageConnectionContext lcc = getLanguageConnectionContext();
0584: if (lcc.getRunTimeStatisticsMode()) {
0585: endExecutionTime = getCurrentTimeMillis();
0586:
0587: lcc.setRunTimeStatisticsObject(lcc
0588: .getExecutionContext()
0589: .getResultSetStatisticsFactory()
0590: .getRunTimeStatistics(activation, this ,
0591: subqueryTrackingArray));
0592:
0593: HeaderPrintWriter istream = lcc.getLogQueryPlan() ? Monitor
0594: .getStream()
0595: : null;
0596: if (istream != null) {
0597: istream
0598: .printlnWithHeader(LanguageConnectionContext.xidStr
0599: + lcc
0600: .getTransactionExecute()
0601: .getTransactionIdString()
0602: + "), "
0603: + LanguageConnectionContext.lccStr
0604: + lcc.getInstanceNumber()
0605: + "), "
0606: + lcc
0607: .getRunTimeStatisticsObject()
0608: .getStatementText()
0609: + " ******* "
0610: + lcc
0611: .getRunTimeStatisticsObject()
0612: .getStatementExecutionPlanText());
0613: }
0614: }
0615:
0616: }
0617:
0618: if (!isClosed())
0619: close();
0620:
0621: finished = true;
0622:
0623: if (isTopResultSet && activation.isSingleExecution())
0624: activation.close();
0625: }
0626: }
0627:
0628: /* The following methods are common to almost all sub-classes.
0629: * They are overriden in selected cases.
0630: */
0631:
0632: /**
0633: * Returns the description of the table's rows
0634: */
0635: public ResultDescription getResultDescription() {
0636: return resultDescription;
0637: }
0638:
0639: /**
0640: * Get the execution time in milliseconds.
0641: *
0642: * @return long The execution time in milliseconds.
0643: */
0644: public long getExecuteTime() {
0645: return getTimeSpent(ResultSet.ENTIRE_RESULTSET_TREE);
0646: }
0647:
0648: /**
0649: * Get the Timestamp for the beginning of execution.
0650: *
0651: * @return Timestamp The Timestamp for the beginning of execution.
0652: */
0653: public Timestamp getBeginExecutionTimestamp() {
0654: if (startExecutionTime == 0) {
0655: return null;
0656: } else {
0657: return new Timestamp(startExecutionTime);
0658: }
0659: }
0660:
0661: /**
0662: * Get the Timestamp for the end of execution.
0663: *
0664: * @return Timestamp The Timestamp for the end of execution.
0665: */
0666: public Timestamp getEndExecutionTimestamp() {
0667: if (endExecutionTime == 0) {
0668: return null;
0669: } else {
0670: return new Timestamp(endExecutionTime);
0671: }
0672: }
0673:
0674: /**
0675: * @see ResultSet#getSubqueryTrackingArray
0676: */
0677: public final NoPutResultSet[] getSubqueryTrackingArray(
0678: int numSubqueries) {
0679: if (subqueryTrackingArray == null) {
0680: subqueryTrackingArray = new NoPutResultSet[numSubqueries];
0681: }
0682:
0683: return subqueryTrackingArray;
0684: }
0685:
0686: /**
0687: * Return the current time in milliseconds, if DEBUG and RunTimeStats is
0688: * on, else return 0. (Only pay price of system call if need to.)
0689: *
0690: * @return long Current time in milliseconds.
0691: */
0692: protected final long getCurrentTimeMillis() {
0693: if (statisticsTimingOn) {
0694: return System.currentTimeMillis();
0695: } else {
0696: return 0;
0697: }
0698: }
0699:
0700: /**
0701: * @see ResultSet#getAutoGeneratedKeysResultset
0702: */
0703: public ResultSet getAutoGeneratedKeysResultset() {
0704: //A non-null resultset would be returned only for an insert statement
0705: return (ResultSet) null;
0706: }
0707:
0708: /**
0709: * Return the elapsed time in milliseconds, between now and the beginTime, if
0710: * DEBUG and RunTimeStats is on, else return 0.
0711: * (Only pay price of system call if need to.)
0712: *
0713: * @return long Elapsed time in milliseconds.
0714: */
0715:
0716: protected final long getElapsedMillis(long beginTime) {
0717: if (statisticsTimingOn) {
0718: return (System.currentTimeMillis() - beginTime);
0719: } else {
0720: return 0;
0721: }
0722: }
0723:
0724: /**
0725: * Dump out the time information for run time stats.
0726: *
0727: * @return Nothing.
0728: */
0729: protected final String dumpTimeStats(String indent, String subIndent) {
0730: return indent
0731: + MessageService
0732: .getTextMessage(SQLState.LANG_TIME_SPENT_THIS)
0733: + " "
0734: + getTimeSpent(ResultSet.CURRENT_RESULTSET_ONLY)
0735: + "\n"
0736: + indent
0737: + MessageService
0738: .getTextMessage(SQLState.LANG_TIME_SPENT_THIS_AND_BELOW)
0739: + " "
0740: + getTimeSpent(NoPutResultSet.ENTIRE_RESULTSET_TREE)
0741: + "\n"
0742: + indent
0743: + MessageService
0744: .getTextMessage(SQLState.LANG_TOTAL_TIME_BREAKDOWN)
0745: + "\n"
0746: + subIndent
0747: + MessageService
0748: .getTextMessage(SQLState.LANG_CONSTRUCTOR_TIME)
0749: + " "
0750: + constructorTime
0751: + "\n"
0752: + subIndent
0753: + MessageService
0754: .getTextMessage(SQLState.LANG_OPEN_TIME)
0755: + " "
0756: + openTime
0757: + "\n"
0758: + subIndent
0759: + MessageService
0760: .getTextMessage(SQLState.LANG_NEXT_TIME)
0761: + " "
0762: + nextTime
0763: + "\n"
0764: + subIndent
0765: + MessageService
0766: .getTextMessage(SQLState.LANG_CLOSE_TIME) + " "
0767: + closeTime;
0768: }
0769:
0770: /**
0771: * Attach this result set to the top statement context on the stack.
0772: * Result sets can be directly read from the JDBC layer. The JDBC layer
0773: * will push and pop a statement context around each ResultSet.getNext().
0774: * There's no guarantee that the statement context used for the last
0775: * getNext() will be the context used for the current getNext(). The
0776: * last statement context may have been popped off the stack and so
0777: * will not be available for cleanup if an error occurs. To make sure
0778: * that we will be cleaned up, we always attach ourselves to the top
0779: * context.
0780: *
0781: * The fun and games occur in nested contexts: using JDBC result sets inside
0782: * user code that is itself invoked from queries or CALL statements.
0783: *
0784: *
0785: * @exception StandardException thrown if cursor finished.
0786: */
0787: protected void attachStatementContext() throws StandardException {
0788: if (isTopResultSet) {
0789: if (statementContext == null || !statementContext.onStack()) {
0790: statementContext = getLanguageConnectionContext()
0791: .getStatementContext();
0792: }
0793: statementContext.setTopResultSet(this ,
0794: subqueryTrackingArray);
0795: // Pick up any materialized subqueries
0796: if (subqueryTrackingArray == null) {
0797: subqueryTrackingArray = statementContext
0798: .getSubqueryTrackingArray();
0799: }
0800: }
0801:
0802: }
0803:
0804: /**
0805: * Cache the language connection context. Return it.
0806: *
0807: * @return the language connection context
0808: */
0809: protected final LanguageConnectionContext getLanguageConnectionContext() {
0810: if (lcc == null) {
0811: /* We don't always have an activation. Get the LCC
0812: * from the activation when we have one.
0813: */
0814: if (activation != null) {
0815: lcc = activation.getLanguageConnectionContext();
0816: } else {
0817: lcc = (LanguageConnectionContext) ContextService
0818: .getContext(LanguageConnectionContext.CONTEXT_ID);
0819: }
0820: }
0821:
0822: return lcc;
0823: }
0824:
0825: /** @see NoPutResultSet#resultSetNumber() */
0826: public int resultSetNumber() {
0827: if (SanityManager.DEBUG) {
0828: SanityManager
0829: .THROWASSERT("resultSetNumber() should not be called on a "
0830: + this .getClass().getName());
0831: }
0832:
0833: return 0;
0834: }
0835:
0836: //////////////////////////////////////////////////////
0837: //
0838: // UTILS
0839: //
0840: //////////////////////////////////////////////////////
0841:
0842: /**
0843: * Get a execution factory
0844: *
0845: * @return the execution factory
0846: */
0847: final ExecutionFactory getExecutionFactory() {
0848: if (exFactory == null) {
0849: exFactory = activation.getExecutionFactory();
0850: }
0851: if (SanityManager.DEBUG)
0852: SanityManager.ASSERT(exFactory != null,
0853: "unable to get execution factory");
0854: return exFactory;
0855: }
0856:
0857: /**
0858: * Get the current transaction controller.
0859: *
0860: */
0861: final TransactionController getTransactionController() {
0862: if (tc == null) {
0863: tc = getLanguageConnectionContext().getTransactionExecute();
0864: }
0865: return tc;
0866: }
0867:
0868: /**
0869: * Get a compacted version of the candidate row according to the
0870: * columns specified in the bit map. Share the holders between rows.
0871: * If there is no bit map, use the candidate row as the compact row.
0872: *
0873: * Also, create an array of ints mapping base column positions to
0874: * compact column positions, to make it cheaper to copy columns to
0875: * the compact row, if we ever have to do it again.
0876: *
0877: * @param candidate The row to get the columns from
0878: * @param accessedCols A bit map of the columns that are accessed in
0879: * the candidate row
0880: * @param otherCols An bit map of other column ids - this is used
0881: * in case columns from an index row will be
0882: * copied into a heap row - in this case, we
0883: * need to be sure there are enough columns in
0884: * the compact row. This parameter is null if
0885: * columns will not be copied from an index row
0886: * to a compact heap row. The column numbers in
0887: * the bit map are zero-based.
0888: * @param isKeyed Tells whether to return a ValueRow or an IndexRow
0889: *
0890: * @return A compact row.
0891: */
0892: protected ExecRow getCompactRow(ExecRow candidate,
0893: FormatableBitSet accessedCols, FormatableBitSet otherCols,
0894: boolean isKeyed) throws StandardException {
0895: int numCandidateCols = candidate.nColumns();
0896:
0897: if (accessedCols == null) {
0898: compactRow = candidate;
0899: baseColumnMap = new int[numCandidateCols];
0900: for (int i = 0; i < baseColumnMap.length; i++)
0901: baseColumnMap[i] = i;
0902: } else {
0903: FormatableBitSet allCols;
0904:
0905: if (otherCols == null) {
0906: allCols = accessedCols;
0907: } else {
0908: allCols = new FormatableBitSet(accessedCols);
0909: allCols.or(otherCols);
0910: }
0911:
0912: int numCols = allCols.getNumBitsSet();
0913: baseColumnMap = new int[numCols];
0914:
0915: if (compactRow == null) {
0916: ExecutionFactory ex = lcc.getExecutionContext()
0917: .getExecutionFactory();
0918:
0919: if (isKeyed) {
0920: compactRow = ex.getIndexableRow(numCols);
0921: } else {
0922: compactRow = ex.getValueRow(numCols);
0923: }
0924: }
0925:
0926: int position = 0;
0927: for (int i = allCols.anySetBit(); i != -1; i = allCols
0928: .anySetBit(i)) {
0929: // Stop looking if there are columns beyond the columns
0930: // in the candidate row. This can happen due to the
0931: // otherCols bit map.
0932: if (i >= numCandidateCols)
0933: break;
0934:
0935: DataValueDescriptor sc = candidate.getColumn(i + 1);
0936: if (sc != null) {
0937: compactRow.setColumn(position + 1, sc);
0938: }
0939: baseColumnMap[position] = i;
0940: position++;
0941: }
0942: }
0943:
0944: return compactRow;
0945: }
0946:
0947: /**
0948: * Copy columns from the candidate row from the store to the given
0949: * compact row. If there is no column map, just use the candidate row.
0950: *
0951: * This method assumes the above method (getCompactRow()) was called
0952: * first. getCompactRow() sets up the baseColumnMap.
0953: *
0954: * @param candidateRow The candidate row from the store
0955: * @param compactRow The compact row to fill in
0956: *
0957: * @return The compact row to use
0958: */
0959: protected ExecRow setCompactRow(ExecRow candidateRow,
0960: ExecRow compactRow) {
0961: ExecRow retval;
0962:
0963: //System.out.println("base col map " + baseColumnMap);
0964: if (baseColumnMap == null) {
0965: retval = candidateRow;
0966: } else {
0967: retval = compactRow;
0968:
0969: setCompatRow(compactRow, candidateRow.getRowArray());
0970: }
0971:
0972: return retval;
0973: }
0974:
0975: protected final void setCompatRow(ExecRow compactRow,
0976: Object[] sourceRow) {
0977:
0978: Object[] destRow = compactRow.getRowArray();
0979: int[] lbcm = baseColumnMap;
0980:
0981: for (int i = 0; i < lbcm.length; i++) {
0982:
0983: destRow[i] = sourceRow[lbcm[i]];
0984:
0985: }
0986: }
0987:
0988: /**
0989: * Is this ResultSet or it's source result set for update
0990: * This method will be overriden in the inherited Classes
0991: * if it is true
0992: * @return Whether or not the result set is for update.
0993: */
0994: public boolean isForUpdate() {
0995: return false;
0996: }
0997:
0998: /**
0999: * Checks whether the currently executing statement has been cancelled.
1000: * This is done by checking the statement's allocated StatementContext
1001: * object.
1002: *
1003: * @see StatementContext
1004: */
1005: public void checkCancellationFlag() throws StandardException {
1006: StatementContext localStatementContext = getLanguageConnectionContext()
1007: .getStatementContext();
1008: if (localStatementContext == null) {
1009: return;
1010: }
1011:
1012: if (localStatementContext.isCancelled()) {
1013: throw StandardException
1014: .newException(SQLState.LANG_STATEMENT_CANCELLED_OR_TIMED_OUT);
1015: }
1016: }
1017:
1018: protected final void addWarning(SQLWarning w) {
1019:
1020: if (isTopResultSet) {
1021: if (warnings == null)
1022: warnings = w;
1023: else
1024: warnings.setNextWarning(w);
1025: return;
1026: }
1027:
1028: if (activation != null) {
1029:
1030: ResultSet rs = activation.getResultSet();
1031: if (rs instanceof BasicNoPutResultSetImpl) {
1032: ((BasicNoPutResultSetImpl) rs).addWarning(w);
1033: }
1034:
1035: }
1036: }
1037:
1038: public final SQLWarning getWarnings() {
1039: SQLWarning w = warnings;
1040: warnings = null;
1041: return w;
1042: }
1043: }
|