KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > DeleteResultSet


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.DeleteResultSet
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.services.monitor.Monitor;
25 import org.apache.derby.iapi.services.sanity.SanityManager;
26 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
27 import org.apache.derby.iapi.services.stream.InfoStreams;
28 import org.apache.derby.iapi.error.StandardException;
29 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
30 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
31 import org.apache.derby.iapi.sql.execute.ConstantAction;
32 import org.apache.derby.iapi.sql.execute.CursorResultSet;
33 import org.apache.derby.iapi.sql.execute.ExecRow;
34 import org.apache.derby.iapi.sql.execute.ExecutionContext;
35 import org.apache.derby.iapi.sql.execute.RowChanger;
36 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
37 import org.apache.derby.iapi.sql.Activation;
38 import org.apache.derby.iapi.sql.ResultDescription;
39 import org.apache.derby.iapi.sql.ResultSet;
40 import org.apache.derby.iapi.types.DataValueDescriptor;
41
42 import org.apache.derby.iapi.store.access.ConglomerateController;
43 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
44 import org.apache.derby.iapi.store.access.ScanController;
45 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
46 import org.apache.derby.iapi.store.access.TransactionController;
47
48 import org.apache.derby.iapi.types.RowLocation;
49
50 import org.apache.derby.iapi.reference.SQLState;
51
52 import org.apache.derby.iapi.db.TriggerExecutionContext;
53 import org.apache.derby.iapi.services.io.FormatableBitSet;
54 import java.util.Properties JavaDoc;
55
56 import org.apache.derby.iapi.types.RowLocation;
57 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
58
59 /**
60  * Delete the rows from the specified
61  * base table. This will cause constraints to be checked
62  * and triggers to be executed based on the c's and t's
63  * compiled into the insert plan.
64  */

