KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > DeleteNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.DeleteNode
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.compile;
23
24 import org.apache.derby.iapi.services.context.ContextManager;
25
26 import org.apache.derby.iapi.reference.SQLState;
27 import org.apache.derby.iapi.error.StandardException;
28
29 import org.apache.derby.iapi.sql.conn.Authorizer;
30 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
31 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
32 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
33 import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
34 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
35 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
36
37
38 import org.apache.derby.iapi.sql.ResultSet;
39 import org.apache.derby.iapi.sql.StatementType;
40
41 import org.apache.derby.iapi.sql.compile.CompilerContext;
42 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
43 import org.apache.derby.iapi.reference.ClassName;
44
45 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
46
47 import org.apache.derby.iapi.sql.execute.CursorResultSet;
48 import org.apache.derby.iapi.sql.execute.ConstantAction;
49 import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
50 import org.apache.derby.iapi.sql.execute.ExecRow;
51
52 import org.apache.derby.iapi.sql.Activation;
53
54 import org.apache.derby.iapi.services.sanity.SanityManager;
55
56 import org.apache.derby.iapi.services.compiler.MethodBuilder;
57
58 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
59 import org.apache.derby.iapi.store.access.TransactionController;
60
61 import org.apache.derby.vti.DeferModification;
62
63 import org.apache.derby.catalog.UUID;
64 import org.apache.derby.iapi.services.io.FormatableBitSet;
65
66 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
67
68 import org.apache.derby.impl.sql.execute.DeleteConstantAction;
69 import org.apache.derby.impl.sql.execute.FKInfo;
70
71 import java.lang.reflect.Modifier JavaDoc;
72 import org.apache.derby.iapi.services.classfile.VMOpcode;
73 import org.apache.derby.iapi.services.io.FormatableProperties;
74 import java.util.Vector JavaDoc;
75 import java.util.Hashtable JavaDoc;
76 import java.util.Properties JavaDoc;
77 import org.apache.derby.iapi.sql.compile.NodeFactory;
78 import org.apache.derby.iapi.util.ReuseFactory;
79 import org.apache.derby.iapi.sql.depend.Dependent;
80 import org.apache.derby.iapi.sql.ResultDescription;
81 import org.apache.derby.iapi.services.compiler.LocalField;
82
83
84 /**
85  * A DeleteNode represents a DELETE statement. It is the top-level node
86  * for the statement.
87  *
88  * For positioned delete, there may be no from table specified.
89  * The from table will be derived from the cursor specification of
90  * the named cursor.
91  *
92  * @author Jeff Lichtman
93  */

