--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.VTIResultSet\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.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.loader.ClassInspector;\r
+\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.services.stream.InfoStreams;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.sql.execute.ExecutionContext;\r
+\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.io.FormatableHashtable;\r
+\r
+import org.apache.derby.vti.DeferModification;\r
+import org.apache.derby.vti.IFastPath;\r
+import org.apache.derby.vti.VTIEnvironment;\r
+\r
+import java.sql.PreparedStatement;\r
+import java.sql.ResultSet;\r
+import java.sql.SQLException;\r
+import java.sql.ResultSetMetaData;\r
+\r
+\r
+/**\r
+ */\r
+class VTIResultSet extends NoPutResultSetImpl\r
+ implements CursorResultSet, VTIEnvironment {\r
+\r
+ /* Run time statistics variables */\r
+ public int rowsReturned;\r
+ public String javaClassName;\r
+\r
+ private boolean next;\r
+ private ClassInspector classInspector;\r
+ private GeneratedMethod row;\r
+ private GeneratedMethod constructor;\r
+ private PreparedStatement userPS;\r
+ private ResultSet userVTI;\r
+ private ExecRow allocatedRow;\r
+ private FormatableBitSet referencedColumns;\r
+ private boolean version2;\r
+ private boolean reuseablePs;\r
+ private boolean isTarget;\r
+ private FormatableHashtable compileTimeConstants;\r
+ private int ctcNumber;\r
+\r
+ private boolean pushedProjection;\r
+ private IFastPath fastPath;\r
+\r
+ private Qualifier[][] pushedQualifiers;\r
+\r
+ private boolean[] runtimeNullableColumn;\r
+\r
+ /**\r
+ Specified isolation level of SELECT (scan). If not set or\r
+ not application, it will be set to ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL\r
+ */\r
+ private int scanIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;\r
+\r
+ //\r
+ // class interface\r
+ //\r
+ VTIResultSet(Activation activation, GeneratedMethod row, int resultSetNumber,\r
+ GeneratedMethod constructor,\r
+ String javaClassName,\r
+ Qualifier[][] pushedQualifiers,\r
+ int erdNumber,\r
+ boolean version2, boolean reuseablePs,\r
+ int ctcNumber,\r
+ boolean isTarget,\r
+ int scanIsolationLevel,\r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost) \r
+ throws StandardException\r
+ {\r
+ super(activation, resultSetNumber, \r
+ optimizerEstimatedRowCount, optimizerEstimatedCost);\r
+ this.row = row;\r
+ this.constructor = constructor;\r
+ this.javaClassName = javaClassName;\r
+ this.version2 = version2;\r
+ this.reuseablePs = reuseablePs;\r
+ this.isTarget = isTarget;\r
+ this.pushedQualifiers = pushedQualifiers;\r
+ this.scanIsolationLevel = scanIsolationLevel;\r
+\r
+ if (erdNumber != -1)\r
+ {\r
+ this.referencedColumns = (FormatableBitSet)(activation.getPreparedStatement().\r
+ getSavedObject(erdNumber));\r
+ }\r
+\r
+ this.ctcNumber = ctcNumber;\r
+ compileTimeConstants = (FormatableHashtable) (activation.getPreparedStatement().\r
+ getSavedObject(ctcNumber));\r
+\r
+ constructorTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ //\r
+ // ResultSet interface (leftover from NoPutResultSet)\r
+ //\r
+\r
+\r
+ /**\r
+ * Sets state to 'open'.\r
+ *\r
+ * @exception StandardException thrown if activation closed.\r
+ */\r
+ public void openCore() throws StandardException \r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT( ! isOpen, "VTIResultSet already open");\r
+\r
+ isOpen = true;\r
+ numOpens++;\r
+\r
+ /* We need to Instantiate the user's ResultSet on the each open since\r
+ * there is no way to close and then reopen a java.sql.ResultSet.\r
+ * For Version 2 VTIs, we may be able to skip instantiated their\r
+ * PreparedStatement here.\r
+ */\r
+ try {\r
+ if (version2)\r
+ {\r
+ userPS = (PreparedStatement) constructor.invoke(activation);\r
+\r
+ if (userPS instanceof org.apache.derby.vti.Pushable) {\r
+ org.apache.derby.vti.Pushable p = (org.apache.derby.vti.Pushable) userPS;\r
+ if (referencedColumns != null) {\r
+ pushedProjection = p.pushProjection(this, getProjectedColList());\r
+ }\r
+ }\r
+\r
+ if (userPS instanceof org.apache.derby.vti.IQualifyable) {\r
+ org.apache.derby.vti.IQualifyable q = (org.apache.derby.vti.IQualifyable) userPS;\r
+\r
+ q.setQualifiers(this, pushedQualifiers);\r
+ }\r
+ fastPath = userPS instanceof IFastPath ? (IFastPath) userPS : null;\r
+\r
+ if( isTarget\r
+ && userPS instanceof DeferModification\r
+ && activation.getConstantAction() instanceof UpdatableVTIConstantAction)\r
+ {\r
+ UpdatableVTIConstantAction constants = (UpdatableVTIConstantAction) activation.getConstantAction();\r
+ ((DeferModification) userPS).modificationNotify( constants.statementType, constants.deferred);\r
+ }\r
+ \r
+ if ((fastPath != null) && fastPath.executeAsFastPath())\r
+ ;\r
+ else\r
+ userVTI = userPS.executeQuery();\r
+\r
+ /* Save off the target VTI */\r
+ if (isTarget)\r
+ {\r
+ activation.setTargetVTI(userVTI);\r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+ userVTI = (ResultSet) constructor.invoke(activation);\r
+ }\r
+\r
+ // Set up the nullablity of the runtime columns, may be delayed\r
+ setNullableColumnList();\r
+ }\r
+ catch (Throwable t)\r
+ {\r
+ throw StandardException.unexpectedUserException(t);\r
+ }\r
+\r
+\r
+ openTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ private boolean[] setNullableColumnList() throws SQLException {\r
+\r
+ if (runtimeNullableColumn != null)\r
+ return runtimeNullableColumn;\r
+\r
+ if (userVTI == null)\r
+ return null;\r
+\r
+ ResultSetMetaData rsmd = userVTI.getMetaData();\r
+ boolean[] nullableColumn = new boolean[rsmd.getColumnCount() + 1];\r
+ for (int i = 1; i < nullableColumn.length; i++) {\r
+ nullableColumn[i] = rsmd.isNullable(i) != ResultSetMetaData.columnNoNulls;\r
+ }\r
+\r
+ return runtimeNullableColumn = nullableColumn;\r
+ }\r
+\r
+ /**\r
+ * If the VTI is a version2 vti that does not\r
+ * need to be instantiated multiple times then\r
+ * we simply close the current ResultSet and \r
+ * create a new one via a call to \r
+ * PreparedStatement.executeQuery().\r
+ *\r
+ * @see NoPutResultSet#openCore\r
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public void reopenCore() throws StandardException\r
+ {\r
+ if (reuseablePs)\r
+ {\r
+ /* close the user ResultSet.\r
+ */\r
+ if (userVTI != null)\r
+ {\r
+ try\r
+ {\r
+ userVTI.close();\r
+ userVTI = userPS.executeQuery();\r
+\r
+ /* Save off the target VTI */\r
+ if (isTarget)\r
+ {\r
+ activation.setTargetVTI(userVTI);\r
+ }\r
+ } catch (SQLException se)\r
+ {\r
+ throw StandardException.unexpectedUserException(se);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ close();\r
+ openCore(); \r
+ }\r
+ }\r
+\r
+ /**\r
+ * If open and not returned yet, returns the row\r
+ * after plugging the parameters into the expressions.\r
+ *\r
+ * @exception StandardException thrown on failure.\r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException \r
+ {\r
+ ExecRow result = null;\r
+\r
+ beginTime = getCurrentTimeMillis();\r
+ \r
+ if ( isOpen ) \r
+ {\r
+ try\r
+ {\r
+ if ((userVTI == null) && (fastPath != null)) {\r
+ result = getAllocatedRow();\r
+ int action = fastPath.nextRow(result.getRowArray());\r
+ if (action == IFastPath.GOT_ROW)\r
+ ;\r
+ else if (action == IFastPath.SCAN_COMPLETED)\r
+ result = null;\r
+ else if (action == IFastPath.NEED_RS) {\r
+ userVTI = userPS.executeQuery();\r
+ }\r
+ }\r
+ if ((userVTI != null))\r
+ {\r
+ if( ! userVTI.next())\r
+ {\r
+ if( null != fastPath)\r
+ fastPath.rowsDone();\r
+ result = null;\r
+ }\r
+ else\r
+ {\r
+ // Get the cached row and fill it up\r
+ result = getAllocatedRow();\r
+ populateFromResultSet(result);\r
+ if (fastPath != null)\r
+ fastPath.currentRow(userVTI, result.getRowArray());\r
+ }\r
+ }\r
+ }\r
+ catch (Throwable t)\r
+ {\r
+ throw StandardException.unexpectedUserException(t);\r
+ }\r
+\r
+ }\r
+\r
+ setCurrentRow(result);\r
+ if (result != null)\r
+ {\r
+ rowsReturned++;\r
+ rowsSeen++;\r
+ }\r
+\r
+ nextTime += getElapsedMillis(beginTime);\r
+ return result;\r
+ }\r
+\r
+ \r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.ResultSet#close\r
+ *\r
+ * @exception StandardException thrown on error\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if (isOpen) {\r
+\r
+ // we don't want to keep around a pointer to the\r
+ // row ... so it can be thrown away.\r
+ // REVISIT: does this need to be in a finally\r
+ // block, to ensure that it is executed?\r
+ clearCurrentRow();\r
+ next = false;\r
+\r
+ /* close the user ResultSet. We have to eat any exception here\r
+ * since our close() method cannot throw an exception.\r
+ */\r
+ if (userVTI != null)\r
+ {\r
+ try\r
+ {\r
+ userVTI.close();\r
+ } catch (SQLException se)\r
+ {\r
+ throw StandardException.unexpectedUserException(se);\r
+ }\r
+ finally {\r
+ userVTI = null;\r
+ }\r
+ }\r
+ if ((userPS != null) && !reuseablePs)\r
+ {\r
+ try\r
+ {\r
+ userPS.close();\r
+ } catch (SQLException se)\r
+ {\r
+ throw StandardException.unexpectedUserException(se);\r
+ }\r
+ finally {\r
+ userPS = null;\r
+ }\r
+ }\r
+ super.close();\r
+ }\r
+ else\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.DEBUG("CloseRepeatInfo","Close of VTIResultSet repeated");\r
+\r
+ closeTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ public void finish() throws StandardException {\r
+\r
+ // for a reusablePS it will be closed by the activation\r
+ // when it is closed.\r
+ if ((userPS != null) && !reuseablePs)\r
+ {\r
+ try\r
+ {\r
+ userPS.close();\r
+ userPS = null;\r
+ } catch (SQLException se)\r
+ {\r
+ throw StandardException.unexpectedUserException(se);\r
+ }\r
+ }\r
+\r
+ finishAndRTS();\r
+\r
+ }\r
+\r
+ /**\r
+ * Return the total amount of time spent in this ResultSet\r
+ *\r
+ * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet\r
+ * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.\r
+ *\r
+ * @return long The total amount of time spent (in milliseconds).\r
+ */\r
+ public long getTimeSpent(int type)\r
+ {\r
+ long totTime = constructorTime + openTime + nextTime + closeTime;\r
+ return totTime;\r
+ }\r
+\r
+ //\r
+ // CursorResultSet interface\r
+ //\r
+\r
+ /**\r
+ * This is not operating against a stored table,\r
+ * so it has no row location to report.\r
+ *\r
+ * @see CursorResultSet\r
+ *\r
+ * @return a null.\r
+ */\r
+ public RowLocation getRowLocation() {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT("RowResultSet used in positioned update/delete");\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This is not used in positioned update and delete,\r
+ * so just return a null.\r
+ *\r
+ * @see CursorResultSet\r
+ *\r
+ * @return a null.\r
+ */\r
+ public ExecRow getCurrentRow() {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT("RowResultSet used in positioned update/delete");\r
+ return null;\r
+ }\r
+\r
+ // Class implementation\r
+\r
+ /**\r
+ * Return the GeneratedMethod for instantiating the VTI.\r
+ *\r
+ * @return The GeneratedMethod for instantiating the VTI.\r
+ */\r
+ GeneratedMethod getVTIConstructor()\r
+ {\r
+ return constructor;\r
+ }\r
+\r
+ boolean isReuseablePs() {\r
+ return reuseablePs;\r
+ }\r
+\r
+\r
+ /**\r
+ * Cache the ExecRow for this result set.\r
+ *\r
+ * @return The cached ExecRow for this ResultSet\r
+ *\r
+ * @exception StandardException thrown on failure.\r
+ */\r
+ private ExecRow getAllocatedRow()\r
+ throws StandardException\r
+ {\r
+ if (allocatedRow == null)\r
+ {\r
+ allocatedRow = (ExecRow) row.invoke(activation);\r
+ }\r
+\r
+ return allocatedRow;\r
+ }\r
+\r
+ private int[] getProjectedColList() {\r
+\r
+ FormatableBitSet refs = referencedColumns;\r
+ int size = refs.size();\r
+ int arrayLen = 0;\r
+ for (int i = 0; i < size; i++) {\r
+ if (refs.isSet(i))\r
+ arrayLen++;\r
+ }\r
+\r
+ int[] colList = new int[arrayLen];\r
+ int offset = 0;\r
+ for (int i = 0; i < size; i++) {\r
+ if (refs.isSet(i))\r
+ colList[offset++] = i + 1;\r
+ }\r
+\r
+ return colList;\r
+ }\r
+ /**\r
+ * @exception StandardException thrown on failure to open\r
+ */\r
+ public void populateFromResultSet(ExecRow row)\r
+ throws StandardException\r
+ {\r
+ try\r
+ {\r
+ boolean[] nullableColumn = setNullableColumnList();\r
+ DataValueDescriptor[] columns = row.getRowArray();\r
+ // ExecRows are 0-based, ResultSets are 1-based\r
+ int rsColNumber = 1;\r
+ for (int index = 0; index < columns.length; index++)\r
+ {\r
+ // Skip over unreferenced columns\r
+ if (referencedColumns != null && (! referencedColumns.get(index)))\r
+ {\r
+ if (!pushedProjection)\r
+ rsColNumber++;\r
+\r
+ continue;\r
+ }\r
+\r
+ columns[index].setValueFromResultSet(\r
+ userVTI, rsColNumber, \r
+ /* last parameter is whether or\r
+ * not the column is nullable\r
+ */\r
+ nullableColumn[rsColNumber]);\r
+ rsColNumber++;\r
+ }\r
+\r
+ } catch (StandardException se) {\r
+ throw se;\r
+ }\r
+ catch (Throwable t)\r
+ {\r
+ throw StandardException.unexpectedUserException(t);\r
+ }\r
+ }\r
+\r
+ public final int getScanIsolationLevel() {\r
+ return scanIsolationLevel;\r
+ }\r
+\r
+ /*\r
+ ** VTIEnvironment\r
+ */\r
+ public final boolean isCompileTime() {\r
+ return false;\r
+ }\r
+\r
+ public final String getOriginalSQL() {\r
+ return activation.getPreparedStatement().getSource();\r
+ }\r
+\r
+ public final int getStatementIsolationLevel() {\r
+ return ExecutionContext.CS_TO_JDBC_ISOLATION_LEVEL_MAP[getScanIsolationLevel()];\r
+ }\r
+\r
+\r
+ public final void setSharedState(String key, java.io.Serializable value) {\r
+ if (key == null)\r
+ return;\r
+\r
+ if (compileTimeConstants == null) {\r
+\r
+ Object[] savedObjects = activation.getPreparedStatement().getSavedObjects();\r
+\r
+ synchronized (savedObjects) {\r
+\r
+ compileTimeConstants = (FormatableHashtable) savedObjects[ctcNumber];\r
+ if (compileTimeConstants == null) {\r
+ compileTimeConstants = new FormatableHashtable();\r
+ savedObjects[ctcNumber] = compileTimeConstants;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (value == null)\r
+ compileTimeConstants.remove(key);\r
+ else\r
+ compileTimeConstants.put(key, value);\r
+\r
+\r
+ }\r
+\r
+ public Object getSharedState(String key) {\r
+ if ((key == null) || (compileTimeConstants == null))\r
+ return null;\r
+\r
+ return compileTimeConstants.get(key);\r
+ }\r
+}\r