--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.BasicNoPutResultSetImpl\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.execute;\r
+\r
+import org.apache.derby.iapi.services.context.ContextService;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.stream.InfoStreams;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.conn.StatementContext;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecutionFactory;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.Row;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+\r
+import java.sql.Timestamp;\r
+import java.sql.SQLWarning;\r
+\r
+/**\r
+ * Abstract ResultSet for for operations that return rows but\r
+ * do not allow the caller to put data on output pipes. This\r
+ * basic implementation does not include support for an Activiation.\r
+ * See NoPutResultSetImpl.java for an implementaion with support for\r
+ * an activiation.\r
+ * <p>\r
+ * This abstract class does not define the entire ResultSet\r
+ * interface, but leaves the 'get' half of the interface\r
+ * for subtypes to implement. It is package-visible only,\r
+ * with its methods being public for exposure by its subtypes.\r
+ * <p>\r
+ */\r
+abstract class BasicNoPutResultSetImpl\r
+implements NoPutResultSet\r
+{\r
+ /* Modified during the life of this object */\r
+ protected boolean isOpen;\r
+ protected boolean finished;\r
+ protected ExecRow currentRow;\r
+ protected boolean isTopResultSet;\r
+ protected LanguageConnectionContext lcc;\r
+ private SQLWarning warnings;\r
+\r
+ /* Run time statistics variables */\r
+ public int numOpens;\r
+ public int rowsSeen;\r
+ public int rowsFiltered;\r
+ protected long startExecutionTime;\r
+ protected long endExecutionTime;\r
+ public long beginTime;\r
+ public long constructorTime;\r
+ public long openTime;\r
+ public long nextTime;\r
+ public long closeTime;\r
+\r
+ public double optimizerEstimatedRowCount;\r
+ public double optimizerEstimatedCost;\r
+\r
+ // set on demand during execution\r
+ private StatementContext statementContext;\r
+ public NoPutResultSet[] subqueryTrackingArray;\r
+ ExecRow compactRow;\r
+\r
+ // Set in the constructor and not modified\r
+ protected Activation activation;\r
+ private boolean statisticsTimingOn;\r
+\r
+ ResultDescription resultDescription;\r
+\r
+ private transient ExecutionFactory exFactory;\r
+ private transient TransactionController tc;\r
+\r
+ private int[] baseColumnMap;\r
+\r
+ /**\r
+ * Constructor.\r
+ <BR>\r
+ Sets beginTime for all children to use to measue constructor time.\r
+ *\r
+ * @param resultDescription the result description. May be null.\r
+ * @param activation The activation\r
+ * @param optimizerEstimatedRowCount The optimizer's estimate of the\r
+ * total number of rows for this\r
+ * result set\r
+ * @param optimizerEstimatedCost The optimizer's estimated cost for\r
+ * this result set\r
+ */\r
+ BasicNoPutResultSetImpl(ResultDescription resultDescription,\r
+ Activation activation,\r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost)\r
+ {\r
+ this.activation = activation;\r
+ statisticsTimingOn = (activation != null && getLanguageConnectionContext().getStatisticsTiming());\r
+ beginTime = startExecutionTime = getCurrentTimeMillis();\r
+ this.resultDescription = resultDescription;\r
+ this.optimizerEstimatedRowCount = optimizerEstimatedRowCount;\r
+ this.optimizerEstimatedCost = optimizerEstimatedCost;\r
+ }\r
+ \r
+ public final Activation getActivation()\r
+ {\r
+ return activation;\r
+ }\r
+\r
+ // NoPutResultSet interface\r
+\r
+ /**\r
+ * @see NoPutResultSet#openCore\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public abstract void openCore() throws StandardException;\r
+\r
+ /**\r
+ * This is the default implementation of reopenCore().\r
+ * It simply does a close() followed by an open(). If\r
+ * there are optimizations to be made (caching, etc), this\r
+ * is a good place to do it -- this will be overridden\r
+ * by a number of resultSet imlementations. and SHOULD\r
+ * be overridden by any node that can get between a base\r
+ * table and a join.\r
+ *\r
+ * @see NoPutResultSet#openCore\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public void reopenCore() throws StandardException\r
+ {\r
+ close();\r
+ openCore(); \r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#getNextRowCore\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public abstract ExecRow getNextRowCore() throws StandardException;\r
+\r
+ /**\r
+ * @see NoPutResultSet#getPointOfAttachment\r
+ */\r
+ public int getPointOfAttachment()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "getPointOfAttachment() not expected to be called for " +\r
+ getClass().getName());\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ /**\r
+ * Mark the ResultSet as the topmost one in the ResultSet tree.\r
+ * Useful for closing down the ResultSet on an error.\r
+ */\r
+ public void markAsTopResultSet()\r
+ {\r
+ isTopResultSet = true;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#getScanIsolationLevel\r
+ */\r
+ public int getScanIsolationLevel()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "getScanIsolationLevel() not expected to be called for " +\r
+ getClass().getName());\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ /** @see NoPutResultSet#getEstimatedRowCount */\r
+ public double getEstimatedRowCount()\r
+ {\r
+ return optimizerEstimatedRowCount;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#requiresRelocking\r
+ */\r
+ public boolean requiresRelocking()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "requiresRelocking() not expected to be called for " +\r
+ getClass().getName());\r
+ }\r
+ return false;\r
+ }\r
+\r
+ // ResultSet interface\r
+\r
+ /**\r
+ * open a scan on the table. scan parameters are evaluated\r
+ * at each open, so there is probably some way of altering\r
+ * their values...\r
+ *\r
+ * NOTE: This method should only be called on the top ResultSet\r
+ * of a ResultSet tree to ensure that the entire ResultSet\r
+ * tree gets closed down on an error. the openCore() method\r
+ * will be called for all other ResultSets in the tree.\r
+ *\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public final void open() throws StandardException \r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ finished = false;\r
+\r
+ attachStatementContext();\r
+\r
+ try {\r
+\r
+ openCore();\r
+\r
+ } catch (StandardException se) {\r
+ activation.checkStatementValidity();\r
+ throw se;\r
+ }\r
+\r
+ activation.checkStatementValidity();\r
+ }\r
+\r
+ /**\r
+ * Returns the row at the absolute position from the query, \r
+ * and returns NULL when there is no such position.\r
+ * (Negative position means from the end of the result set.)\r
+ * Moving the cursor to an invalid position leaves the cursor\r
+ * positioned either before the first row (negative position)\r
+ * or after the last row (positive position).\r
+ * NOTE: An exception will be thrown on 0.\r
+ *\r
+ * @param row The position.\r
+ * @return The row at the absolute position, or NULL if no such position.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow getAbsoluteRow(int row) throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, ABSOLUTE);\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "getAbsoluteRow() not expected to be called for " + getClass().getName());\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Returns the row at the relative position from the current\r
+ * cursor position, and returns NULL when there is no such position.\r
+ * (Negative position means toward the beginning of the result set.)\r
+ * Moving the cursor to an invalid position leaves the cursor\r
+ * positioned either before the first row (negative position)\r
+ * or after the last row (positive position).\r
+ * NOTE: 0 is valid.\r
+ * NOTE: An exception is thrown if the cursor is not currently\r
+ * positioned on a row.\r
+ *\r
+ * @param row The position.\r
+ * @return The row at the relative position, or NULL if no such position.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow getRelativeRow(int row) throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, RELATIVE);\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "getRelativeRow() not expected to be called for " + getClass().getName());\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Sets the current position to before the first row and returns NULL\r
+ * because there is no current row.\r
+ *\r
+ * @return NULL.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow setBeforeFirstRow() \r
+ throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, FIRST);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "setBeforeFirstRow() not expected to be called for " + getClass().getName());\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Determine if the cursor is before the first row in the result \r
+ * set. \r
+ *\r
+ * @return true if before the first row, false otherwise. Returns\r
+ * false when the result set contains no rows.\r
+ */\r
+ public boolean checkRowPosition(int isType) throws StandardException\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns the row number of the current row. Row\r
+ * numbers start from 1 and go to 'n'. Corresponds\r
+ * to row numbering used to position current row\r
+ * in the result set (as per JDBC).\r
+ *\r
+ * @return the row number, or 0 if not on a row\r
+ *\r
+ */\r
+ public int getRowNumber()\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ * Returns the first row from the query, and returns NULL when there\r
+ * are no rows.\r
+ *\r
+ * @return The first row, or NULL if no rows.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow getFirstRow() \r
+ throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, FIRST);\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "getFirstRow() not expected to be called for " + getClass().getName());\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Return the requested values computed\r
+ * from the next row (if any) for which\r
+ * the restriction evaluates to true.\r
+ * <p>\r
+ * restriction and projection parameters\r
+ * are evaluated for each row.\r
+ *\r
+ * NOTE: This method should only be called on the top ResultSet\r
+ * of a ResultSet tree to ensure that the entire ResultSet\r
+ * tree gets closed down on an error. the getNextRowCore() method\r
+ * will be called for all other ResultSets in the tree.\r
+ *\r
+ * @exception StandardException thrown on failure.\r
+ * @exception StandardException ResultSetNotOpen thrown if not yet open.\r
+ *\r
+ * @return the next row in the result\r
+ */\r
+ public final ExecRow getNextRow() throws StandardException \r
+ {\r
+ if ( ! isOpen ) {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, NEXT);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ return getNextRowCore();\r
+ }\r
+\r
+ /**\r
+ * Returns the previous row from the query, and returns NULL when there\r
+ * are no more previous rows.\r
+ *\r
+ * @return The previous row, or NULL if no more previous rows.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow getPreviousRow() \r
+ throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, PREVIOUS);\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "getPreviousRow() not expected to be called.");\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Returns the last row from the query, and returns NULL when there\r
+ * are no rows.\r
+ *\r
+ * @return The last row, or NULL if no rows.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow getLastRow()\r
+ throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, LAST);\r
+ }\r
+\r
+ attachStatementContext();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "getLastRow() not expected to be called.");\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Sets the current position to after the last row and returns NULL\r
+ * because there is no current row.\r
+ *\r
+ * @return NULL.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ * @see Row\r
+ */\r
+ public ExecRow setAfterLastRow() \r
+ throws StandardException\r
+ {\r
+ if ( ! isOpen ) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, LAST);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!isTopResultSet)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ this + "expected to be the top ResultSet");\r
+ }\r
+\r
+ SanityManager.THROWASSERT(\r
+ "setAfterLastRow() not expected to be called.");\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+\r
+ /**\r
+ * Returns true.\r
+ */\r
+ public boolean returnsRows() { return true; }\r
+\r
+ public final int modifiedRowCount() { return 0; }\r
+\r
+ /**\r
+ * Clean up on error\r
+ * @exception StandardException Thrown on failure\r
+ *\r
+ */\r
+ public void cleanUp() throws StandardException\r
+ {\r
+ if (isOpen) {\r
+ close();\r
+ }\r
+ }\r
+\r
+ /**\r
+ Report if closed.\r
+ */\r
+ public boolean isClosed() {\r
+ return ( ! isOpen );\r
+ }\r
+\r
+ public void finish() throws StandardException\r
+ {\r
+ finishAndRTS();\r
+ }\r
+\r
+ /**\r
+ * @exception StandardException on error\r
+ */ \r
+ protected final void finishAndRTS() throws StandardException\r
+ {\r
+\r
+ if (!finished) {\r
+ if (!isClosed())\r
+ close();\r
+\r
+ finished = true;\r
+\r
+ if (isTopResultSet && activation.isSingleExecution())\r
+ activation.close();\r
+ }\r
+ }\r
+\r
+ /* The following methods are common to almost all sub-classes.\r
+ * They are overriden in selected cases.\r
+ */\r
+\r
+ /**\r
+ * Returns the description of the table's rows\r
+ */\r
+ public ResultDescription getResultDescription() {\r
+ return resultDescription;\r
+ }\r
+\r
+ /**\r
+ * Get the execution time in milliseconds.\r
+ *\r
+ * @return long The execution time in milliseconds.\r
+ */\r
+ public long getExecuteTime()\r
+ {\r
+ return getTimeSpent(ResultSet.ENTIRE_RESULTSET_TREE);\r
+ }\r
+\r
+ /**\r
+ * Get the Timestamp for the beginning of execution.\r
+ *\r
+ * @return Timestamp The Timestamp for the beginning of execution.\r
+ */\r
+ public Timestamp getBeginExecutionTimestamp()\r
+ {\r
+ if (startExecutionTime == 0)\r
+ {\r
+ return null;\r
+ }\r
+ else\r
+ {\r
+ return new Timestamp(startExecutionTime);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the Timestamp for the end of execution.\r
+ *\r
+ * @return Timestamp The Timestamp for the end of execution.\r
+ */\r
+ public Timestamp getEndExecutionTimestamp()\r
+ {\r
+ if (endExecutionTime == 0)\r
+ {\r
+ return null;\r
+ }\r
+ else\r
+ {\r
+ return new Timestamp(endExecutionTime);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see ResultSet#getSubqueryTrackingArray\r
+ */\r
+ public final NoPutResultSet[] getSubqueryTrackingArray(int numSubqueries)\r
+ {\r
+ if (subqueryTrackingArray == null)\r
+ {\r
+ subqueryTrackingArray = new NoPutResultSet[numSubqueries];\r
+ }\r
+\r
+ return subqueryTrackingArray;\r
+ }\r
+\r
+ /**\r
+ * Return the current time in milliseconds, if DEBUG and RunTimeStats is\r
+ * on, else return 0. (Only pay price of system call if need to.)\r
+ *\r
+ * @return long Current time in milliseconds.\r
+ */\r
+ protected final long getCurrentTimeMillis()\r
+ {\r
+ if (statisticsTimingOn)\r
+ {\r
+ return System.currentTimeMillis();\r
+ }\r
+ else\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see ResultSet#getAutoGeneratedKeysResultset\r
+ */\r
+ public ResultSet getAutoGeneratedKeysResultset()\r
+ {\r
+ //A non-null resultset would be returned only for an insert statement \r
+ return (ResultSet)null;\r
+ }\r
+\r
+ /**\r
+ * Return the elapsed time in milliseconds, between now and the beginTime, if\r
+ * DEBUG and RunTimeStats is on, else return 0. \r
+ * (Only pay price of system call if need to.)\r
+ *\r
+ * @return long Elapsed time in milliseconds.\r
+ */\r
+\r
+ protected final long getElapsedMillis(long beginTime)\r
+ {\r
+ if (statisticsTimingOn)\r
+ {\r
+ return (System.currentTimeMillis() - beginTime);\r
+ }\r
+ else\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Dump out the time information for run time stats.\r
+ *\r
+ * @return Nothing.\r
+ */\r
+ protected final String dumpTimeStats(String indent, String subIndent)\r
+ {\r
+ return \r
+ indent +\r
+ MessageService.getTextMessage(SQLState.LANG_TIME_SPENT_THIS) +\r
+ " " + getTimeSpent(ResultSet.CURRENT_RESULTSET_ONLY) + "\n" +\r
+ indent +\r
+ MessageService.getTextMessage(\r
+ SQLState.LANG_TIME_SPENT_THIS_AND_BELOW) +\r
+ " " + getTimeSpent(NoPutResultSet.ENTIRE_RESULTSET_TREE) + "\n" +\r
+ indent +\r
+ MessageService.getTextMessage(\r
+ SQLState.LANG_TOTAL_TIME_BREAKDOWN) + "\n" +\r
+ subIndent +\r
+ MessageService.getTextMessage(SQLState.LANG_CONSTRUCTOR_TIME) +\r
+ " " + constructorTime + "\n" +\r
+ subIndent +\r
+ MessageService.getTextMessage(SQLState.LANG_OPEN_TIME) +\r
+ " " + openTime + "\n" +\r
+ subIndent +\r
+ MessageService.getTextMessage(SQLState.LANG_NEXT_TIME) +\r
+ " " + nextTime + "\n" +\r
+ subIndent +\r
+ MessageService.getTextMessage(SQLState.LANG_CLOSE_TIME) +\r
+ " " + closeTime;\r
+ }\r
+\r
+\r
+ /**\r
+ * Attach this result set to the top statement context on the stack.\r
+ * Result sets can be directly read from the JDBC layer. The JDBC layer\r
+ * will push and pop a statement context around each ResultSet.getNext().\r
+ * There's no guarantee that the statement context used for the last\r
+ * getNext() will be the context used for the current getNext(). The\r
+ * last statement context may have been popped off the stack and so\r
+ * will not be available for cleanup if an error occurs. To make sure\r
+ * that we will be cleaned up, we always attach ourselves to the top \r
+ * context.\r
+ *\r
+ * The fun and games occur in nested contexts: using JDBC result sets inside\r
+ * user code that is itself invoked from queries or CALL statements.\r
+ *\r
+ *\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ protected void attachStatementContext() throws StandardException\r
+ {\r
+ if (isTopResultSet)\r
+ {\r
+ if (statementContext == null || !statementContext.onStack() )\r
+ {\r
+ statementContext = getLanguageConnectionContext().getStatementContext();\r
+ }\r
+ statementContext.setTopResultSet(this, subqueryTrackingArray);\r
+ // Pick up any materialized subqueries\r
+ if (subqueryTrackingArray == null)\r
+ {\r
+ subqueryTrackingArray = statementContext.getSubqueryTrackingArray();\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Cache the language connection context. Return it.\r
+ *\r
+ * @return the language connection context\r
+ */\r
+ protected final LanguageConnectionContext getLanguageConnectionContext()\r
+ {\r
+ if ( lcc == null )\r
+ {\r
+ /* We don't always have an activation. Get the LCC\r
+ * from the activation when we have one.\r
+ */\r
+ if (activation != null)\r
+ {\r
+ lcc = activation.getLanguageConnectionContext();\r
+ }\r
+ else\r
+ {\r
+ lcc = (LanguageConnectionContext) ContextService.getContext(LanguageConnectionContext.CONTEXT_ID); \r
+ }\r
+ }\r
+\r
+ return lcc;\r
+ }\r
+\r
+ /** @see NoPutResultSet#resultSetNumber() */\r
+ public int resultSetNumber() {\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.THROWASSERT(\r
+ "resultSetNumber() should not be called on a " +\r
+ this.getClass().getName()\r
+ );\r
+ }\r
+\r
+ return 0;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////\r
+ //\r
+ // UTILS \r
+ //\r
+ //////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Get a execution factory\r
+ *\r
+ * @return the execution factory\r
+ */\r
+ final ExecutionFactory getExecutionFactory() \r
+ {\r
+ if (exFactory == null) {\r
+ exFactory = activation.getExecutionFactory();\r
+ }\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(exFactory!=null,"unable to get execution factory");\r
+ return exFactory;\r
+ }\r
+\r
+ /**\r
+ * Get the current transaction controller.\r
+ *\r
+ */\r
+ final TransactionController getTransactionController()\r
+ {\r
+ if (tc == null)\r
+ {\r
+ tc = getLanguageConnectionContext().getTransactionExecute();\r
+ }\r
+ return tc;\r
+ }\r
+\r
+ /**\r
+ * Get a compacted version of the candidate row according to the\r
+ * columns specified in the bit map. Share the holders between rows.\r
+ * If there is no bit map, use the candidate row as the compact row.\r
+ *\r
+ * Also, create an array of ints mapping base column positions to\r
+ * compact column positions, to make it cheaper to copy columns to\r
+ * the compact row, if we ever have to do it again.\r
+ *\r
+ * @param candidate The row to get the columns from\r
+ * @param accessedCols A bit map of the columns that are accessed in\r
+ * the candidate row\r
+ * @param otherCols An bit map of other column ids - this is used\r
+ * in case columns from an index row will be\r
+ * copied into a heap row - in this case, we\r
+ * need to be sure there are enough columns in\r
+ * the compact row. This parameter is null if\r
+ * columns will not be copied from an index row\r
+ * to a compact heap row. The column numbers in\r
+ * the bit map are zero-based.\r
+ * @param isKeyed Tells whether to return a ValueRow or an IndexRow\r
+ *\r
+ * @return A compact row.\r
+ */\r
+ protected ExecRow getCompactRow(ExecRow candidate,\r
+ FormatableBitSet accessedCols,\r
+ FormatableBitSet otherCols,\r
+ boolean isKeyed)\r
+ throws StandardException\r
+ {\r
+ int numCandidateCols = candidate.nColumns();\r
+\r
+ if (accessedCols == null)\r
+ {\r
+ compactRow = candidate;\r
+ baseColumnMap = new int[numCandidateCols];\r
+ for (int i = 0; i < baseColumnMap.length; i++)\r
+ baseColumnMap[i] = i;\r
+ }\r
+ else\r
+ {\r
+ FormatableBitSet allCols;\r
+\r
+ if (otherCols == null)\r
+ {\r
+ allCols = accessedCols;\r
+ }\r
+ else\r
+ {\r
+ allCols = new FormatableBitSet(accessedCols);\r
+ allCols.or(otherCols);\r
+ }\r
+\r
+ int numCols = allCols.getNumBitsSet();\r
+ baseColumnMap = new int[numCols];\r
+\r
+ if (compactRow == null)\r
+ {\r
+ ExecutionFactory ex = lcc.getLanguageConnectionFactory().getExecutionFactory();\r
+\r
+ if (isKeyed)\r
+ {\r
+ compactRow = ex.getIndexableRow(numCols);\r
+ }\r
+ else\r
+ {\r
+ compactRow = ex.getValueRow(numCols);\r
+ }\r
+ }\r
+\r
+ int position = 0;\r
+ for (int i = allCols.anySetBit();\r
+ i != -1;\r
+ i = allCols.anySetBit(i))\r
+ {\r
+ // Stop looking if there are columns beyond the columns\r
+ // in the candidate row. This can happen due to the\r
+ // otherCols bit map.\r
+ if (i >= numCandidateCols)\r
+ break;\r
+\r
+ DataValueDescriptor sc = candidate.getColumn(i+1);\r
+ if (sc != null)\r
+ {\r
+ compactRow.setColumn(\r
+ position + 1,\r
+ sc\r
+ );\r
+ }\r
+ baseColumnMap[position] = i;\r
+ position++;\r
+ }\r
+ }\r
+\r
+ return compactRow;\r
+ }\r
+\r
+ /**\r
+ * Copy columns from the candidate row from the store to the given\r
+ * compact row. If there is no column map, just use the candidate row.\r
+ *\r
+ * This method assumes the above method (getCompactRow()) was called\r
+ * first. getCompactRow() sets up the baseColumnMap.\r
+ *\r
+ * @param candidateRow The candidate row from the store\r
+ * @param compactRow The compact row to fill in\r
+ *\r
+ * @return The compact row to use\r
+ */\r
+ protected ExecRow setCompactRow(ExecRow candidateRow, ExecRow compactRow)\r
+ {\r
+ ExecRow retval;\r
+\r
+ //System.out.println("base col map " + baseColumnMap);\r
+ if (baseColumnMap == null)\r
+ {\r
+ retval = candidateRow;\r
+ }\r
+ else\r
+ {\r
+ retval = compactRow;\r
+\r
+ setCompatRow(compactRow, candidateRow.getRowArray());\r
+ }\r
+\r
+ return retval;\r
+ }\r
+\r
+\r
+ protected final void setCompatRow(ExecRow compactRow, Object[] sourceRow) {\r
+\r
+ Object[] destRow = compactRow.getRowArray();\r
+ int[] lbcm = baseColumnMap;\r
+\r
+ for (int i = 0; i < lbcm.length; i++)\r
+ {\r
+\r
+ destRow[i] = sourceRow[lbcm[i]];\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Is this ResultSet or it's source result set for update\r
+ * This method will be overriden in the inherited Classes\r
+ * if it is true\r
+ * @return Whether or not the result set is for update.\r
+ */\r
+ public boolean isForUpdate()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Checks whether the currently executing statement has been cancelled.\r
+ * This is done by checking the statement's allocated StatementContext\r
+ * object.\r
+ *\r
+ * @see StatementContext\r
+ */\r
+ public void checkCancellationFlag()\r
+ throws\r
+ StandardException\r
+ {\r
+ StatementContext localStatementContext = getLanguageConnectionContext().getStatementContext(); \r
+ if (localStatementContext == null) {\r
+ return;\r
+ }\r
+\r
+ if (localStatementContext.isCancelled()) {\r
+ throw StandardException.newException(SQLState.LANG_STATEMENT_CANCELLED_OR_TIMED_OUT);\r
+ }\r
+ }\r
+\r
+ protected final void addWarning(SQLWarning w) {\r
+\r
+ if (isTopResultSet) {\r
+ if (warnings == null)\r
+ warnings = w;\r
+ else \r
+ warnings.setNextWarning(w);\r
+ return;\r
+ }\r
+\r
+ if (activation != null) {\r
+\r
+ ResultSet rs = activation.getResultSet();\r
+ if (rs instanceof BasicNoPutResultSetImpl) {\r
+ ((BasicNoPutResultSetImpl) rs).addWarning(w);\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ public final SQLWarning getWarnings() {\r
+ SQLWarning w = warnings;\r
+ warnings = null;\r
+ return w;\r
+ }\r
+}\r