94
95 public class DeleteNode extends DMLModStatementNode
96 {
97     /* Column name for the RowLocation column in the ResultSet */
98     private static final String JavaDoc COLUMNNAME = "###RowLocationToDelete";
99
100     /* Filled in by bind. */
101     protected boolean deferred;
102     protected ExecRow emptyHeapRow;
103     protected FromTable targetTable;
104     protected FKInfo fkInfo;
105     protected FormatableBitSet readColsBitSet;
106
107     private ConstantAction[] dependentConstantActions;
108     private boolean cascadeDelete;
109     private QueryTreeNode[] dependentNodes;
110
111     /**
112      * Initializer for a DeleteNode.
113      *
114      * @param targetTableName The name of the table to delete from
115      * @param queryExpression The query expression that will generate
116      * the rows to delete from the given table
117      */

118
119     public void init(Object JavaDoc targetTableName,
120                       Object JavaDoc queryExpression)
121     {
122         super.init(queryExpression);
123         this.targetTableName = (TableName) targetTableName;
124     }
125
126     public String JavaDoc statementToString()
127     {
128         return "DELETE";
129     }
130
131     /**
132      * Bind this DeleteNode. This means looking up tables and columns and
133      * getting their types, and figuring out the result types of all
134      * expressions, as well as doing view resolution, permissions checking,
135      * etc.
136      * <p>
137      * If any indexes need to be updated, we add all the columns in the
138      * base table to the result column list, so that we can use the column
139      * values as look-up keys for the index rows to be deleted. Binding a
140      * delete will also massage the tree so that the ResultSetNode has
141      * column containing the RowLocation of the base row.
142      *
143      * @return The bound query tree
144      *
145      * @exception StandardException Thrown on error
146      */

147
148     public QueryTreeNode bind() throws StandardException
149     {
150         // We just need select privilege on the where clause tables
151
getCompilerContext().pushCurrentPrivType( Authorizer.SELECT_PRIV);
152         try
153         {
154             FromList fromList = (FromList) getNodeFactory().getNode(
155                                     C_NodeTypes.FROM_LIST,
156                                     getNodeFactory().doJoinOrderOptimization(),
157                                     getContextManager());
158             ResultColumn rowLocationColumn = null;
159             CurrentRowLocationNode rowLocationNode;
160             TableName cursorTargetTableName = null;
161             CurrentOfNode currentOfNode = null;
162         
163             DataDictionary dataDictionary = getDataDictionary();
164             super.bindTables(dataDictionary);
165
166             // wait to bind named target table until the underlying
167
// cursor is bound, so that we can get it from the
168
// cursor if this is a positioned delete.
169

170             // for positioned delete, get the cursor's target table.
171
if (SanityManager.DEBUG)
172                 SanityManager.ASSERT(resultSet != null && resultSet instanceof SelectNode,
173                 "Delete must have a select result set");
174
175             SelectNode sel;
176             sel = (SelectNode)resultSet;
177             targetTable = (FromTable) sel.fromList.elementAt(0);
178             if (targetTable instanceof CurrentOfNode)
179             {
180                 currentOfNode = (CurrentOfNode) targetTable;
181
182                 cursorTargetTableName = currentOfNode.getBaseCursorTargetTableName();
183                 // instead of an assert, we might say the cursor is not updatable.
184
if (SanityManager.DEBUG)
185                     SanityManager.ASSERT(cursorTargetTableName != null);
186             }
187
188             if (targetTable instanceof FromVTI)
189             {
190                 targetVTI = (FromVTI) targetTable;
191                 targetVTI.setTarget();
192             }
193             else
194             {
195                 // positioned delete can leave off the target table.
196
// we get it from the cursor supplying the position.
197
if (targetTableName == null)
198                 {
199                     // verify we have current of
200
if (SanityManager.DEBUG)
201                         SanityManager.ASSERT(cursorTargetTableName!=null);
202
203                 targetTableName = cursorTargetTableName;
204                 }
205                 // for positioned delete, we need to verify that
206
// the named table is the same as the cursor's target (base table name).
207
else if (cursorTargetTableName != null)
208                 {
209                     // this match requires that the named table in the delete
210
// be the same as a base name in the cursor.
211
if ( !targetTableName.equals(cursorTargetTableName))
212                     {
213                         throw StandardException.newException(SQLState.LANG_CURSOR_DELETE_MISMATCH,
214                             targetTableName,
215                             currentOfNode.getCursorName());
216                     }
217                 }
218             }
219         
220             // descriptor must exist, tables already bound.
221
verifyTargetTable();
222
223             /* Generate a select list for the ResultSetNode - CurrentRowLocation(). */
224             if (SanityManager.DEBUG)
225                 SanityManager.ASSERT((resultSet.resultColumns == null),
226                               "resultColumns is expected to be null until bind time");
227
228
229             if (targetTable instanceof FromVTI)
230             {
231                 getResultColumnList();
232                 resultColumnList = targetTable.getResultColumnsForList(null,
233                                 resultColumnList, null);
234
235                 /* Set the new result column list in the result set */
236                 resultSet.setResultColumns(resultColumnList);
237             }
238             else
239             {
240                 /*
241                 ** Start off assuming no columns from the base table
242                 ** are needed in the rcl.
243                 */

244
245                 resultColumnList = new ResultColumnList();
246
247                 FromBaseTable fbt = getResultColumnList(resultColumnList);
248
249                 readColsBitSet = getReadMap(dataDictionary,
250                                         targetTableDescriptor);
251
252                 resultColumnList = fbt.addColsToList(resultColumnList, readColsBitSet);
253
254                 /*
255                 ** If all bits are set, then behave as if we chose all
256                 ** in the first place
257                 */

258                 int i = 1;
259                 int size = targetTableDescriptor.getMaxColumnID();
260                 for (; i <= size; i++)
261                 {
262                     if (!readColsBitSet.get(i))
263                     {
264                         break;
265                     }
266                 }
267
268                 if (i > size)
269                 {
270                     readColsBitSet = null;
271                 }
272
273                 /*
274                 ** Construct an empty heap row for use in our constant action.
275                 */

276                 emptyHeapRow = targetTableDescriptor.getEmptyExecRow(getContextManager());
277
278                 /* Generate the RowLocation column */
279                 rowLocationNode = (CurrentRowLocationNode) getNodeFactory().getNode(
280                                         C_NodeTypes.CURRENT_ROW_LOCATION_NODE,
281                                         getContextManager());
282                 rowLocationColumn =
283                     (ResultColumn) getNodeFactory().getNode(
284                                     C_NodeTypes.RESULT_COLUMN,
285                                     COLUMNNAME,
286                                     rowLocationNode,
287                                     getContextManager());
288                 rowLocationColumn.markGenerated();
289
290                 /* Append to the ResultColumnList */
291                 resultColumnList.addResultColumn(rowLocationColumn);
292
293                 /* Force the added columns to take on the table's correlation name, if any */
294                 correlateAddedColumns( resultColumnList, targetTable );
295             
296                 /* Set the new result column list in the result set */
297                 resultSet.setResultColumns(resultColumnList);
298             }
299
300             /* Bind the expressions before the ResultColumns are bound */
301             super.bindExpressions();
302
303             /* Bind untyped nulls directly under the result columns */
304             resultSet.getResultColumns().
305                 bindUntypedNullsToResultColumns(resultColumnList);
306
307             if (! (targetTable instanceof FromVTI))
308             {
309                 /* Bind the new ResultColumn */
310                 rowLocationColumn.bindResultColumnToExpression();
311
312                 bindConstraints(dataDictionary,
313                             getNodeFactory(),
314                             targetTableDescriptor,
315                             null,
316                             resultColumnList,
317                             (int[]) null,
318                             readColsBitSet,
319                             false,
320                             true); /* we alway include triggers in core language */
321
322                 /* If the target table is also a source table, then
323                 * the delete will have to be in deferred mode
324                 * For deletes, this means that the target table appears in a
325                 * subquery. Also, self-referencing foreign key deletes
326                 * are deferred. And triggers cause the delete to be deferred.
327                 */

328                 if (resultSet.subqueryReferencesTarget(
329                                     targetTableDescriptor.getName(), true) ||
330                     requiresDeferredProcessing())
331                 {
332                     deferred = true;
333                 }
334             }
335             else
336             {
337                 deferred = VTIDeferModPolicy.deferIt( DeferModification.DELETE_STATEMENT,
338                                                   targetVTI,
339                                                   null,
340                                                   sel.getWhereClause());
341             }
342             sel = null; // done with sel
343

344             /* Verify that all underlying ResultSets reclaimed their FromList */
345             if (SanityManager.DEBUG)
346             {
347                 SanityManager.ASSERT(fromList.size() == 0,
348                     "fromList.size() is expected to be 0, not " +
349                     fromList.size() +
350                     " on return from RS.bindExpressions()");
351             }
352
353             //In case of cascade delete , create nodes for
354
//the ref action dependent tables and bind them.
355
if(fkTableNames != null)
356             {
357                 String JavaDoc currentTargetTableName = targetTableDescriptor.getSchemaName() +
358                          "." + targetTableDescriptor.getName();
359
360                 if(!isDependentTable){
361                     //graph node
362
graphHashTable = new Hashtable JavaDoc();
363                 }
364
365                 /*Check whether the current tatget is already been explored.
366                 *If we are seeing the same table name which we binded earlier
367                 *means we have cyclic references.
368                 */

369                 if(!graphHashTable.containsKey(currentTargetTableName))
370                 {
371                     cascadeDelete = true;
372                     int noDependents = fkTableNames.length;
373                     dependentNodes = new QueryTreeNode[noDependents];
374                     graphHashTable.put(currentTargetTableName, new Integer JavaDoc(noDependents));
375                     for(int i =0 ; i < noDependents ; i ++)
376                     {
377                         dependentNodes[i] = getDependentTableNode(fkTableNames[i],
378                                                               fkRefActions[i],
379                                                               fkColDescriptors[i]);
380                         dependentNodes[i].bind();
381                     }
382                 }
383             }
384             else
385             {
386                 //case where current dependent table does not have dependent tables
387
if(isDependentTable)
388                 {
389                     String JavaDoc currentTargetTableName = targetTableDescriptor.getSchemaName()
390                              + "." + targetTableDescriptor.getName();
391                     graphHashTable.put(currentTargetTableName, new Integer JavaDoc(0));
392
393                 }
394             }
395             if (isPrivilegeCollectionRequired())
396             {
397                 getCompilerContext().pushCurrentPrivType( getPrivType());
398                 getCompilerContext().addRequiredTablePriv( targetTableDescriptor);
399                 getCompilerContext().popCurrentPrivType();
400             }
401         }
402         finally
403         {
404             getCompilerContext().popCurrentPrivType();
405         }
406         return this;
407     } // end of bind
408

409     int getPrivType()
410     {
411         return Authorizer.DELETE_PRIV;
412     }
413
414     /**
415      * Return true if the node references SESSION schema tables (temporary or permanent)
416      *
417      * @return true if references SESSION schema tables, else false
418      *
419      * @exception StandardException Thrown on error
420      */

421     public boolean referencesSessionSchema()
422         throws StandardException
423     {
424         //If delete table is on a SESSION schema table, then return true.
425
return resultSet.referencesSessionSchema();
426     }
427
428     /**
429      * Compile constants that Execution will use
430      *
431      * @exception StandardException Thrown on failure
432      */

433     public ConstantAction makeConstantAction() throws StandardException
434     {
435
436         /* Different constant actions for base tables and updatable VTIs */
437         if (targetTableDescriptor != null)
438         {
439             // Base table
440
int lockMode = resultSet.updateTargetLockMode();
441             long heapConglomId = targetTableDescriptor.getHeapConglomerateId();
442             TransactionController tc = getLanguageConnectionContext().getTransactionCompile();
443             StaticCompiledOpenConglomInfo[] indexSCOCIs =
444                 new StaticCompiledOpenConglomInfo[indexConglomerateNumbers.length];
445
446             for (int index = 0; index < indexSCOCIs.length; index++)
447             {
448                 indexSCOCIs[index] = tc.getStaticCompiledConglomInfo(indexConglomerateNumbers[index]);
449             }
450
451             /*
452             ** Do table locking if the table's lock granularity is
453             ** set to table.
454             */

455             if (targetTableDescriptor.getLockGranularity() == TableDescriptor.TABLE_LOCK_GRANULARITY)
456             {
457                 lockMode = TransactionController.MODE_TABLE;
458             }
459
460             ResultDescription resultDescription = null;
461             if(isDependentTable)
462             {
463                 //triggers need the result description ,
464
//dependent tables don't have a source from generation time
465
//to get the result description
466
resultDescription = makeResultDescription();
467             }
468
469
470             return getGenericConstantActionFactory().getDeleteConstantAction
471                 ( heapConglomId,
472                   targetTableDescriptor.getTableType(),
473                   tc.getStaticCompiledConglomInfo(heapConglomId),
474                   indicesToMaintain,
475                   indexConglomerateNumbers,
476                   indexSCOCIs,
477                   emptyHeapRow,
478                   deferred,
479                   false,
480                   targetTableDescriptor.getUUID(),
481                   lockMode,
482                   null, null, null, 0, null, null,
483                   resultDescription,
484                   getFKInfo(),
485                   getTriggerInfo(),
486                   (readColsBitSet == null) ? (FormatableBitSet)null : new FormatableBitSet(readColsBitSet),
487                   getReadColMap(targetTableDescriptor.getNumberOfColumns(),readColsBitSet),
488                   resultColumnList.getStreamStorableColIds(targetTableDescriptor.getNumberOfColumns()),
489                   (readColsBitSet == null) ?
490                       targetTableDescriptor.getNumberOfColumns() :
491                       readColsBitSet.getNumBitsSet(),
492                   (UUID) null,
493                   resultSet.isOneRowResultSet(),
494                   dependentConstantActions);
495         }
496         else
497         {
498             /* Return constant action for VTI
499              * NOTE: ConstantAction responsible for preserving instantiated
500              * VTIs for in-memory queries and for only preserving VTIs
501              * that implement Serializable for SPSs.
502              */

503             return getGenericConstantActionFactory().getUpdatableVTIConstantAction( DeferModification.DELETE_STATEMENT,
504                         deferred);
505         }
506     }
507
508     /**
509      * Code generation for delete.
510      * The generated code will contain:
511      * o A static member for the (xxx)ResultSet with the RowLocations
512      * o The static member will be assigned the appropriate ResultSet within
513      * the nested calls to get the ResultSets. (The appropriate cast to the
514      * (xxx)ResultSet will be generated.)
515      * o The CurrentRowLocation() in SelectNode's select list will generate
516      * a new method for returning the RowLocation as well as a call to
517      * that method which will be stuffed in the call to the
518      * ProjectRestrictResultSet.
519      * o In case of referential actions, this function generate an
520      * array of resultsets on its dependent tables.
521      *
522      * @param acb The ActivationClassBuilder for the class being built
523      * @param mb The execute() method to be built
524      *
525      * @exception StandardException Thrown on error
526      */

527     public void generate(ActivationClassBuilder acb,
528                                 MethodBuilder mb)
529                             throws StandardException
530     {
531
532         //If the DML is on the temporary table, generate the code to mark temporary table as modified in the current UOW
533
generateCodeForTemporaryTable(acb, mb);
534
535         /* generate the parameters */
536         if(!isDependentTable)
537             generateParameterValueSet(acb);
538
539         acb.pushGetResultSetFactoryExpression(mb);
540         acb.newRowLocationScanResultSetName();
541         resultSet.generate(acb, mb); // arg 1
542

543         String JavaDoc resultSetGetter;
544         int argCount;
545         String JavaDoc parentResultSetId;
546
547         // Base table
548
if (targetTableDescriptor != null)
549         {
550             /* Create the declaration for the scan ResultSet which generates the
551              * RowLocations to be deleted.
552              * Note that the field cannot be static because there
553              * can be multiple activations of the same activation class,
554              * and they can't share this field. Only exprN fields can
555              * be shared (or, more generally, read-only fields).
556              * RESOLVE - Need to deal with the type of the field.
557              */

558
559             acb.newFieldDeclaration(Modifier.PRIVATE,
560                                     ClassName.CursorResultSet,
561                                     acb.getRowLocationScanResultSetName());
562
563             if(cascadeDelete || isDependentTable)
564             {
565                 resultSetGetter = "getDeleteCascadeResultSet";
566                 argCount = 4;
567             }
568             else
569             {
570                 resultSetGetter = "getDeleteResultSet";
571                 argCount = 1;
572             }
573             
574         } else {
575             argCount = 1;
576             resultSetGetter = "getDeleteVTIResultSet";
577         }
578
579         if(isDependentTable)
580         {
581             mb.push(acb.addItem(makeConstantAction()));
582         
583         }else
584         {
585             if(cascadeDelete)
586             {
587                 mb.push(-1); //root table.
588
}
589         }
590
591         String JavaDoc resultSetArrayType = ClassName.ResultSet + "[]";
592         if(cascadeDelete)
593         {
594             parentResultSetId = targetTableDescriptor.getSchemaName() +
595                                    "." + targetTableDescriptor.getName();
596             // Generate the code to build the array
597
LocalField arrayField =
598                 acb.newFieldDeclaration(Modifier.PRIVATE, resultSetArrayType);
599             mb.pushNewArray(ClassName.ResultSet, dependentNodes.length); // new ResultSet[size]
600
mb.setField(arrayField);
601             for(int index=0 ; index < dependentNodes.length ; index++)
602             {
603
604                 dependentNodes[index].setRefActionInfo(fkIndexConglomNumbers[index],
605                                                        fkColArrays[index],
606                                                        parentResultSetId,
607                                                        true);
608                 mb.getField(arrayField); // first arg (resultset array reference)
609
/*beetle:5360 : if too many statements are added to a method,
610                  *size of method can hit 65k limit, which will
611                  *lead to the class format errors at load time.
612                  *To avoid this problem, when number of statements added
613                  *to a method is > 2048, remaing statements are added to a new function
614                  *and called from the function which created the function.
615                  *See Beetle 5135 or 4293 for further details on this type of problem.
616                 */

617                 if(mb.statementNumHitLimit(10))
618                 {
619                     MethodBuilder dmb = acb.newGeneratedFun(ClassName.ResultSet, Modifier.PRIVATE);
620                     dependentNodes[index].generate(acb,dmb); //generates the resultset expression
621
dmb.methodReturn();
622                     dmb.complete();
623                     /* Generate the call to the new method */
624                     mb.pushThis();
625                     //second arg will be generated by this call
626
mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String JavaDoc) null, dmb.getName(), ClassName.ResultSet, 0);
627                 }else
628                 {
629                     dependentNodes[index].generate(acb,mb); //generates the resultset expression
630
}
631
632                 mb.setArrayElement(index);
633             }
634             mb.getField(arrayField); // fourth argument - array reference
635
}
636         else
637         {
638             if(isDependentTable)
639             {
640                 mb.pushNull(resultSetArrayType); //No dependent tables for this table
641
}
642         }
643
644
645         if(cascadeDelete || isDependentTable)
646         {
647             parentResultSetId = targetTableDescriptor.getSchemaName() +
648                                    "." + targetTableDescriptor.getName();
649             mb.push(parentResultSetId);
650
651         }
652         mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, resultSetGetter, ClassName.ResultSet, argCount);
653
654
655         if(!isDependentTable && cascadeDelete)
656         {
657             int numResultSets = acb.getRowCount();
658             if(numResultSets > 0)
659             {
660                 //generate activation.raParentResultSets = new NoPutResultSet[size]
661
MethodBuilder constructor = acb.getConstructor();
662                 constructor.pushThis();
663                 constructor.pushNewArray(ClassName.CursorResultSet, numResultSets);
664                 constructor.putField(ClassName.BaseActivation,
665                                      "raParentResultSets",
666                                      ClassName.CursorResultSet + "[]");
667                 constructor.endStatement();
668             }
669         }
670     }
671
672
673     /**
674      * Return the type of statement, something from
675      * StatementType.
676      *
677      * @return the type of statement
678      */

