--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.NormalizeResultSet\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.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultColumnDescriptor;\r
+import org.apache.derby.iapi.sql.ResultDescription;\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
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+/**\r
+ * Cast the rows from the source result set to match the format of the\r
+ * result set for the entire statement.\r
+ */\r
+\r
+class NormalizeResultSet extends NoPutResultSetImpl\r
+ implements CursorResultSet\r
+{\r
+ /*\r
+ ** Set in constructor and not altered during life of object.\r
+ */\r
+\r
+ public NoPutResultSet source;\r
+ private ExecRow normalizedRow;\r
+ private int numCols;\r
+ private int startCol;\r
+\r
+ /* RESOLVE - We need to pass the ResultDescription for this ResultSet\r
+ * as a parameter to the constructor and use it instead of the one from\r
+ * the activation\r
+ */\r
+ private ResultDescription resultDescription;\r
+\r
+ /* info for caching DTSs */\r
+ private DataTypeDescriptor[] desiredTypes;\r
+\r
+ /**\r
+ * Constructor for a NormalizeResultSet\r
+ *\r
+ * @param source The NoPutResultSet from which to get rows\r
+ * to be normalized\r
+ * @param activation The activation for this execution\r
+ * @param resultSetNumber The resultSetNumber\r
+ * @param erdNumber The integer for the ResultDescription\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+\r
+ public NormalizeResultSet(NoPutResultSet source,\r
+ Activation activation, int resultSetNumber,\r
+ int erdNumber,\r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost,\r
+ boolean forUpdate) throws StandardException\r
+ {\r
+ super(activation, resultSetNumber, optimizerEstimatedRowCount, \r
+ optimizerEstimatedCost);\r
+ this.source = source;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (! (activation.getPreparedStatement().getSavedObject(erdNumber)\r
+ instanceof ResultDescription))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "activation.getPreparedStatement().getSavedObject(erdNumber) " +\r
+ "expected to be instanceof ResultDescription");\r
+ }\r
+\r
+ // source expected to be non-null, mystery stress test bug\r
+ // - sometimes get NullPointerException in openCore().\r
+ SanityManager.ASSERT(source != null,\r
+ "NRS(), source expected to be non-null");\r
+ }\r
+\r
+ this.resultDescription = \r
+ (ResultDescription) activation.getPreparedStatement().getSavedObject(erdNumber);\r
+\r
+ numCols = resultDescription.getColumnCount();\r
+ \r
+ /*\r
+ An update row, for an update statement which sets n columns; i.e\r
+ UPDATE tab set x,y,z=.... where ...;\r
+ has,\r
+ before values of x,y,z after values of x,y,z and rowlocation.\r
+ need only normalize after values of x,y,z.\r
+ i.e insead of starting at index = 1, I need to start at index = 4.\r
+ also I needn't normalize the last value in the row.\r
+ */\r
+ startCol = (forUpdate) ? ((numCols - 1)/ 2) + 1 : 1;\r
+ normalizedRow = activation.getExecutionFactory().getValueRow(numCols);\r
+ constructorTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+\r
+ //\r
+ // ResultSet interface (leftover from NoPutResultSet)\r
+ //\r
+\r
+ /**\r
+ * open a scan on the source. scan parameters are evaluated\r
+ * at each open, so there is probably some way of altering\r
+ * their values...\r
+ *\r
+ * @exception StandardException thrown on failure \r
+ */\r
+ public void openCore() throws StandardException\r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT( ! isOpen, "NormalizeResultSet already open");\r
+\r
+ // source expected to be non-null, mystery stress test bug\r
+ // - sometimes get NullPointerException in openCore().\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(source != null,\r
+ "NRS().openCore(), source expected to be non-null");\r
+ }\r
+\r
+ source.openCore();\r
+ isOpen = true;\r
+ numOpens++;\r
+\r
+ openTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ /**\r
+ * reopen 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
+ * @exception StandardException thrown if cursor finished.\r
+ */\r
+ public void reopenCore() throws StandardException \r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isOpen, "NormalizeResultSet already open");\r
+\r
+ source.reopenCore();\r
+ numOpens++;\r
+\r
+ openTime += getElapsedMillis(beginTime);\r
+ }\r
+\r
+ /**\r
+ *\r
+ * @exception StandardException thrown on failure \r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException\r
+ {\r
+ ExecRow sourceRow = null;\r
+ ExecRow result = null;\r
+\r
+ beginTime = getCurrentTimeMillis();\r
+ if (!isOpen)\r
+ throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");\r
+\r
+ sourceRow = source.getNextRowCore();\r
+ if (sourceRow != null)\r
+ {\r
+ result = normalizeRow(sourceRow);\r
+ rowsSeen++;\r
+ }\r
+\r
+ currentRow = result;\r
+ setCurrentRow(result);\r
+\r
+ nextTime += getElapsedMillis(beginTime);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * If the result set has been opened,\r
+ * close the open scan.\r
+ *\r
+ * @exception StandardException thrown on error\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ beginTime = getCurrentTimeMillis();\r
+ if ( isOpen )\r
+ {\r
+ currentRow = null;\r
+ source.close();\r
+\r
+ super.close();\r
+ }\r
+ else\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.DEBUG("CloseRepeatInfo","Close of NormalizeResultSet repeated");\r
+\r
+ closeTime += getElapsedMillis(beginTime);\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
+\r
+ if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)\r
+ {\r
+ return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);\r
+ }\r
+ else\r
+ {\r
+ return totTime;\r
+ }\r
+ }\r
+\r
+ //\r
+ // CursorResultSet interface\r
+ //\r
+\r
+ /**\r
+ * Gets information from its source. We might want\r
+ * to have this take a CursorResultSet in its constructor some day,\r
+ * instead of doing a cast here?\r
+ *\r
+ * @see CursorResultSet\r
+ *\r
+ * @return the row location of the current cursor row.\r
+ *\r
+ * @exception StandardException thrown on failure \r
+ */\r
+ public RowLocation getRowLocation() throws StandardException \r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(source instanceof CursorResultSet, "source is not a cursorresultset");\r
+ return ( (CursorResultSet)source ).getRowLocation();\r
+ }\r
+\r
+ /**\r
+ * Gets information from last getNextRow call.\r
+ *\r
+ * @see CursorResultSet\r
+ *\r
+ * @return the last row returned.\r
+ */\r
+ /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),\r
+ * once there is such a method. (currentRow is redundant)\r
+ */\r
+ public ExecRow getCurrentRow() \r
+ {\r
+ return currentRow;\r
+ }\r
+\r
+ //\r
+ // class implementation\r
+ //\r
+ /**\r
+ * Normalize a row. For now, this means calling constructors through\r
+ * the type services to normalize a type to itself. For example,\r
+ * if you're putting a char(30) value into a char(15) column, it\r
+ * calls a SQLChar constructor with the char(30) value, and the\r
+ * constructor truncates the value and makes sure that no non-blank\r
+ * characters are truncated.\r
+ *\r
+ * In the future, this mechanism will be extended to do type conversions,\r
+ * as well. I didn't implement type conversions yet because it looks\r
+ * like a lot of work, and we needed char and varchar right away.\r
+ *\r
+ * @param sourceRow The row to normalize\r
+ *\r
+ * @return The normalized row\r
+ *\r
+ * @exception StandardException thrown on failure \r
+ */\r
+ private ExecRow normalizeRow(ExecRow sourceRow) throws StandardException\r
+ {\r
+ int whichCol;\r
+\r
+ if (desiredTypes == null)\r
+ {\r
+ desiredTypes = new DataTypeDescriptor[numCols];\r
+ for (whichCol = 1; whichCol <= numCols; whichCol++)\r
+ {\r
+ DataTypeDescriptor dtd = resultDescription.getColumnDescriptor(whichCol).getType();\r
+\r
+ desiredTypes[whichCol - 1] = dtd;\r
+ }\r
+\r
+ }\r
+\r
+ for (whichCol = 1; whichCol <= numCols; whichCol++)\r
+ {\r
+ DataValueDescriptor sourceCol = sourceRow.getColumn(whichCol);\r
+ if (sourceCol != null)\r
+ {\r
+ DataValueDescriptor normalizedCol;\r
+ // skip the before values in case of update\r
+ if (whichCol < startCol)\r
+ normalizedCol = sourceCol;\r
+ else\r
+ try {\r
+ normalizedCol = \r
+ desiredTypes[whichCol - 1].normalize(sourceCol, \r
+ normalizedRow.getColumn(whichCol));\r
+ } catch (StandardException se) {\r
+ // Catch illegal null insert and add column info\r
+ if (se.getMessageId().startsWith(SQLState.LANG_NULL_INTO_NON_NULL))\r
+ {\r
+ ResultColumnDescriptor columnDescriptor =\r
+ resultDescription.getColumnDescriptor(whichCol);\r
+ throw\r
+ StandardException.newException(SQLState.LANG_NULL_INTO_NON_NULL, \r
+ columnDescriptor.getName());\r
+ }\r
+ //just rethrow if not LANG_NULL_INTO_NON_NULL\r
+ throw se;\r
+ }\r
+\r
+ normalizedRow.setColumn(whichCol, normalizedCol);\r
+ }\r
+ }\r
+\r
+ return normalizedRow;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#updateRow\r
+ */\r
+ public void updateRow (ExecRow row) throws StandardException {\r
+ source.updateRow(row);\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#markRowAsDeleted\r
+ */\r
+ public void markRowAsDeleted() throws StandardException {\r
+ source.markRowAsDeleted();\r
+ }\r
+\r
+}\r