001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.DependentResultSet
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.sanity.SanityManager;
025: import org.apache.derby.iapi.error.StandardException;
026: import org.apache.derby.iapi.types.DataValueFactory;
027: import org.apache.derby.iapi.types.RowLocation;
028: import org.apache.derby.iapi.sql.execute.ExecRow;
029: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
030: import org.apache.derby.iapi.sql.execute.ScanQualifier;
031: import org.apache.derby.iapi.store.access.ConglomerateController;
032: import org.apache.derby.iapi.store.access.ScanController;
033: import org.apache.derby.iapi.store.access.TransactionController;
034: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
035: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
036: import org.apache.derby.iapi.sql.execute.CursorResultSet;
037: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
038: import org.apache.derby.iapi.sql.Activation;
039: import org.apache.derby.iapi.types.RefDataValue;
040: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
041: import org.apache.derby.iapi.services.io.FormatableBitSet;
042: import org.apache.derby.iapi.services.loader.GeneratedMethod;
043: import org.apache.derby.iapi.store.access.Qualifier;
044: import org.apache.derby.iapi.sql.execute.ExecutionContext;
045: import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;
046: import java.util.Vector;
047: import java.util.Properties;
048: import org.apache.derby.iapi.reference.SQLState;
049: import org.apache.derby.iapi.services.i18n.MessageService;
050:
051: /**
052: * DependentResultSet should be used by only ON DELETE CASCADE/ON DELETE SET NULL ref
053: * actions implementation to gather the rows from the dependent tables.
054: * Idea is to scan the foreign key index for the rows in
055: * the source table matelized temporary result set. Scanning of foreign key index gives us the
056: * rows that needs to be deleted on dependent tables. Using the row location
057: * we got from the index , base row is fetched.
058: */
059: class DependentResultSet extends NoPutResultSetImpl implements
060: CursorResultSet {
061:
062: ConglomerateController heapCC;
063: RowLocation baseRowLocation; // base row location we got from the index
064: ExecRow indexRow; //templeate to fetch the index row
065: IndexRow indexQualifierRow; // template for the index qualifier row
066: ScanController indexSC; // Index Scan Controller
067: StaticCompiledOpenConglomInfo indexScoci;
068: DynamicCompiledOpenConglomInfo indexDcoci;
069: int numFkColumns;
070: boolean isOpen; // source result set is opened or not
071: boolean deferred;
072: TemporaryRowHolderResultSet source; // Current parent table result set
073: TransactionController tc;
074: String parentResultSetId;
075: int[] fkColArray;
076: RowLocation rowLocation;
077: TemporaryRowHolder[] sourceRowHolders;
078: TemporaryRowHolderResultSet[] sourceResultSets;
079: int[] sourceOpened;
080: int sArrayIndex;
081: Vector sVector;
082:
083: protected ScanController scanController;
084: protected boolean scanControllerOpened;
085: protected boolean isKeyed;
086: protected boolean firstScan = true;
087: protected ExecIndexRow startPosition;
088: protected ExecIndexRow stopPosition;
089: protected ExecRow candidate;
090:
091: // set in constructor and not altered during
092: // life of object.
093: protected long conglomId;
094: protected DynamicCompiledOpenConglomInfo heapDcoci;
095: protected StaticCompiledOpenConglomInfo heapScoci;
096: protected GeneratedMethod resultRowAllocator;
097: protected GeneratedMethod startKeyGetter;
098: protected int startSearchOperator;
099: protected GeneratedMethod stopKeyGetter;
100: protected int stopSearchOperator;
101: protected Qualifier[][] qualifiers;
102: public String tableName;
103: public String userSuppliedOptimizerOverrides;
104: public String indexName;
105: protected boolean runTimeStatisticsOn;
106: protected FormatableBitSet accessedCols;
107: public int rowsPerRead;
108: public boolean forUpdate;
109: private boolean sameStartStopPosition;
110: public int isolationLevel;
111: public int lockMode;
112:
113: // Run time statistics
114: private Properties scanProperties;
115: public String startPositionString;
116: public String stopPositionString;
117: public boolean isConstraint;
118: public boolean coarserLock;
119: public boolean oneRowScan;
120: protected long rowsThisScan;
121:
122: //
123: // class interface
124: //
125: DependentResultSet(long conglomId,
126: StaticCompiledOpenConglomInfo scoci, Activation activation,
127: GeneratedMethod resultRowAllocator, int resultSetNumber,
128: GeneratedMethod startKeyGetter, int startSearchOperator,
129: GeneratedMethod stopKeyGetter, int stopSearchOperator,
130: boolean sameStartStopPosition, Qualifier[][] qualifiers,
131: String tableName, String userSuppliedOptimizerOverrides,
132: String indexName, boolean isConstraint, boolean forUpdate,
133: int colRefItem, int lockMode, boolean tableLocked,
134: int isolationLevel, int rowsPerRead, boolean oneRowScan,
135: double optimizerEstimatedRowCount,
136: double optimizerEstimatedCost, String parentResultSetId,
137: long fkIndexConglomId, int fkColArrayItem, int rltItem)
138: throws StandardException {
139: super (activation, resultSetNumber, optimizerEstimatedRowCount,
140: optimizerEstimatedCost);
141:
142: this .conglomId = conglomId;
143:
144: /* Static info created at compile time and can be shared across
145: * instances of the plan.
146: * Dynamic info created on 1st instantiation of this ResultSet as
147: * it cannot be shared.
148: */
149: this .heapScoci = scoci;
150: heapDcoci = activation.getTransactionController()
151: .getDynamicCompiledConglomInfo(conglomId);
152:
153: if (SanityManager.DEBUG) {
154: SanityManager.ASSERT(activation != null,
155: "table scan must get activation context");
156: SanityManager.ASSERT(resultRowAllocator != null,
157: "table scan must get row allocator");
158: if (sameStartStopPosition) {
159: SanityManager
160: .ASSERT(stopKeyGetter == null,
161: "stopKeyGetter expected to be null when sameStartStopPosition is true");
162: }
163: }
164:
165: this .resultRowAllocator = resultRowAllocator;
166:
167: this .startKeyGetter = startKeyGetter;
168: this .startSearchOperator = startSearchOperator;
169: this .stopKeyGetter = stopKeyGetter;
170: this .stopSearchOperator = stopSearchOperator;
171: this .sameStartStopPosition = sameStartStopPosition;
172: this .qualifiers = qualifiers;
173: this .tableName = tableName;
174: this .userSuppliedOptimizerOverrides = userSuppliedOptimizerOverrides;
175: this .indexName = "On Foreign Key"; // RESOLVE , get actual indexName;
176: this .isConstraint = isConstraint;
177: this .forUpdate = forUpdate;
178: this .rowsPerRead = rowsPerRead;
179: this .oneRowScan = oneRowScan;
180:
181: // retrieve the valid column list from
182: // the saved objects, if it exists
183: this .accessedCols = null;
184: if (colRefItem != -1) {
185: this .accessedCols = (FormatableBitSet) (activation
186: .getPreparedStatement().getSavedObject(colRefItem));
187: }
188:
189: //unless the table locking is specified in sys.systables,
190: //irrespective of what optimizer says choose record level
191: //locking for dependent result sets.
192: if (!tableLocked) {
193: this .lockMode = TransactionController.MODE_RECORD;
194: } else {
195: this .lockMode = lockMode;
196: }
197:
198: //Because the scan for the tables in this result set are done
199: //internally for delete cascades, isolation should be set to
200: //REPEATABLE READ irrespective what the user level isolation level is.
201: this .isolationLevel = TransactionController.ISOLATION_REPEATABLE_READ;
202:
203: runTimeStatisticsOn = (activation != null && activation
204: .getLanguageConnectionContext()
205: .getRunTimeStatisticsMode());
206:
207: /* Only call row allocators once */
208: candidate = (ExecRow) resultRowAllocator.invoke(activation);
209:
210: tc = activation.getTransactionController();
211: //values required to scan the forein key index.
212: indexDcoci = tc.getDynamicCompiledConglomInfo(fkIndexConglomId);
213: indexScoci = tc.getStaticCompiledConglomInfo(fkIndexConglomId);
214:
215: this .parentResultSetId = parentResultSetId;
216: this .fkColArray = (int[]) (activation.getPreparedStatement()
217: .getSavedObject(fkColArrayItem));
218:
219: this .rowLocation = (RowLocation) (activation
220: .getPreparedStatement().getSavedObject(rltItem));
221: numFkColumns = fkColArray.length;
222: indexQualifierRow = new IndexRow(numFkColumns);
223: constructorTime += getElapsedMillis(beginTime);
224: }
225:
226: /**
227: * Get a scan controller positioned using searchRow as
228: * the start/stop position. The assumption is that searchRow
229: * is of the same format as the index being opened.
230: * @param searchRow the row to match
231: * @exception StandardException on error
232: */
233:
234: private ScanController openIndexScanController(ExecRow searchRow)
235: throws StandardException {
236: setupQualifierRow(searchRow);
237: indexSC = tc.openCompiledScan(false, // hold
238: TransactionController.OPENMODE_FORUPDATE, // update only
239: lockMode, // lock Mode
240: isolationLevel, //isolation level
241: (FormatableBitSet) null, // retrieve all fields
242: indexQualifierRow.getRowArray(), // startKeyValue
243: ScanController.GE, // startSearchOp
244: null, // qualifier
245: indexQualifierRow.getRowArray(), // stopKeyValue
246: ScanController.GT, // stopSearchOp
247: indexScoci, indexDcoci);
248:
249: return indexSC;
250:
251: }
252:
253: //reopen the scan with a differnt search row
254: private void reopenIndexScanController(ExecRow searchRow)
255: throws StandardException {
256:
257: setupQualifierRow(searchRow);
258: indexSC.reopenScan(indexQualifierRow.getRowArray(), // startKeyValue
259: ScanController.GE, // startSearchOp
260: null, // qualifier
261: indexQualifierRow.getRowArray(), // stopKeyValue
262: ScanController.GT // stopSearchOp
263: );
264: }
265:
266: /*
267: ** Do reference copy for the qualifier row. No cloning.
268: ** So we cannot get another row until we are done with
269: ** this one.
270: */
271: private void setupQualifierRow(ExecRow searchRow) {
272: Object[] indexColArray = indexQualifierRow.getRowArray();
273: Object[] baseColArray = searchRow.getRowArray();
274:
275: for (int i = 0; i < numFkColumns; i++) {
276: indexColArray[i] = baseColArray[fkColArray[i] - 1];
277: }
278: }
279:
280: private void openIndexScan(ExecRow searchRow)
281: throws StandardException {
282:
283: if (indexSC == null) {
284: indexSC = openIndexScanController(searchRow);
285: //create a template for the index row
286: indexRow = indexQualifierRow.getClone();
287: indexRow
288: .setColumn(numFkColumns + 1, rowLocation.getClone());
289:
290: } else {
291: reopenIndexScanController(searchRow);
292: }
293: }
294:
295: /**
296: Fetch a row from the index scan.
297:
298: @return The row or null. Note that the next call to fetch will
299: replace the columns in the returned row.
300: @exception StandardException Ooops
301: */
302: private ExecRow fetchIndexRow() throws StandardException {
303: if (!indexSC.fetchNext(indexRow.getRowArray())) {
304: return null;
305: }
306: return indexRow;
307: }
308:
309: /**
310: Fetch the base row corresponding to the current index row
311:
312: @return The base row row or null.
313: @exception StandardException Ooops
314: */
315: private ExecRow fetchBaseRow() throws StandardException {
316:
317: if (currentRow == null) {
318: currentRow = getCompactRow(candidate, accessedCols,
319: (FormatableBitSet) null, isKeyed);
320: }
321:
322: baseRowLocation = (RowLocation) indexRow.getColumn(indexRow
323: .getRowArray().length);
324: boolean base_row_exists = heapCC.fetch(baseRowLocation,
325: candidate.getRowArray(), accessedCols);
326:
327: if (SanityManager.DEBUG) {
328: SanityManager.ASSERT(base_row_exists,
329: "base row disappeared.");
330: }
331:
332: return currentRow;
333: }
334:
335: ExecRow searchRow = null; //the current row we are searching for
336:
337: //this function will return an index row on dependent table
338: public ExecRow getNextRowCore() throws StandardException {
339:
340: beginTime = getCurrentTimeMillis();
341: if (searchRow == null) {
342: //we are searching for a row first time
343: if ((searchRow = getNextParentRow()) != null)
344: openIndexScan(searchRow);
345: }
346:
347: ExecRow currentIndexRow = null;
348: while (searchRow != null) {
349: //get if the current search row has more
350: //than one row in the dependent tables
351: currentIndexRow = fetchIndexRow();
352:
353: if (currentIndexRow != null)
354: break;
355: if ((searchRow = getNextParentRow()) != null)
356: openIndexScan(searchRow);
357: }
358:
359: nextTime += getElapsedMillis(beginTime);
360: if (currentIndexRow != null) {
361: rowsSeen++;
362: return fetchBaseRow();
363: } else {
364: return currentIndexRow;
365: }
366:
367: }
368:
369: //this function will return the rows from the parent result sets
370: private ExecRow getNextParentRow() throws StandardException {
371:
372: ExecRow cRow;
373: TemporaryRowHolder rowHolder;
374:
375: if (sourceOpened[sArrayIndex] == 0) {
376: rowHolder = sourceRowHolders[sArrayIndex];
377: source = (TemporaryRowHolderResultSet) rowHolder
378: .getResultSet();
379: source.open(); //open the cursor result set
380: sourceOpened[sArrayIndex] = -1;
381: sourceResultSets[sArrayIndex] = source;
382: }
383:
384: if (sourceOpened[sArrayIndex] == 1) {
385: source = sourceResultSets[sArrayIndex];
386: source.reStartScan(sourceRowHolders[sArrayIndex]
387: .getTemporaryConglomId(),
388: sourceRowHolders[sArrayIndex]
389: .getPositionIndexConglomId());
390: sourceOpened[sArrayIndex] = -1;
391:
392: }
393:
394: if (sVector.size() > sourceRowHolders.length) {
395: addNewSources();
396: }
397:
398: cRow = source.getNextRow();
399: while (cRow == null
400: && (sArrayIndex + 1) < sourceRowHolders.length) {
401:
402: //opening the next source;
403: sArrayIndex++;
404: if (sourceOpened[sArrayIndex] == 0) {
405: rowHolder = sourceRowHolders[sArrayIndex];
406: source = (TemporaryRowHolderResultSet) rowHolder
407: .getResultSet();
408: source.open(); //open the cursor result set
409: sourceOpened[sArrayIndex] = -1;
410: sourceResultSets[sArrayIndex] = source;
411: }
412:
413: if (sourceOpened[sArrayIndex] == 1) {
414: source = sourceResultSets[sArrayIndex];
415: source.reStartScan(sourceRowHolders[sArrayIndex]
416: .getTemporaryConglomId(),
417: sourceRowHolders[sArrayIndex]
418: .getPositionIndexConglomId());
419: sourceOpened[sArrayIndex] = -1;
420: }
421:
422: cRow = source.getNextRow();
423: }
424:
425: if (cRow == null) {
426: //which means no source has any more currently rows.
427: sArrayIndex = 0;
428: //mark all the sources to restartScan.
429: for (int i = 0; i < sourceOpened.length; i++)
430: sourceOpened[i] = 1;
431: }
432:
433: return cRow;
434: }
435:
436: /*
437: ** Open the heap Conglomerate controller
438: **
439: ** @param transaction controller will open one if null
440: */
441: public ConglomerateController openHeapConglomerateController()
442: throws StandardException {
443: return tc.openCompiledConglomerate(false,
444: TransactionController.OPENMODE_FORUPDATE, lockMode,
445: isolationLevel, heapScoci, heapDcoci);
446: }
447:
448: /**
449: Close the all the opens we did in this result set.
450: */
451: public void close() throws StandardException {
452: //save the information for the runtime stastics
453: // This is where we get the scan properties for the reference index scans
454: if (runTimeStatisticsOn) {
455: startPositionString = printStartPosition();
456: stopPositionString = printStopPosition();
457: scanProperties = getScanProperties();
458: }
459:
460: if (indexSC != null) {
461: indexSC.close();
462: indexSC = null;
463: }
464:
465: if (heapCC != null) {
466: heapCC.close();
467: heapCC = null;
468: }
469: if (isOpen) {
470: source.close();
471: }
472:
473: closeTime += getElapsedMillis(beginTime);
474: }
475:
476: public void finish() throws StandardException {
477: if (source != null)
478: source.finish();
479: finishAndRTS();
480: }
481:
482: public void openCore() throws StandardException {
483:
484: sVector = activation.getParentResultSet(parentResultSetId);
485: int size = sVector.size();
486: sourceRowHolders = new TemporaryRowHolder[size];
487: sourceOpened = new int[size];
488: sourceResultSets = new TemporaryRowHolderResultSet[size];
489: for (int i = 0; i < size; i++) {
490: sourceRowHolders[i] = (TemporaryRowHolder) sVector
491: .elementAt(i);
492: sourceOpened[i] = 0;
493: }
494:
495: //open the table scan
496: heapCC = openHeapConglomerateController();
497: numOpens++;
498: openTime += getElapsedMillis(beginTime);
499: }
500:
501: private void addNewSources() {
502: int size = sVector.size();
503: TemporaryRowHolder[] tsourceRowHolders = new TemporaryRowHolder[size];
504: int[] tsourceOpened = new int[size];
505: TemporaryRowHolderResultSet[] tsourceResultSets = new TemporaryRowHolderResultSet[size];
506:
507: //copy the source we have now
508: System.arraycopy(sourceRowHolders, 0, tsourceRowHolders, 0,
509: sourceRowHolders.length);
510: System.arraycopy(sourceOpened, 0, tsourceOpened, 0,
511: sourceOpened.length);
512: System.arraycopy(sourceResultSets, 0, tsourceResultSets, 0,
513: sourceResultSets.length);
514:
515: //copy the new sources
516: for (int i = sourceRowHolders.length; i < size; i++) {
517: tsourceRowHolders[i] = (TemporaryRowHolder) sVector
518: .elementAt(i);
519: tsourceOpened[i] = 0;
520: }
521:
522: sourceRowHolders = tsourceRowHolders;
523: sourceOpened = tsourceOpened;
524: sourceResultSets = tsourceResultSets;
525: }
526:
527: /**
528: * Can we get instantaneous locks when getting share row
529: * locks at READ COMMITTED.
530: */
531: private boolean canGetInstantaneousLocks() {
532: return false;
533: }
534:
535: public long getTimeSpent(int type) {
536: return constructorTime + openTime + nextTime + closeTime;
537: }
538:
539: //Cursor result set information.
540: public RowLocation getRowLocation() throws StandardException {
541: return baseRowLocation;
542: }
543:
544: public ExecRow getCurrentRow() throws StandardException {
545: return currentRow;
546: }
547:
548: public Properties getScanProperties() {
549: if (scanProperties == null) {
550: scanProperties = new Properties();
551: }
552: try {
553: if (indexSC != null) {
554: indexSC.getScanInfo().getAllScanInfo(scanProperties);
555: /* Did we get a coarser lock due to
556: * a covering lock, lock escalation
557: * or configuration?
558: */
559: coarserLock = indexSC.isTableLocked()
560: && (lockMode == TransactionController.MODE_RECORD);
561: }
562: } catch (StandardException se) {
563: // ignore
564: }
565:
566: return scanProperties;
567: }
568:
569: public String printStartPosition() {
570: return printPosition(ScanController.GE, indexQualifierRow);
571: }
572:
573: public String printStopPosition() {
574: return printPosition(ScanController.GT, indexQualifierRow);
575: }
576:
577: /**
578: * Return a start or stop positioner as a String.
579: *
580: * If we already generated the information, then use
581: * that. Otherwise, invoke the activation to get it.
582: */
583: private String printPosition(int searchOperator,
584: ExecIndexRow positioner) {
585: String idt = "";
586: String output = "";
587:
588: String searchOp = null;
589: switch (searchOperator) {
590: case ScanController.GE:
591: searchOp = ">=";
592: break;
593:
594: case ScanController.GT:
595: searchOp = ">";
596: break;
597:
598: default:
599: if (SanityManager.DEBUG) {
600: SanityManager.THROWASSERT("Unknown search operator "
601: + searchOperator);
602: }
603:
604: // NOTE: This does not have to be internationalized because
605: // this code should never be reached.
606: searchOp = "unknown value (" + searchOperator + ")";
607: break;
608: }
609:
610: if (positioner != null) {
611: output = output
612: + "\t"
613: + MessageService.getTextMessage(
614: SQLState.LANG_POSITIONER, searchOp, String
615: .valueOf(positioner.nColumns()))
616: + "\n";
617:
618: output = output
619: + "\t"
620: + MessageService
621: .getTextMessage(SQLState.LANG_ORDERED_NULL_SEMANTICS)
622: + "\n";
623: for (int position = 0; position < positioner.nColumns(); position++) {
624: if (positioner.areNullsOrdered(position)) {
625: output = output + position + " ";
626: }
627: }
628:
629: }
630:
631: return output + "\n";
632: }
633:
634: /**
635: * Return an array of Qualifiers as a String
636: */
637: public String printQualifiers() {
638: //There are no qualifiers in thie result set for index scans.
639: String idt = "";
640: return idt + MessageService.getTextMessage(SQLState.LANG_NONE);
641: }
642: }
|