679     protected final int getStatementType()
680     {
681         return StatementType.DELETE;
682     }
683
684
685     /**
686       * Gets the map of all columns which must be read out of the base table.
687       * These are the columns needed to:
688       *
689       * o maintain indices
690       * o maintain foreign keys
691       *
692       * The returned map is a FormatableBitSet with 1 bit for each column in the
693       * table plus an extra, unsued 0-bit. If a 1-based column id must
694       * be read from the base table, then the corresponding 1-based bit
695       * is turned ON in the returned FormatableBitSet.
696       *
697       * @param dd the data dictionary to look in
698       * @param baseTable the base table descriptor
699       *
700       * @return a FormatableBitSet of columns to be read out of the base table
701       *
702       * @exception StandardException Thrown on error
703       */

704     public FormatableBitSet getReadMap
705     (
706         DataDictionary dd,
707         TableDescriptor baseTable
708     )
709         throws StandardException
710     {
711         boolean[] needsDeferredProcessing = new boolean[1];
712         needsDeferredProcessing[0] = requiresDeferredProcessing();
713
714         Vector JavaDoc conglomVector = new Vector JavaDoc();
715         relevantTriggers = new GenericDescriptorList();
716
717         FormatableBitSet columnMap = DeleteNode.getDeleteReadMap(baseTable, conglomVector, relevantTriggers, needsDeferredProcessing );
718
719         markAffectedIndexes( conglomVector );
720
721         adjustDeferredFlag( needsDeferredProcessing[0] );
722
723         return columnMap;
724     }
725
726     /**
727      * In case of referential actions, we require to perform
728      * DML (UPDATE or DELETE) on the dependent tables.
729      * Following function returns the DML Node for the dependent table.
730      */

