001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.NormalizeResultSet
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.sql.Activation;
032: import org.apache.derby.iapi.sql.ResultDescription;
033: import org.apache.derby.iapi.sql.ResultSet;
034: import org.apache.derby.iapi.sql.ResultColumnDescriptor;
035: import org.apache.derby.iapi.types.TypeId;
036:
037: import org.apache.derby.iapi.types.DataValueDescriptor;
038: import org.apache.derby.iapi.types.DataTypeDescriptor;
039: import org.apache.derby.iapi.types.RowLocation;
040:
041: import org.apache.derby.iapi.sql.execute.CursorResultSet;
042: import org.apache.derby.iapi.sql.execute.ExecRow;
043: import org.apache.derby.iapi.sql.execute.ExecutionContext;
044: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
045:
046: import org.apache.derby.iapi.services.loader.GeneratedMethod;
047:
048: import org.apache.derby.iapi.error.StandardException;
049: import org.apache.derby.iapi.reference.SQLState;
050:
051: /**
052: * Cast the rows from the source result set to match the format of the
053: * result set for the entire statement.
054: */
055:
056: class NormalizeResultSet extends NoPutResultSetImpl implements
057: CursorResultSet {
058: /*
059: ** Set in constructor and not altered during life of object.
060: */
061:
062: public NoPutResultSet source;
063: private ExecRow normalizedRow;
064: private int numCols;
065: private int startCol;
066:
067: /* RESOLVE - We need to pass the ResultDescription for this ResultSet
068: * as a parameter to the constructor and use it instead of the one from
069: * the activation
070: */
071: private ResultDescription resultDescription;
072:
073: /* info for caching DTSs */
074: private DataTypeDescriptor[] desiredTypes;
075:
076: /**
077: * Constructor for a NormalizeResultSet
078: *
079: * @param source The NoPutResultSet from which to get rows
080: * to be normalized
081: * @param activation The activation for this execution
082: * @param resultSetNumber The resultSetNumber
083: * @param erdNumber The integer for the ResultDescription
084: *
085: * @exception StandardException on error
086: */
087:
088: public NormalizeResultSet(NoPutResultSet source,
089: Activation activation, int resultSetNumber, int erdNumber,
090: double optimizerEstimatedRowCount,
091: double optimizerEstimatedCost, boolean forUpdate)
092: throws StandardException {
093: super (activation, resultSetNumber, optimizerEstimatedRowCount,
094: optimizerEstimatedCost);
095: this .source = source;
096:
097: if (SanityManager.DEBUG) {
098: if (!(activation.getPreparedStatement().getSavedObject(
099: erdNumber) instanceof ResultDescription)) {
100: SanityManager
101: .THROWASSERT("activation.getPreparedStatement().getSavedObject(erdNumber) "
102: + "expected to be instanceof ResultDescription");
103: }
104:
105: // source expected to be non-null, mystery stress test bug
106: // - sometimes get NullPointerException in openCore().
107: SanityManager.ASSERT(source != null,
108: "NRS(), source expected to be non-null");
109: }
110:
111: this .resultDescription = (ResultDescription) activation
112: .getPreparedStatement().getSavedObject(erdNumber);
113:
114: numCols = resultDescription.getColumnCount();
115:
116: /*
117: An update row, for an update statement which sets n columns; i.e
118: UPDATE tab set x,y,z=.... where ...;
119: has,
120: before values of x,y,z after values of x,y,z and rowlocation.
121: need only normalize after values of x,y,z.
122: i.e insead of starting at index = 1, I need to start at index = 4.
123: also I needn't normalize the last value in the row.
124: */
125: startCol = (forUpdate) ? ((numCols - 1) / 2) + 1 : 1;
126: normalizedRow = activation.getExecutionFactory().getValueRow(
127: numCols);
128: constructorTime += getElapsedMillis(beginTime);
129: }
130:
131: //
132: // ResultSet interface (leftover from NoPutResultSet)
133: //
134:
135: /**
136: * open a scan on the source. scan parameters are evaluated
137: * at each open, so there is probably some way of altering
138: * their values...
139: *
140: * @exception StandardException thrown on failure
141: */
142: public void openCore() throws StandardException {
143: beginTime = getCurrentTimeMillis();
144: if (SanityManager.DEBUG)
145: SanityManager.ASSERT(!isOpen,
146: "NormalizeResultSet already open");
147:
148: // source expected to be non-null, mystery stress test bug
149: // - sometimes get NullPointerException in openCore().
150: if (SanityManager.DEBUG) {
151: SanityManager.ASSERT(source != null,
152: "NRS().openCore(), source expected to be non-null");
153: }
154:
155: source.openCore();
156: isOpen = true;
157: numOpens++;
158:
159: openTime += getElapsedMillis(beginTime);
160: }
161:
162: /**
163: * reopen a scan on the table. scan parameters are evaluated
164: * at each open, so there is probably some way of altering
165: * their values...
166: *
167: * @exception StandardException thrown if cursor finished.
168: */
169: public void reopenCore() throws StandardException {
170: beginTime = getCurrentTimeMillis();
171: if (SanityManager.DEBUG)
172: SanityManager.ASSERT(isOpen,
173: "NormalizeResultSet already open");
174:
175: source.reopenCore();
176: numOpens++;
177:
178: openTime += getElapsedMillis(beginTime);
179: }
180:
181: /**
182: *
183: * @exception StandardException thrown on failure
184: */
185: public ExecRow getNextRowCore() throws StandardException {
186: ExecRow sourceRow = null;
187: ExecRow result = null;
188:
189: beginTime = getCurrentTimeMillis();
190: if (!isOpen)
191: throw StandardException.newException(
192: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
193:
194: sourceRow = source.getNextRowCore();
195: if (sourceRow != null) {
196: result = normalizeRow(sourceRow);
197: rowsSeen++;
198: }
199:
200: currentRow = result;
201: setCurrentRow(result);
202:
203: nextTime += getElapsedMillis(beginTime);
204: return result;
205: }
206:
207: /**
208: * If the result set has been opened,
209: * close the open scan.
210: *
211: * @exception StandardException thrown on error
212: */
213: public void close() throws StandardException {
214: beginTime = getCurrentTimeMillis();
215: if (isOpen) {
216: currentRow = null;
217: source.close();
218:
219: super .close();
220: } else if (SanityManager.DEBUG)
221: SanityManager.DEBUG("CloseRepeatInfo",
222: "Close of NormalizeResultSet repeated");
223:
224: closeTime += getElapsedMillis(beginTime);
225: }
226:
227: /**
228: * Return the total amount of time spent in this ResultSet
229: *
230: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
231: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
232: *
233: * @return long The total amount of time spent (in milliseconds).
234: */
235: public long getTimeSpent(int type) {
236: long totTime = constructorTime + openTime + nextTime
237: + closeTime;
238:
239: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
240: return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);
241: } else {
242: return totTime;
243: }
244: }
245:
246: //
247: // CursorResultSet interface
248: //
249:
250: /**
251: * Gets information from its source. We might want
252: * to have this take a CursorResultSet in its constructor some day,
253: * instead of doing a cast here?
254: *
255: * @see CursorResultSet
256: *
257: * @return the row location of the current cursor row.
258: *
259: * @exception StandardException thrown on failure
260: */
261: public RowLocation getRowLocation() throws StandardException {
262: if (SanityManager.DEBUG)
263: SanityManager.ASSERT(source instanceof CursorResultSet,
264: "source is not a cursorresultset");
265: return ((CursorResultSet) source).getRowLocation();
266: }
267:
268: /**
269: * Gets information from last getNextRow call.
270: *
271: * @see CursorResultSet
272: *
273: * @return the last row returned.
274: */
275: /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
276: * once there is such a method. (currentRow is redundant)
277: */
278: public ExecRow getCurrentRow() {
279: return currentRow;
280: }
281:
282: //
283: // class implementation
284: //
285: /**
286: * Normalize a row. For now, this means calling constructors through
287: * the type services to normalize a type to itself. For example,
288: * if you're putting a char(30) value into a char(15) column, it
289: * calls a SQLChar constructor with the char(30) value, and the
290: * constructor truncates the value and makes sure that no non-blank
291: * characters are truncated.
292: *
293: * In the future, this mechanism will be extended to do type conversions,
294: * as well. I didn't implement type conversions yet because it looks
295: * like a lot of work, and we needed char and varchar right away.
296: *
297: * @param sourceRow The row to normalize
298: *
299: * @return The normalized row
300: *
301: * @exception StandardException thrown on failure
302: */
303: private ExecRow normalizeRow(ExecRow sourceRow)
304: throws StandardException {
305: int whichCol;
306:
307: if (desiredTypes == null) {
308: desiredTypes = new DataTypeDescriptor[numCols];
309: for (whichCol = 1; whichCol <= numCols; whichCol++) {
310: DataTypeDescriptor dtd = resultDescription
311: .getColumnDescriptor(whichCol).getType();
312:
313: desiredTypes[whichCol - 1] = dtd;
314: }
315:
316: }
317:
318: for (whichCol = 1; whichCol <= numCols; whichCol++) {
319: DataValueDescriptor sourceCol = sourceRow
320: .getColumn(whichCol);
321: if (sourceCol != null) {
322: DataValueDescriptor normalizedCol;
323: // skip the before values in case of update
324: if (whichCol < startCol)
325: normalizedCol = sourceCol;
326: else
327: try {
328: normalizedCol = desiredTypes[whichCol - 1]
329: .normalize(sourceCol, normalizedRow
330: .getColumn(whichCol));
331: } catch (StandardException se) {
332: // Catch illegal null insert and add column info
333: if (se.getMessageId().startsWith(
334: SQLState.LANG_NULL_INTO_NON_NULL)) {
335: ResultColumnDescriptor columnDescriptor = resultDescription
336: .getColumnDescriptor(whichCol);
337: throw StandardException.newException(
338: SQLState.LANG_NULL_INTO_NON_NULL,
339: columnDescriptor.getName());
340: }
341: //just rethrow if not LANG_NULL_INTO_NON_NULL
342: throw se;
343: }
344:
345: normalizedRow.setColumn(whichCol, normalizedCol);
346: }
347: }
348:
349: return normalizedRow;
350: }
351:
352: /**
353: * @see NoPutResultSet#updateRow
354: */
355: public void updateRow(ExecRow row) throws StandardException {
356: source.updateRow(row);
357: }
358:
359: /**
360: * @see NoPutResultSet#markRowAsDeleted
361: */
362: public void markRowAsDeleted() throws StandardException {
363: source.markRowAsDeleted();
364: }
365:
366: }
|