001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.NoPutResultSetImpl
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.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.io.FormatableBitSet;
027: import org.apache.derby.iapi.services.i18n.MessageService;
028:
029: import org.apache.derby.iapi.services.loader.GeneratedMethod;
030:
031: import org.apache.derby.iapi.services.sanity.SanityManager;
032:
033: import org.apache.derby.iapi.error.StandardException;
034:
035: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
036: import org.apache.derby.iapi.sql.conn.StatementContext;
037:
038: import org.apache.derby.iapi.sql.depend.DependencyManager;
039:
040: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
041: import org.apache.derby.iapi.sql.execute.ExecRow;
042: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
043: import org.apache.derby.iapi.sql.execute.ExecutionContext;
044: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
045: import org.apache.derby.iapi.sql.execute.ResultSetStatisticsFactory;
046: import org.apache.derby.iapi.sql.execute.TargetResultSet;
047:
048: import org.apache.derby.iapi.sql.Activation;
049: import org.apache.derby.iapi.sql.ResultDescription;
050:
051: import org.apache.derby.iapi.store.access.Qualifier;
052: import org.apache.derby.iapi.store.access.RowLocationRetRowSource;
053: import org.apache.derby.iapi.store.access.RowSource;
054:
055: import org.apache.derby.iapi.types.DataValueDescriptor;
056:
057: import org.apache.derby.iapi.types.Orderable;
058: import org.apache.derby.iapi.types.RowLocation;
059:
060: import java.sql.Timestamp;
061:
062: /**
063: * Abstract ResultSet with built in Activation support for operations that
064: * return rows but do not allow the caller to put data on output pipes. This
065: * implementation of ResultSet is meant to be overridden by subtypes in the
066: * execution engine. Its primary users will be DML operations that do not put
067: * data on output pipes, but simply return it due to being result sets
068: * themselves.
069: * <p>
070: * This abstract class does not define the entire ResultSet
071: * interface, but leaves the 'get' half of the interface
072: * for subtypes to implement. It is package-visible only,
073: * with its methods being public for exposure by its subtypes.
074: * <p>
075: */
076: abstract class NoPutResultSetImpl extends BasicNoPutResultSetImpl {
077: /* Set in constructor and not modified */
078: public final int resultSetNumber;
079:
080: /* fields used for formating run time statistics output */
081: protected String indent;
082: protected String subIndent;
083: protected int sourceDepth;
084:
085: // fields used when being called as a RowSource
086: private boolean needsRowLocation;
087: protected ExecRow clonedExecRow;
088: GeneratedMethod checkGM;
089: long heapConglomerate;
090: protected TargetResultSet targetResultSet;
091:
092: /* beetle 4464. compact flags into array of key column positions that we do check/skip nulls,
093: * so that we burn less cycles for each row, column.
094: */
095: protected int[] checkNullCols;
096: protected int cncLen;
097:
098: /**
099: * Constructor
100: *
101: * @param activation The activation
102: * @param resultSetNumber The resultSetNumber
103: * @param optimizerEstimatedRowCount The optimizer's estimated number
104: * of rows.
105: * @param optimizerEstimatedCost The optimizer's estimated cost
106: */
107: NoPutResultSetImpl(Activation activation, int resultSetNumber,
108: double optimizerEstimatedRowCount,
109: double optimizerEstimatedCost) {
110: super (null, activation, optimizerEstimatedRowCount,
111: optimizerEstimatedCost);
112:
113: if (SanityManager.DEBUG) {
114: SanityManager.ASSERT(activation != null,
115: "activation expected to be non-null");
116: SanityManager.ASSERT(resultSetNumber >= 0,
117: "resultSetNumber expected to be >= 0");
118: }
119: this .resultSetNumber = resultSetNumber;
120: }
121:
122: // NoPutResultSet interface
123:
124: /**
125: * Returns the description of the table's rows
126: */
127: public ResultDescription getResultDescription() {
128: return activation.getResultDescription();
129: }
130:
131: /**
132: Return my cursor name for JDBC. Can be null.
133: */
134: public String getCursorName() {
135:
136: String cursorName = activation.getCursorName();
137: if ((cursorName == null) && isForUpdate()) {
138:
139: activation.setCursorName(activation
140: .getLanguageConnectionContext()
141: .getUniqueCursorName());
142:
143: cursorName = activation.getCursorName();
144: }
145:
146: return cursorName;
147: }
148:
149: /** @see NoPutResultSet#resultSetNumber() */
150: public int resultSetNumber() {
151: return resultSetNumber;
152: }
153:
154: /**
155: Close needs to invalidate any dependent statements, if this is a cursor.
156: Must be called by any subclasses that override close().
157: @exception StandardException on error
158: */
159: public void close() throws StandardException {
160: if (!isOpen)
161: return;
162:
163: /* If this is the top ResultSet then we must
164: * close all of the open subqueries for the
165: * entire query.
166: */
167: if (isTopResultSet) {
168: int staLength = (subqueryTrackingArray == null) ? 0
169: : subqueryTrackingArray.length;
170:
171: for (int index = 0; index < staLength; index++) {
172: if (subqueryTrackingArray[index] == null) {
173: continue;
174: }
175: if (subqueryTrackingArray[index].isClosed()) {
176: continue;
177: }
178: subqueryTrackingArray[index].close();
179: }
180: }
181:
182: /*
183: ** If we are the activation's top result set, make it forget about
184: ** us, because we're closed now.
185: */
186: if (activation.getResultSet() == this ) {
187: activation.clearResultSet();
188: }
189:
190: isOpen = false;
191:
192: }
193:
194: /** @see NoPutResultSet#setTargetResultSet */
195: public void setTargetResultSet(TargetResultSet trs) {
196: targetResultSet = trs;
197: }
198:
199: /** @see NoPutResultSet#setNeedsRowLocation */
200: public void setNeedsRowLocation(boolean needsRowLocation) {
201: this .needsRowLocation = needsRowLocation;
202: }
203:
204: // RowSource interface
205:
206: /**
207: * @see RowSource#getValidColumns
208: */
209: public FormatableBitSet getValidColumns() {
210: // All columns are valid
211: return null;
212: }
213:
214: /**
215: * @see RowSource#getNextRowFromRowSource
216: * @exception StandardException on error
217: */
218: public DataValueDescriptor[] getNextRowFromRowSource()
219: throws StandardException {
220: ExecRow execRow = getNextRowCore();
221: if (execRow != null) {
222: /* Let the target preprocess the row. For now, this
223: * means doing an in place clone on any indexed columns
224: * to optimize cloning and so that we don't try to drain
225: * a stream multiple times. This is where we also
226: * enforce any check constraints.
227: */
228: clonedExecRow = targetResultSet
229: .preprocessSourceRow(execRow);
230:
231: return execRow.getRowArray();
232: }
233:
234: return null;
235: }
236:
237: /**
238: * @see RowSource#needsToClone
239: */
240: public boolean needsToClone() {
241: return (true);
242: }
243:
244: /**
245: * @see RowSource#closeRowSource
246: */
247: public void closeRowSource() {
248: // Do nothing here - actual work will be done in close()
249: }
250:
251: // RowLocationRetRowSource interface
252:
253: /**
254: * @see RowLocationRetRowSource#needsRowLocation
255: */
256: public boolean needsRowLocation() {
257: return needsRowLocation;
258: }
259:
260: /**
261: * @see RowLocationRetRowSource#rowLocation
262: * @exception StandardException on error
263: */
264: public void rowLocation(RowLocation rl) throws StandardException {
265: targetResultSet.changedRow(clonedExecRow, rl);
266: }
267:
268: // class implementation
269:
270: /**
271: * Clear the Orderable cache for each qualifier.
272: * (This should be done each time a scan/conglomerate with
273: * qualifiers is reopened.)
274: *
275: * @param qualifiers The Qualifiers to clear
276: */
277: protected void clearOrderableCache(Qualifier[][] qualifiers)
278: throws StandardException {
279: // Clear the Qualifiers's Orderable cache
280: if (qualifiers != null) {
281: Qualifier qual;
282: for (int term = 0; term < qualifiers.length; term++) {
283: for (int index = 0; index < qualifiers[term].length; index++) {
284: qual = qualifiers[term][index];
285: qual.clearOrderableCache();
286: /* beetle 4880 performance enhancement and avoid deadlock while pushing
287: * down method call to store: pre-evaluate.
288: */
289: if (((GenericQualifier) qual).variantType != Qualifier.VARIANT)
290: qual.getOrderable(); // ignore return value
291: }
292: }
293: }
294: }
295:
296: /* Support methods for RowSource interface.
297: * These methods are used for enabling check constraint enforcement and
298: * replication logging for published tables when we are a RowSource.
299: */
300:
301: /**
302: * Set the GeneratedMethod for enforcing check constraints
303: *
304: * @param checkGM The GeneratedMethod for enforcing any check constraints.
305: */
306: protected void setCheckConstraints(GeneratedMethod checkGM) {
307: this .checkGM = checkGM;
308: }
309:
310: /**
311: * Set the heap conglomerate number (used in enforcing check constraints)
312: *
313: * @param heapConglomerate The heap conglomerate number.
314: */
315: protected void setHeapConglomerate(long heapConglomerate) {
316: this .heapConglomerate = heapConglomerate;
317: }
318:
319: /**
320: * Set the current row to the row passed in.
321: *
322: * @param row the new current row
323: *
324: */
325: public final void setCurrentRow(ExecRow row) {
326: activation.setCurrentRow(row, resultSetNumber);
327: currentRow = row;
328: }
329:
330: /**
331: * Clear the current row
332: *
333: */
334: public final void clearCurrentRow() {
335: currentRow = null;
336: activation.clearCurrentRow(resultSetNumber);
337: }
338:
339: /**
340: * Is this ResultSet or it's source result set for update
341: * This method will be overriden in the inherited Classes
342: * if it is true
343: * @return Whether or not the result set is for update.
344: */
345: public boolean isForUpdate() {
346: return false;
347: }
348:
349: /**
350: * Return true if we should skip the scan due to nulls in the start
351: * or stop position when the predicate on the column(s) in question
352: * do not implement ordered null semantics. beetle 4464, we also compact
353: * the areNullsOrdered flags into checkNullCols here.
354: *
355: * @param startPosition An index row for the start position
356: * @param stopPosition An index row for the stop position
357: *
358: * @return true means not to do the scan
359: */
360: protected boolean skipScan(ExecIndexRow startPosition,
361: ExecIndexRow stopPosition) throws StandardException {
362: int nStartCols = (startPosition == null) ? 0 : startPosition
363: .nColumns();
364: int nStopCols = (stopPosition == null) ? 0 : stopPosition
365: .nColumns();
366:
367: /* Two facts 1) for start and stop key column positions, one has to be the prefix
368: * of the other, 2) startPosition.areNullsOrdered(i) can't be different from
369: * stopPosition.areNullsOrdered(i) unless the case "c > null and c < 5", (where c is
370: * non-nullable), in which we skip the scan anyway.
371: * So we can just use the longer one to get checkNullCols.
372: */
373: boolean startKeyLonger = false;
374: int size = nStopCols;
375: if (nStartCols > nStopCols) {
376: startKeyLonger = true;
377: size = nStartCols;
378: }
379: if (size == 0)
380: return false;
381: if ((checkNullCols == null) || (checkNullCols.length < size))
382: checkNullCols = new int[size];
383: cncLen = 0;
384:
385: boolean returnValue = false;
386: for (int position = 0; position < nStartCols; position++) {
387: if (!startPosition.areNullsOrdered(position)) {
388: if (startKeyLonger)
389: checkNullCols[cncLen++] = position + 1;
390: if (startPosition.getColumn(position + 1).isNull()) {
391: returnValue = true;
392: if (!startKeyLonger)
393: break;
394: }
395: }
396: }
397: if (startKeyLonger && returnValue)
398: return true;
399: for (int position = 0; position < nStopCols; position++) {
400: if (!stopPosition.areNullsOrdered(position)) {
401: if (!startKeyLonger)
402: checkNullCols[cncLen++] = position + 1;
403: if (returnValue)
404: continue;
405: if (stopPosition.getColumn(position + 1).isNull()) {
406: returnValue = true;
407: if (startKeyLonger)
408: break;
409: }
410: }
411: }
412:
413: return returnValue;
414: }
415:
416: /**
417: * Return true if we should skip the scan due to nulls in the row
418: * when the start or stop positioners on the columns containing
419: * null do not implement ordered null semantics.
420: *
421: * @param row An index row
422: *
423: * @return true means skip the row because it has null
424: */
425: protected boolean skipRow(ExecRow row) throws StandardException {
426: for (int i = 0; i < cncLen; i++) {
427: if (row.getColumn(checkNullCols[i]).isNull())
428: return true;
429: }
430:
431: return false;
432: }
433:
434: /**
435: * Return a 2-d array of Qualifiers as a String
436: */
437: public static String printQualifiers(Qualifier[][] qualifiers) {
438: String idt = "";
439:
440: String output = "";
441: if (qualifiers == null) {
442: return idt
443: + MessageService.getTextMessage(SQLState.LANG_NONE);
444: }
445:
446: for (int term = 0; term < qualifiers.length; term++) {
447: for (int i = 0; i < qualifiers[term].length; i++) {
448: Qualifier qual = qualifiers[term][i];
449:
450: output = idt
451: + output
452: + MessageService.getTextMessage(
453: SQLState.LANG_COLUMN_ID_ARRAY, String
454: .valueOf(term), String
455: .valueOf(i)) + ": "
456: + qual.getColumnId() + "\n";
457:
458: int operator = qual.getOperator();
459: String opString = null;
460: switch (operator) {
461: case Orderable.ORDER_OP_EQUALS:
462: opString = "=";
463: break;
464:
465: case Orderable.ORDER_OP_LESSOREQUALS:
466: opString = "<=";
467: break;
468:
469: case Orderable.ORDER_OP_LESSTHAN:
470: opString = "<";
471: break;
472:
473: default:
474: if (SanityManager.DEBUG) {
475: SanityManager.THROWASSERT("Unknown operator "
476: + operator);
477: }
478:
479: // NOTE: This does not have to be internationalized, because
480: // this code should never be reached.
481: opString = "unknown value (" + operator + ")";
482: break;
483: }
484: output = output
485: + idt
486: + MessageService
487: .getTextMessage(SQLState.LANG_OPERATOR)
488: + ": "
489: + opString
490: + "\n"
491: + idt
492: + MessageService
493: .getTextMessage(SQLState.LANG_ORDERED_NULLS)
494: + ": "
495: + qual.getOrderedNulls()
496: + "\n"
497: + idt
498: + MessageService
499: .getTextMessage(SQLState.LANG_UNKNOWN_RETURN_VALUE)
500: + ": "
501: + qual.getUnknownRV()
502: + "\n"
503: + idt
504: + MessageService
505: .getTextMessage(SQLState.LANG_NEGATE_COMPARISON_RESULT)
506: + ": " + qual.negateCompareResult() + "\n";
507: }
508: }
509:
510: return output;
511: }
512:
513: /**
514: * @see NoPutResultSet#updateRow
515: *
516: * This method is result sets used for scroll insensitive updatable
517: * result sets for other result set it is a no-op.
518: */
519: public void updateRow(ExecRow row) throws StandardException {
520: // Only ResultSets of type Scroll Insensitive implement
521: // detectability, so for other result sets this method
522: // is a no-op
523: }
524:
525: /**
526: * @see NoPutResultSet#markRowAsDeleted
527: *
528: * This method is result sets used for scroll insensitive updatable
529: * result sets for other result set it is a no-op.
530: */
531: public void markRowAsDeleted() throws StandardException {
532: // Only ResultSets of type Scroll Insensitive implement
533: // detectability, so for other result sets this method
534: // is a no-op
535: }
536:
537: /**
538: * @see NoPutResultSet#positionScanAtRowLocation
539: *
540: * This method is result sets used for scroll insensitive updatable
541: * result sets for other result set it is a no-op.
542: */
543: public void positionScanAtRowLocation(RowLocation rl)
544: throws StandardException {
545: // Only ResultSets of type Scroll Insensitive implement
546: // detectability, so for other result sets this method
547: // is a no-op
548: }
549:
550: }
|