731     private QueryTreeNode getDependentTableNode(String JavaDoc tableName, int refAction,
732                                                 ColumnDescriptorList cdl) throws StandardException
733     {
734         QueryTreeNode node=null;
735
736         int index = tableName.indexOf('.');
737         String JavaDoc schemaName = tableName.substring(0 , index);
738         String JavaDoc tName = tableName.substring(index+1);
739         if(refAction == StatementType.RA_CASCADE)
740         {
741             node = getEmptyDeleteNode(schemaName , tName);
742             ((DeleteNode)node).isDependentTable = true;
743             ((DeleteNode)node).graphHashTable = graphHashTable;
744         }
745
746         if(refAction == StatementType.RA_SETNULL)
747         {
748             node = getEmptyUpdateNode(schemaName , tName, cdl);
749             ((UpdateNode)node).isDependentTable = true;
750             ((UpdateNode)node).graphHashTable = graphHashTable;
751         }
752
753         return node;
754     }
755
756
757     private QueryTreeNode getEmptyDeleteNode(String JavaDoc schemaName, String JavaDoc targetTableName)
758         throws StandardException
759     {
760
761         ValueNode whereClause = null;
762         TableName tableName = null;
763         FromTable fromTable = null;
764         QueryTreeNode retval;
765         SelectNode resultSet;
766
767         tableName = new TableName();
768         tableName.init(schemaName , targetTableName);
769
770         NodeFactory nodeFactory = getNodeFactory();
771         FromList fromList = (FromList) nodeFactory.getNode(C_NodeTypes.FROM_LIST, getContextManager());
772         fromTable = (FromTable) nodeFactory.getNode(
773                                                     C_NodeTypes.FROM_BASE_TABLE,
774                                                     tableName,
775                                                     null,
776                                                     ReuseFactory.getInteger(FromBaseTable.DELETE),
777                                                     null,
778                                                     getContextManager());
779
780         //we would like to use references index & table scan instead of
781
//what optimizer says for the dependent table scan.
782
Properties targetProperties = new FormatableProperties();
783         targetProperties.put("index", "null");
784         ((FromBaseTable) fromTable).setTableProperties(targetProperties);
785
786         fromList.addFromTable(fromTable);
787         resultSet = (SelectNode) nodeFactory.getNode(
788                                                      C_NodeTypes.SELECT_NODE,
789                                                      null,
790                                                      null, /* AGGREGATE list */
791                                                      fromList, /* FROM list */
792                                                      whereClause, /* WHERE clause */
793                                                      null, /* GROUP BY list */
794                                                      getContextManager());
795
796         retval =(QueryTreeNode) nodeFactory.getNode(
797                                                     C_NodeTypes.DELETE_NODE,
798                                                     tableName,
799                                                     resultSet,
800                                                     getContextManager());
801
802         return retval;
803     }
804
805
806     
807     private QueryTreeNode getEmptyUpdateNode(String JavaDoc schemaName,
808                                              String JavaDoc targetTableName,
809                                              ColumnDescriptorList cdl)
810         throws StandardException
811     {
812
813         ValueNode whereClause = null;
814         TableName tableName = null;
815         FromTable fromTable = null;
816         QueryTreeNode retval;
817         SelectNode resultSet;
818
819         tableName = new TableName();
820         tableName.init(schemaName , targetTableName);
821
822         NodeFactory nodeFactory = getNodeFactory();
823         FromList fromList = (FromList) nodeFactory.getNode(C_NodeTypes.FROM_LIST, getContextManager());
824         fromTable = (FromTable) nodeFactory.getNode(
825                                                     C_NodeTypes.FROM_BASE_TABLE,
826                                                     tableName,
827                                                     null,
828                                                     ReuseFactory.getInteger(FromBaseTable.DELETE),
829                                                     null,
830                                                     getContextManager());
831
832
833         //we would like to use references index & table scan instead of
834
//what optimizer says for the dependent table scan.
835
Properties targetProperties = new FormatableProperties();
836         targetProperties.put("index", "null");
837         ((FromBaseTable) fromTable).setTableProperties(targetProperties);
838
839         fromList.addFromTable(fromTable);
840
841         resultSet = (SelectNode) nodeFactory.getNode(
842                                                      C_NodeTypes.SELECT_NODE,
843                                                      getSetClause(tableName, cdl),
844                                                      null, /* AGGREGATE list */
845                                                      fromList, /* FROM list */
846                                                      whereClause, /* WHERE clause */
847                                                      null, /* GROUP BY list */
848                                                      getContextManager());
849
850         retval =(QueryTreeNode) nodeFactory.getNode(
851                                                     C_NodeTypes.UPDATE_NODE,
852                                                     tableName,
853                                                     resultSet,
854                                                     getContextManager());
855
856         return retval;
857     }
858
859
860  
861     private ResultColumnList getSetClause(TableName tabName,
862                                           ColumnDescriptorList cdl)
863         throws StandardException
864     {
865         ResultColumn resultColumn;
866         ValueNode valueNode;
867
868         NodeFactory nodeFactory = getNodeFactory();
869         ResultColumnList columnList = (ResultColumnList) nodeFactory.getNode(
870                                                 C_NodeTypes.RESULT_COLUMN_LIST,
871                                                 getContextManager());
872
873         valueNode = (ValueNode) nodeFactory.getNode(C_NodeTypes.UNTYPED_NULL_CONSTANT_NODE,
874                                                              getContextManager());
875         for(int index =0 ; index < cdl.size() ; index++)
876         {
877             ColumnDescriptor cd = (ColumnDescriptor) cdl.elementAt(index);
878             //only columns that are nullable need to be set to 'null' for ON
879
//DELETE SET NULL
880
if((cd.getType()).isNullable())
881             {
882                 resultColumn = (ResultColumn) nodeFactory.getNode(
883                                         C_NodeTypes.RESULT_COLUMN,
884                                         cd,
885                                         valueNode,
886                                         getContextManager());
887
888                 columnList.addResultColumn(resultColumn);
889             }
890         }
891         return columnList;
892     }
893
894
895     public QueryTreeNode optimize() throws StandardException
896     {
897         if(cascadeDelete)
898         {
899             for(int index=0 ; index < dependentNodes.length ; index++)
900             {
901                 dependentNodes[index] = dependentNodes[index].optimize();
902             }
903         }
904
905         return super.optimize();
906     }
907
908     /**
909       * Builds a bitmap of all columns which should be read from the
910       * Store in order to satisfy an DELETE statement.
911       *
912       *
913       * 1) finds all indices on this table
914       * 2) adds the index columns to a bitmap of affected columns
915       * 3) adds the index descriptors to a list of conglomerate
916       * descriptors.
917       * 4) finds all DELETE triggers on the table
918       * 5) if there are any DELETE triggers, marks all columns in the bitmap
919       * 6) adds the triggers to an evolving list of triggers
920       *
921       * @param conglomVector OUT: vector of affected indices
922       * @param relevantTriggers IN/OUT. Passed in as an empty list. Filled in as we go.
923       * @param needsDeferredProcessing IN/OUT. true if the statement already needs
924       * deferred processing. set while evaluating this
925       * routine if a trigger requires
926       * deferred processing
927       *
928       * @return a FormatableBitSet of columns to be read out of the base table
929       *
930       * @exception StandardException Thrown on error
931       */