65 class DeleteResultSet extends DMLWriteResultSet
66 {
67     private TransactionController tc;
68     DeleteConstantAction constants;
69     protected ResultDescription resultDescription;
70     protected NoPutResultSet source;
71     NoPutResultSet savedSource;
72     int numIndexes;
73     protected RowChanger rc;
74     private ExecRow row;
75
76     protected ConglomerateController deferredBaseCC;
77
78     protected TemporaryRowHolderImpl rowHolder;
79
80     private int numOpens; // number of opens w/o a close
81
private boolean firstExecute;
82
83     // cached across opens()s
84
private FormatableBitSet baseRowReadList;
85     private int rlColumnNumber;
86     protected FKInfo[] fkInfoArray;
87     private TriggerInfo triggerInfo;
88     private RISetChecker fkChecker;
89     private TriggerEventActivator triggerActivator;
90     private boolean noTriggersOrFks;
91
92     ExecRow deferredSparseRow;
93     ExecRow deferredBaseRow;
94     int lockMode;
95     protected boolean cascadeDelete;
96     ExecRow deferredRLRow = null;
97     int numberOfBaseColumns = 0;
98
99     /**
100      * Returns the description of the deleted rows.
101      * REVISIT: Do we want this to return NULL instead?
102      */

103     public ResultDescription getResultDescription()
104     {
105         return resultDescription;
106     }
107
108     /*
109      * class interface
110      *
111      */

112     DeleteResultSet
113     (
114         NoPutResultSet source,
115         Activation activation
116     )
117         throws StandardException
118     {
119         this(source, activation.getConstantAction(), activation);
120     }
121     /**
122      * REMIND: At present this takes just the conglomerate id
123      * of the table. We can expect this to expand to include
124      * passing information about triggers, constraints, and
125      * any additional conglomerates on the underlying table
126      * for access methods.
127      *
128      * @exception StandardException Thrown on error
129      */

130     DeleteResultSet
131     (
132         NoPutResultSet source,
133         ConstantAction passedInConstantAction,
134         Activation activation
135     )
136         throws StandardException
137     {
138         super(activation, passedInConstantAction);
139         this.source = source;
140
141         tc = activation.getTransactionController();
142         constants = (DeleteConstantAction) constantAction;
143         fkInfoArray = constants.getFKInfo( lcc.getExecutionContext() );
144         triggerInfo = constants.getTriggerInfo(lcc.getExecutionContext());
145         noTriggersOrFks = ((fkInfoArray == null) && (triggerInfo == null));
146         baseRowReadList = constants.getBaseRowReadList();
147         if(source != null)
148             resultDescription = source.getResultDescription();
149         else
150             resultDescription = constants.resultDescription;
151
152     }
153
154     /**
155         @exception StandardException Standard Cloudscape error policy
156     */

157     public void open() throws StandardException
158     {
159
160         setup();
161         boolean rowsFound = collectAffectedRows(); //this call also deletes rows , if not deferred
162
if (! rowsFound)
163         {
164             activation.addWarning(
165                         StandardException.newWarning(
166                             SQLState.LANG_NO_ROW_FOUND));
167         }
168
169         /*
170         ** If the delete is deferred, scan the temporary conglomerate to
171         ** get the RowLocations of the rows to be deleted. Re-fetch the
172         ** rows and delete them using the RowChanger.
173         */

174         if (constants.deferred)
175         {
176             runFkChecker(true); //check for only RESTRICT referential action rule violations
177
fireBeforeTriggers();
178             deleteDeferredRows();
179             runFkChecker(false); //check for all constraint violations
180
// apply
181
rc.finish();
182             fireAfterTriggers();
183         }
184
185     
186         /* Cache query plan text for source, before it gets blown away */
187         if (lcc.getRunTimeStatisticsMode())
188         {
189             /* savedSource nulled after run time statistics generation */
190             savedSource = source;
191         }
192
193         cleanUp();
194         endTime = getCurrentTimeMillis();
195
196     }
197     
198
199     //this routine open the source and find the dependent rows
200
void setup() throws StandardException
201     {
202
203         // Remember if this is the 1st execution
204
firstExecute = (rc == null);
205
206         try {
207
208             //open the source for the parent tables
209
if (numOpens++ == 0)
210             {
211                 source.openCore();
212             }
213             else
214             {
215                 source.reopenCore();
216             }
217         } catch (StandardException se) {
218             activation.checkStatementValidity();
219             throw se;
220
221         }
222
223         activation.checkStatementValidity();
224
225         /* Get or re-use the row changer.
226          * NOTE: We need to set ourself as the top result set
227          * if this is not the 1st execution. (Done in constructor
228          * for 1st execution.)
229          */

230         if (firstExecute)
231         {
232             rc = lcc.getLanguageConnectionFactory().getExecutionFactory().
233                          getRowChanger(
234                                 constants.conglomId,
235                                 constants.heapSCOCI,
236                                 heapDCOCI,
237                                 constants.irgs,
238                                 constants.indexCIDS,
239                                 constants.indexSCOCIs,
240                                 indexDCOCIs,
241                                 constants.numColumns,
242                                 tc,
243                                 (int[])null,
244                                 baseRowReadList,
245                                 constants.getBaseRowReadMap(),
246                                 constants.getStreamStorableHeapColIds(),
247                                 activation);
248         }
249         else
250         {
251             lcc.getStatementContext().setTopResultSet(this, subqueryTrackingArray);
252         }
253         /* decode the lock mode for the execution isolation level */
254         lockMode = UpdateResultSet.decodeLockMode(lcc, constants.lockMode);
255
256         /* Open the RowChanger before the source ResultSet so that
257          * the store will see the RowChanger's lock as a covering lock
258          * if it is a table lock.
259          */

260         rc.open(lockMode);
261
262         /* The source does not know whether or not we are doing a
263          * deferred mode delete. If we are, then we must clear the
264          * index scan info from the activation so that the row changer
265          * does not re-use that information (which won't be valid for
266          * a deferred mode delete).
267          */

268         if (constants.deferred || cascadeDelete)
269         {
270             activation.clearIndexScanInfo();
271         }
272
273         rowCount = 0;
274         if(!cascadeDelete)
275             row = getNextRowCore(source);
276
277         /*
278         ** We need the number of columns even if there are
279         ** no rows. Note that source.ressultDescription() may
280         ** be null on a rep target doing a refresh.
281         */

282         if (resultDescription == null)
283         {
284             if (SanityManager.DEBUG)
285             {
286                 /*
287                 ** We NEED a result description when we are going to
288                 ** to have to kick off a trigger. In a replicated environment
289                 ** we don't get a result description when we are replaying
290                 ** source xacts on the target, but we shouldn't be firing
291                 ** a trigger in that case anyway.
292                 */

293                 SanityManager.ASSERT(triggerInfo == null, "result description is needed to supply to trigger result sets");
294             }
295             numberOfBaseColumns = (row == null) ? 0 : row.nColumns();
296         }
297         else
298         {
299             numberOfBaseColumns = resultDescription.getColumnCount();
300         }
301
302         numIndexes = constants.irgs.length;
303
304         if (constants.deferred || cascadeDelete)
305         {
306             Properties JavaDoc properties = new Properties JavaDoc();
307
308             // Get the properties on the old heap
309
rc.getHeapConglomerateController().getInternalTablePropertySet(properties);
310
311             /*
312             ** If deferred and fk or trigger, we are going to grab
313             ** the entire row.
314             **
315             ** If we are deferred w/o a fk, then we only
316             ** save the row location.
317             */

318             deferredRLRow = RowUtil.getEmptyValueRow(1, lcc);
319             rlColumnNumber = noTriggersOrFks ? 1: numberOfBaseColumns;
320             if(cascadeDelete)
321             {
322                 rowHolder = new TemporaryRowHolderImpl(activation, properties,
323                         (resultDescription != null) ?
324                             resultDescription.truncateColumns(rlColumnNumber) :
325                             null, false);
326
327
328             }else
329             {
330
331                 rowHolder = new TemporaryRowHolderImpl(activation, properties,
332                         (resultDescription != null) ?
333                             resultDescription.truncateColumns(rlColumnNumber) :
334                             null);
335
336             }
337
338             rc.setRowHolder(rowHolder);
339         }
340
341         if (fkInfoArray != null)
342         {
343             if (fkChecker == null)
344             {
345                 fkChecker = new RISetChecker(tc, fkInfoArray);
346             }
347             else
348             {
349                 fkChecker.reopen();
350             }
351         }
352     }
353
354
355     boolean collectAffectedRows() throws StandardException
356     {
357
358         DataValueDescriptor rlColumn;
359         RowLocation baseRowLocation;
360         boolean rowsFound = false;
361
362         if(cascadeDelete)
363             row = getNextRowCore(source);
364
365         while ( row != null )
366         {
367             /* By convention, the last column for a delete contains a SQLRef
368              * containing the RowLocation of the row to be deleted. If we're
369              * doing a deferred delete, store the RowLocations in the
370              * temporary conglomerate. If we're not doing a deferred delete,
371              * just delete the rows immediately.
372              */

373
374             rowsFound = true;
375
376             rlColumn = row.getColumn( row.nColumns() );
377         
378             if (constants.deferred || cascadeDelete)
379             {
380
381                 /*
382                 ** If we are deferred because of a trigger or foreign
383                 ** key, we need to save off the entire row. Otherwise,
384                 ** we just save the RID.
385                 */

386                 if (noTriggersOrFks)
387                 {
388                     deferredRLRow.setColumn(1, rlColumn);
389                     rowHolder.insert(deferredRLRow);
390                 }
391                 else
392                 {
393                     rowHolder.insert(row);
394                 }
395                 
396                 /*
397                 ** If we haven't already, lets get a template to
398                 ** use as a template for our rescan of the base table.
399                 ** Do this now while we have a real row to use
400                 ** as a copy.
401                 **
402                 ** There is one less column in the base row than
403                 ** there is in source row, because the base row
404                 ** doesn't contain the row location.
405                 */

406                 if (deferredBaseRow == null)
407                 {
408                     deferredBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns - 1, lcc);
409             
410                     RowUtil.copyCloneColumns(deferredBaseRow, row,
411                                             numberOfBaseColumns - 1);
412                     deferredSparseRow = makeDeferredSparseRow(deferredBaseRow,
413                                                                 baseRowReadList,
414                                                                 lcc);
415                 }
416             }
417             else
418             {
419                 if (fkChecker != null)
420                 {
421                     fkChecker.doPKCheck(row, false);
422                 }
423
424                 baseRowLocation =
425                     (RowLocation) (rlColumn).getObject();
426
427                 if (SanityManager.DEBUG)
428                 {
429                     SanityManager.ASSERT(baseRowLocation != null,
430                             "baseRowLocation is null");
431                 }
432
433                 rc.deleteRow(row,baseRowLocation);
434                 source.markRowAsDeleted();
435             }
436
437             rowCount++;
438
439             // No need to do a next on a single row source
440
if (constants.singleRowSource)
441             {
442                 row = null;
443             }
444             else
445             {
446                 row = getNextRowCore(source);
447             }
448         }
449
450         return rowsFound;
451     }
452
453
454     // execute the before triggers set on the table
455
void fireBeforeTriggers() throws StandardException
456     {
457
458         if (triggerInfo != null)
459         {
460             if (triggerActivator == null)
461             {
462                 triggerActivator = new TriggerEventActivator(lcc,
463                                                              tc,
464                                                              constants.targetUUID,
465                                                              triggerInfo,
466                                                              TriggerExecutionContext.DELETE_EVENT,
467                                                              activation, null
468                                                              );
469             }
470             else
471             {
472                 triggerActivator.reopen();
473             }
474
475             // fire BEFORE trigger
476
triggerActivator.notifyEvent(TriggerEvents.BEFORE_DELETE,
477                                          rowHolder.getResultSet(),
478                                          (CursorResultSet)null);
479             triggerActivator.cleanup();
480
481         }
482
483     }
484
485     //execute the after triggers set on the table.
486
void fireAfterTriggers() throws StandardException
487     {
488
489         // fire AFTER trigger
490
if (triggerActivator != null)
491         {
492             triggerActivator.reopen();
493             triggerActivator.notifyEvent(TriggerEvents.AFTER_DELETE,
494                                          rowHolder.getResultSet(),
495                                          (CursorResultSet)null);
496             triggerActivator.cleanup();
497         }
498         
499     }
500
501
502     //delete the rows that in case deferred case and
503
//during cascade delete (All deletes are deferred during cascade action)
504
void deleteDeferredRows() throws StandardException
505     {
506         
507         DataValueDescriptor rlColumn;
508         RowLocation baseRowLocation;
509         ExecRow deferredRLRow = null;
510
511         deferredBaseCC = tc.openCompiledConglomerate(false,
512                                                      tc.OPENMODE_FORUPDATE|tc.OPENMODE_SECONDARY_LOCKED,
513                                                      lockMode,
514                                                      TransactionController.ISOLATION_SERIALIZABLE,
515                                                      constants.heapSCOCI,
516                                                      heapDCOCI);
517             
518         CursorResultSet rs = rowHolder.getResultSet();
519         try
520         {
521             /*
522             ** We need to do a fetch doing a partial row
523             ** read. We need to shift our 1-based bit
524             ** set to a zero based bit set like the store
525             ** expects.
526             */

527             FormatableBitSet readBitSet = RowUtil.shift(baseRowReadList, 1);
528
529             rs.open();
530             while ((deferredRLRow = rs.getNextRow()) != null)
531             {
532                 rlColumn = deferredRLRow.getColumn(rlColumnNumber);
533                 baseRowLocation =
534                     (RowLocation) (rlColumn).getObject();
535     
536                 /* Get the base row at the given RowLocation */
537                 boolean row_exists =
538                     deferredBaseCC.fetch(
539                                          baseRowLocation, deferredSparseRow.getRowArray(),
540                                          readBitSet);
541
542                 // In case of cascade delete , things like before triggers can delete
543
// the rows before the dependent result get a chance to delete
544
if(cascadeDelete && !row_exists)
545                     continue;
546
547                 if (SanityManager.DEBUG)
548                 {
549                     if (!row_exists)
550                     {
551                             SanityManager.THROWASSERT("could not find row "+baseRowLocation);
552                     }
553                 }
554     
555                 rc.deleteRow(deferredBaseRow, baseRowLocation);
556                 source.markRowAsDeleted();
557             }
558         } finally
559         {
560                 rs.close();
561         }
562     }
563
564
565     // make sure foreign key constraints are not violated
566
void runFkChecker(boolean restrictCheckOnly) throws StandardException
567     {
568
569         ExecRow deferredRLRow = null;
570         if (fkChecker != null)
571         {
572             /*
573             ** Second scan to make sure all the foreign key
574             ** constraints are ok. We have to do this after
575             ** we have completed the deletes in case of self referencing
576             ** constraints.
577             */

578             CursorResultSet rs = rowHolder.getResultSet();
579             try
580             {
581                 rs.open();
582                 while ((deferredRLRow = rs.getNextRow()) != null)
583                 {
584                     fkChecker.doPKCheck(deferredRLRow, restrictCheckOnly);
585                 }
586             } finally
587             {
588                 rs.close();
589             }
590         }
591     }
592
593     /**
594       * create a source for the dependent table
595       *
596       * <P>Delete Cascade ResultSet class will override this method.
597       *
598       * @exception StandardException Thrown on error
599       */

600     NoPutResultSet createDependentSource(RowChanger rc)
601         throws StandardException
602     {
603         return null;
604     }
605
606
607     /**
608      * @see ResultSet#cleanUp
609      *
610      * @exception StandardException Thrown on error
611      */

612     public void cleanUp() throws StandardException
613     {
614         numOpens = 0;
615
616         /* Close down the source ResultSet tree */
617         if (source != null)
618         {
619             source.close();
620             // source is reused across executions
621
}
622         if (rc != null)
623         {
624             rc.close();
625             // rc is reused across executions
626
}
627
628         if (rowHolder != null)
629         {
630             rowHolder.close();
631             // rowHolder is reused across executions
632
}
633
634         if (fkChecker != null)
635         {
636             fkChecker.close();
637             // fkcheckers is reused across executions
638
}
639
640         if (deferredBaseCC != null)
641             deferredBaseCC.close();
642         deferredBaseCC = null;
643
644         super.close();
645     }
646
647     public void finish() throws StandardException {
648         if (source != null)
649             source.finish();
650         super.finish();
651     }
652
653 }
654
655
656
657
658
659
660
661
662
663
664
665
666
Popular Tags