932     private static FormatableBitSet getDeleteReadMap
933     (
934         TableDescriptor baseTable,
935         Vector JavaDoc conglomVector,
936         GenericDescriptorList relevantTriggers,
937         boolean[] needsDeferredProcessing
938     )
939         throws StandardException
940     {
941         int columnCount = baseTable.getMaxColumnID();
942         FormatableBitSet columnMap = new FormatableBitSet(columnCount + 1);
943
944         /*
945         ** Get a list of the indexes that need to be
946         ** updated. ColumnMap contains all indexed
947         ** columns where 1 or more columns in the index
948         ** are going to be modified.
949         **
950         ** Notice that we don't need to add constraint
951         ** columns. This is because we add all key constraints
952         ** (e.g. foreign keys) as a side effect of adding their
953         ** indexes above. And we don't need to deal with
954         ** check constraints on a delete.
955         **
956         ** Adding indexes also takes care of the replication
957         ** requirement of having the primary key.
958         */

959         DMLModStatementNode.getXAffectedIndexes(baseTable, null, columnMap, conglomVector );
960
961         /*
962         ** If we have any triggers, then get all the columns
963         ** because we don't know what the user will ultimately
964         ** reference.
965         */

966         baseTable.getAllRelevantTriggers( StatementType.DELETE, (int[])null, relevantTriggers );
967         if ( relevantTriggers.size() > 0 ) { needsDeferredProcessing[0] = true; }
968
969         if (relevantTriggers.size() > 0)
970         {
971             for (int i = 1; i <= columnCount; i++)
972             {
973                 columnMap.set(i);
974             }
975         }
976
977         return columnMap;
978     }
979     
980     /*
981      * Force column references (particularly those added by the compiler)
982      * to use the correlation name on the base table, if any.
983      */

984     private void correlateAddedColumns( ResultColumnList rcl, FromTable fromTable )
985         throws StandardException
986     {
987         String JavaDoc correlationName = fromTable.getCorrelationName();
988
989         if ( correlationName == null ) { return; }
990
991         TableName correlationNameNode = makeTableName( null, correlationName );
992         int count = rcl.size();
993
994         for ( int i = 0; i < count; i++ )
995         {
996             ResultColumn column = (ResultColumn) rcl.elementAt( i );
997
998             ValueNode expression = column.getExpression();
999
1000            if ( (expression != null) && (expression instanceof ColumnReference) )
1001            {
1002                ColumnReference reference = (ColumnReference) expression;
1003                
1004                reference.setTableNameNode( correlationNameNode );
1005            }
1006        }
1007        
1008    }
1009    
1010}
1011
Popular Tags