KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.UpdateNode
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.services.loader.GeneratedMethod;
27
28 import org.apache.derby.iapi.services.compiler.MethodBuilder;
29
30 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
31 import org.apache.derby.iapi.sql.conn.Authorizer;
32 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
33 import org.apache.derby.impl.sql.execute.FKInfo;
34 import org.apache.derby.iapi.services.compiler.MethodBuilder;
35
36 import org.apache.derby.iapi.services.sanity.SanityManager;
37 import org.apache.derby.iapi.error.StandardException;
38 import org.apache.derby.iapi.sql.compile.CompilerContext;
39 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
40 import org.apache.derby.iapi.sql.compile.Visitable;
41 import org.apache.derby.iapi.sql.compile.Visitor;
42
43 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
44
45 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
46 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
47 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
48 import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
49 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
50 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
51 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
52 import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
53
54 import org.apache.derby.iapi.reference.SQLState;
55 import org.apache.derby.iapi.sql.execute.ConstantAction;
56 import org.apache.derby.iapi.sql.execute.CursorResultSet;
57 import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
58 import org.apache.derby.iapi.sql.execute.ExecRow;
59
60 import org.apache.derby.iapi.sql.Activation;
61 import org.apache.derby.iapi.sql.ResultSet;
62 import org.apache.derby.iapi.sql.StatementType;
63
64 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
65 import org.apache.derby.iapi.store.access.TransactionController;
66
67 import org.apache.derby.vti.DeferModification;
68
69 import org.apache.derby.iapi.services.io.FormatableBitSet;
70 import org.apache.derby.iapi.reference.ClassName;
71
72 import org.apache.derby.iapi.util.ReuseFactory;
73 import org.apache.derby.iapi.services.classfile.VMOpcode;
74
75 import java.lang.reflect.Modifier JavaDoc;
76 import java.sql.SQLException JavaDoc;
77 import java.util.Properties JavaDoc;
78 import java.util.Vector JavaDoc;
79
80 /**
81  * An UpdateNode represents an UPDATE statement. It is the top node of the
82  * query tree for that statement.
83  * For positioned update, there may be no from table specified.
84  * The from table will be derived from the cursor specification of
85  * the named cursor.
86  *
87  * @author Jeff Lichtman
88  */

89
90 public final class UpdateNode extends DMLModStatementNode
91 {
92     //Note: These are public so they will be visible to
93
//the RepUpdateNode.
94
public int[] changedColumnIds;
95     public ExecRow emptyHeapRow;
96     public boolean deferred;
97     public ValueNode checkConstraints;
98     public FKInfo fkInfo;
99     
100     protected FromTable targetTable;
101     protected FormatableBitSet readColsBitSet;
102     protected boolean positionedUpdate;
103
104     /* Column name for the RowLocation in the ResultSet */
105     public static final String JavaDoc COLUMNNAME = "###RowLocationToUpdate";
106
107     /**
108      * Initializer for an UpdateNode.
109      *
110      * @param targetTableName The name of the table to update
111      * @param resultSet The ResultSet that will generate
112      * the rows to update from the given table
113      */

114
115     public void init(
116                Object JavaDoc targetTableName,
117                Object JavaDoc resultSet)
118     {
119         super.init(resultSet);
120         this.targetTableName = (TableName) targetTableName;
121     }
122
123     /**
124      * Convert this object to a String. See comments in QueryTreeNode.java
125      * for how this should be done for tree printing.
126      *
127      * @return This object as a String
128      */

129
130     public String JavaDoc toString()
131     {
132         if (SanityManager.DEBUG)
133         {
134             return targetTableName.toString() + "\n" +
135                 super.toString();
136         }
137         else
138         {
139             return "";
140         }
141     }
142
143     public String JavaDoc statementToString()
144     {
145         return "UPDATE";
146     }
147
148     /**
149      * Prints the sub-nodes of this object. See QueryTreeNode.java for
150      * how tree printing is supposed to work.
151      *
152      * @param depth The depth of this node in the tree
153      */

154
155     public void printSubNodes(int depth)
156     {
157         if (SanityManager.DEBUG)
158         {
159             super.printSubNodes(depth);
160
161             if (targetTableName != null)
162             {
163                 printLabel(depth, "targetTableName: ");
164                 targetTableName.treePrint(depth + 1);
165             }
166
167             /* RESOLVE - need to print out targetTableDescriptor */
168         }
169     }
170
171     /**
172      * Bind this UpdateNode. This means looking up tables and columns and
173      * getting their types, and figuring out the result types of all
174      * expressions, as well as doing view resolution, permissions checking,
175      * etc.
176      * <p>
177      * Binding an update will also massage the tree so that
178      * the ResultSetNode has a set of columns to contain the old row
179      * value, followed by a set of columns to contain the new row
180      * value, followed by a column to contain the RowLocation of the
181      * row to be updated.
182      *
183      * @return The bound query tree
184      *
185      * @exception StandardException Thrown on error
186      */

187
188     public QueryTreeNode bind() throws StandardException
189     {
190         // We just need select privilege on the expressions
191
getCompilerContext().pushCurrentPrivType( Authorizer.SELECT_PRIV);
192
193         FromList fromList = (FromList) getNodeFactory().getNode(
194                                     C_NodeTypes.FROM_LIST,
195                                     getNodeFactory().doJoinOrderOptimization(),
196                                     getContextManager());
197         ResultColumn rowLocationColumn = null;
198         ValueNode rowLocationNode = null;
199         TableName cursorTargetTableName = null;
200         CurrentOfNode currentOfNode = null;
201         FromList resultFromList;
202         ResultColumnList afterColumns = null;
203
204         DataDictionary dataDictionary = getDataDictionary();
205
206         // check if targetTable is a synonym
207
if (targetTableName != null)
208         {
209             TableName synonymTab = resolveTableToSynonym(this.targetTableName);
210             if (synonymTab != null)
211             {
212                 this.synonymTableName = targetTableName;
213                 this.targetTableName = synonymTab;
214             }
215         }
216
217         bindTables(dataDictionary);
218
219         // wait to bind named target table until the cursor
220
// binding is done, so that we can get it from the
221
// cursor if this is a positioned update.
222

223         // for positioned update, get the cursor's target table.
224
if (SanityManager.DEBUG)
225         {
226             SanityManager.ASSERT((resultSet!=null && resultSet instanceof SelectNode),
227                 "Update must have a select result set");
228         }
229
230         SelectNode sel;
231         sel = (SelectNode)resultSet;
232         targetTable = (FromTable) sel.fromList.elementAt(0);
233
234         if (targetTable instanceof CurrentOfNode)
235         {
236             positionedUpdate = true;
237             currentOfNode = (CurrentOfNode) targetTable;
238             cursorTargetTableName = currentOfNode.getBaseCursorTargetTableName();
239
240             // instead of an assert, we might say the cursor is not updatable.
241
if (SanityManager.DEBUG)
242             {
243                 SanityManager.ASSERT(cursorTargetTableName != null);
244             }
245         }
246
247         if (targetTable instanceof FromVTI)
248         {
249             targetVTI = (FromVTI) targetTable;
250             targetVTI.setTarget();
251         }
252         else
253         {
254             // positioned update can leave off the target table.
255
// we get it from the cursor supplying the position.
256
if (targetTableName == null)
257             {
258                 // verify we have current of
259
if (SanityManager.DEBUG)
260                     SanityManager.ASSERT(cursorTargetTableName!=null);
261
262                 targetTableName = cursorTargetTableName;
263             }
264             // for positioned update, we need to verify that
265
// the named table is the same as the cursor's target.
266
else if (cursorTargetTableName != null)
267             {
268                 // this match requires that the named table in the update
269
// be the same as a correlation name in the cursor.
270
if ( !targetTableName.equals(cursorTargetTableName))
271                 {
272                     throw StandardException.newException(SQLState.LANG_CURSOR_UPDATE_MISMATCH,
273                         targetTableName,
274                         currentOfNode.getCursorName());
275                 }
276             }
277         }
278         
279         // because we verified that the tables match
280
// and we already bound the cursor or the select,
281
// the table descriptor should always be found.
282
verifyTargetTable();
283         
284         /* OVERVIEW - We generate a new ResultColumn, CurrentRowLocation(), and
285          * prepend it to the beginning of the source ResultColumnList. This
286          * will tell us which row(s) to update at execution time. However,
287          * we must defer prepending this generated column until the other
288          * ResultColumns are bound since there will be no ColumnDescriptor
289          * for the generated column. Thus, the sequence of actions is:
290          *
291          * o Bind existing ResultColumnList (columns in SET clause)
292          * o If this is a positioned update with a FOR UPDATE OF list,
293          * then verify that all of the target columns are in the
294          * FOR UPDATE OF list.
295          * o Get the list of indexes that need to be updated.
296          * o Create a ResultColumnList of all the columns in the target
297          * table - this represents the old row.
298          * o If we don't know which columns are being updated,
299          * expand the original ResultColumnList to include all the
300          * columns in the target table, and sort it to be in the
301          * order of the columns in the target table. This represents
302          * the new row. Append it to the ResultColumnList representing
303          * the old row.
304          * o Construct the changedColumnIds array sorted by column position.
305          * o Generate the read column bit map and append any columns
306          * needed for index maint, etc.
307          * o Generate a new ResultColumn for CurrentRowLocation() and
308          * mark it as a generated column.
309          * o Append the new ResultColumn to the ResultColumnList
310          * (This must be done before binding the expressions, so
311          * that the proper type info gets propagated to the new
312          * ResultColumn.)
313          * o Bind the expressions.
314          * o Bind the generated ResultColumn.
315          */

316
317         /* Verify that all underlying ResultSets reclaimed their FromList */
318         if (SanityManager.DEBUG)
319         {
320             SanityManager.ASSERT(fromList.size() == 0,
321                 "fromList.size() is expected to be 0, not " +
322                 fromList.size() +
323                 " on return from RS.bindExpressions()");
324         }
325
326         /*
327         ** The current result column list is the one supplied by the user.
328         ** Mark these columns as "updated", so we can tell later which
329         ** columns are really being updated, and which have been added
330         ** but are not really being updated.
331         */

332         resultSet.getResultColumns().markUpdated();
333
334         /* Prepend CurrentRowLocation() to the select's result column list. */
335         if (SanityManager.DEBUG)
336         SanityManager.ASSERT((resultSet.resultColumns != null),
337                               "resultColumns is expected not to be null at bind time");
338
339         /*
340         ** Get the result FromTable, which should be the only table in the
341         ** from list.
342         */

343         resultFromList = resultSet.getFromList();
344         if (SanityManager.DEBUG)
345         SanityManager.ASSERT(resultFromList.size() == 1,
346             "More than one table in result from list in an update.");
347
348         /* Normalize the SET clause's result column list for synonym */
349         if (synonymTableName != null)
350             normalizeSynonymColumns( resultSet.resultColumns, targetTable );
351         
352         /* Bind the original result columns by column name */
353         normalizeCorrelatedColumns( resultSet.resultColumns, targetTable );
354
355         getCompilerContext().pushCurrentPrivType(getPrivType()); // Update privilege
356
resultSet.bindResultColumns(targetTableDescriptor,
357                     targetVTI,
358                     resultSet.resultColumns, this,
359                     fromList);
360         getCompilerContext().popCurrentPrivType();
361
362         LanguageConnectionContext lcc = getLanguageConnectionContext();
363         if (lcc.getAutoincrementUpdate() == false)
364             resultSet.getResultColumns().checkAutoincrement(null);
365
366         /*
367         ** Mark the columns in this UpdateNode's result column list as
368         ** updateable in the ResultColumnList of the table being updated.
369         ** only do this for FromBaseTables - if the result table is a
370         ** CurrentOfNode, it already knows what columns in its cursor
371         ** are updateable.
372         */

373         boolean allColumns = false;
374         if (targetTable instanceof FromBaseTable)
375         {
376             ((FromBaseTable) targetTable).markUpdated(
377                                                 resultSet.getResultColumns());
378         }
379         else if (targetTable instanceof FromVTI)
380         {
381             resultColumnList = resultSet.getResultColumns();
382         }
383         else
384         {
385             /*
386             ** Positioned update: WHERE CURRENT OF
387             */

388             if (SanityManager.DEBUG)
389             {
390                 SanityManager.ASSERT(currentOfNode != null, "currentOfNode is null");
391             }
392
393             ExecPreparedStatement cursorStmt = currentOfNode.getCursorStatement();
394             String JavaDoc[] ucl = cursorStmt.getUpdateColumns();
395
396             /*
397             ** If there is no update column list, we need to build
398             ** out the result column list to have all columns.
399             */

400             if (ucl == null || (ucl.length == 0))
401             {
402                 /*
403                 ** Get the resultColumnList representing ALL of the columns in the
404                 ** base table. This is the "before" portion of the result row.
405                 */

406                 getResultColumnList();
407
408                 /*
409                 ** Add the "after" portion of the result row. This is the update
410                 ** list augmented to include every column in the target table.
411                 ** Those columns that are not being updated are set to themselves.
412                 ** The expanded list will be in the order of the columns in the base
413                 ** table.
414                 */

415                 afterColumns = resultSet.getResultColumns().expandToAll(
416                                                     targetTableDescriptor,
417                                                     targetTable.getTableName());
418     
419                 /*
420                 ** Need to get all indexes here since we aren't calling
421                 ** getReadMap().
422                 */

423                 getAffectedIndexes(targetTableDescriptor,
424                                     (ResultColumnList)null, (FormatableBitSet)null);
425                 allColumns = true;
426             }
427             else
428             {
429                 /* Check the updatability */
430                 resultSet.getResultColumns().checkColumnUpdateability(ucl,
431                                 currentOfNode.getCursorName());
432             }
433         }
434
435         changedColumnIds = getChangedColumnIds(resultSet.getResultColumns());
436
437         /*
438         ** We need to add in all the columns that are needed
439         ** by the constraints on this table.
440         */

441         if (!allColumns && targetVTI == null)
442         {
443             getCompilerContext().pushCurrentPrivType( Authorizer.NULL_PRIV);
444             try
445             {
446                 readColsBitSet = new FormatableBitSet();
447                 FromBaseTable fbt = getResultColumnList(resultSet.getResultColumns());
448                 afterColumns = resultSet.getResultColumns().copyListAndObjects();
449
450                 readColsBitSet = getReadMap(dataDictionary,
451                                         targetTableDescriptor,
452                                         afterColumns);
453
454                 afterColumns = fbt.addColsToList(afterColumns, readColsBitSet);
455                 resultColumnList = fbt.addColsToList(resultColumnList, readColsBitSet);
456
457                 /*
458                 ** If all bits are set, then behave as if we chose all
459                 ** in the first place
460                 */

461                 int i = 1;
462                 int size = targetTableDescriptor.getMaxColumnID();
463                 for (; i <= size; i++)
464                 {
465                     if (!readColsBitSet.get(i))
466                     {
467                         break;
468                     }
469                 }
470
471                 if (i > size)
472                 {
473                     readColsBitSet = null;
474                     allColumns = true;
475                 }
476             }
477             finally
478             {
479                 getCompilerContext().popCurrentPrivType();
480             }
481         }
482
483         if (targetVTI == null)
484         {
485             /*
486             ** Construct an empty heap row for use in our constant action.
487             */

488             emptyHeapRow = targetTableDescriptor.getEmptyExecRow(getContextManager());
489
490             /* Append the list of "after" columns to the list of "before" columns,
491              * preserving the afterColumns list. (Necessary for binding
492              * check constraints.)
493              */

494             resultColumnList.appendResultColumns(afterColumns, false);
495
496             /* Generate the RowLocation column */
497             rowLocationNode = (CurrentRowLocationNode) getNodeFactory().getNode(
498                                         C_NodeTypes.CURRENT_ROW_LOCATION_NODE,
499                                         getContextManager());
500         }
501         else
502         {
503             rowLocationNode = (NumericConstantNode) getNodeFactory().getNode(
504                                         C_NodeTypes.INT_CONSTANT_NODE,
505                                         ReuseFactory.getInteger( 0),
506                                         getContextManager());
507         }
508             
509         rowLocationColumn =
510           (ResultColumn) getNodeFactory().getNode(
511               C_NodeTypes.RESULT_COLUMN,
512               COLUMNNAME,
513               rowLocationNode,
514               getContextManager());
515         rowLocationColumn.markGenerated();
516
517             /* Append to the ResultColumnList */
518         resultColumnList.addResultColumn(rowLocationColumn);
519
520         /*
521          * The last thing that we do to the generated RCL is to clear
522          * the table name out from each RC. See comment on
523          * checkTableNameAndScrubResultColumns().
524          */

525         checkTableNameAndScrubResultColumns(resultColumnList);
526
527         /* Set the new result column list in the result set */
528         resultSet.setResultColumns(resultColumnList);
529
530         /* Bind the expressions */
531         getCompilerContext().pushCurrentPrivType(getPrivType()); // Update privilege
532
super.bindExpressions();
533         getCompilerContext().popCurrentPrivType();
534
535         /* Bind untyped nulls directly under the result columns */
536         resultSet.
537             getResultColumns().
538                 bindUntypedNullsToResultColumns(resultColumnList);
539
540         if (null != rowLocationColumn)
541         {
542             /* Bind the new ResultColumn */
543             rowLocationColumn.bindResultColumnToExpression();
544         }
545
546         resultColumnList.checkStorableExpressions();
547
548         /* Insert a NormalizeResultSetNode above the source if the source
549          * and target column types and lengths do not match.
550          */

551         if (! resultColumnList.columnTypesAndLengthsMatch())
552         {
553             resultSet = resultSet.genNormalizeResultSetNode(resultSet, true);
554             resultColumnList.copyTypesAndLengthsToSource(resultSet.getResultColumns());
555                                 
556             if (hasCheckConstraints(dataDictionary, targetTableDescriptor))
557             {
558                 /* Get and bind all check constraints on the columns
559                  * being updated. We want to bind the check constraints against
560                  * the after columns. We need to bind against the portion of the
561                  * resultColumns in the new NormalizeResultSet that point to
562                  * afterColumns. Create an RCL composed of just those RCs in
563                  * order to bind the check constraints.
564                  */

565                 int afterColumnsSize = afterColumns.size();
566                 afterColumns = (ResultColumnList) getNodeFactory().getNode(
567                                                 C_NodeTypes.RESULT_COLUMN_LIST,
568                                                 getContextManager());
569                 ResultColumnList normalizedRCs = resultSet.getResultColumns();
570                 for (int index = 0; index < afterColumnsSize; index++)
571                 {
572                     afterColumns.addElement(normalizedRCs.elementAt(index + afterColumnsSize));
573                 }
574             }
575         }
576
577         if( null != targetVTI)
578         {
579             deferred = VTIDeferModPolicy.deferIt( DeferModification.UPDATE_STATEMENT,
580                                                   targetVTI,
581                                                   resultColumnList.getColumnNames(),
582                                                   sel.getWhereClause());
583         }
584         else // not VTI
585
{
586             /* we always include triggers in core language */
587             boolean hasTriggers = (getAllRelevantTriggers(dataDictionary, targetTableDescriptor,
588                                                           changedColumnIds, true).size() > 0);
589
590             /* Get and bind all constraints on the columns being updated */
591             checkConstraints = bindConstraints( dataDictionary,
592                                                 getNodeFactory(),
593                                                 targetTableDescriptor,
594                                                 null,
595                                                 hasTriggers ? resultColumnList : afterColumns,
596                                                 changedColumnIds,
597                                                 readColsBitSet,
598                                                 false,
599                                                 true); /* we always include triggers in core language */
600
601             /* If the target table is also a source table, then
602              * the update will have to be in deferred mode
603              * For updates, this means that the target table appears in a
604              * subquery. Also, self referencing foreign keys are
605              * deferred. And triggers cause an update to be deferred.
606              */

607             if (resultSet.subqueryReferencesTarget(
608                 targetTableDescriptor.getName(), true) ||
609                 requiresDeferredProcessing())
610             {
611                 deferred = true;
612             }
613         }
614
615         getCompilerContext().popCurrentPrivType();
616
617         return this;
618     } // end of bind()
619

620     int getPrivType()
621     {
622         return Authorizer.UPDATE_PRIV;
623     }
624
625     /**
626      * Return true if the node references SESSION schema tables (temporary or permanent)
627      *
628      * @return true if references SESSION schema tables, else false
629      *
630      * @exception StandardException Thrown on error
631      */

632     public boolean referencesSessionSchema()
633         throws StandardException
634     {
635         //If this node references a SESSION schema table, then return true.
636
return(resultSet.referencesSessionSchema());
637
638     }
639
640     /**
641      * Compile constants that Execution will use
642      *
643      * @exception StandardException Thrown on failure
644      */

645     public ConstantAction makeConstantAction() throws StandardException
646     {
647         /*
648         ** Updates are also deferred if they update a column in the index
649         ** used to scan the table being updated.
650         */

651         if (! deferred )
652         {
653             ConglomerateDescriptor updateCD =
654                                         targetTable.
655                                             getTrulyTheBestAccessPath().
656                                                 getConglomerateDescriptor();
657
658             if (updateCD != null && updateCD.isIndex())
659             {
660                 int [] baseColumns =
661                         updateCD.getIndexDescriptor().baseColumnPositions();
662
663                 if (resultSet.
664                         getResultColumns().
665                                         updateOverlaps(baseColumns))
666                 {
667                     deferred = true;
668                 }
669             }
670         }
671
672         if( null == targetTableDescriptor)
673         {
674             /* Return constant action for VTI
675              * NOTE: ConstantAction responsible for preserving instantiated
676              * VTIs for in-memory queries and for only preserving VTIs
677              * that implement Serializable for SPSs.
678              */

679             return getGenericConstantActionFactory().getUpdatableVTIConstantAction( DeferModification.UPDATE_STATEMENT,
680                         deferred, changedColumnIds);
681         }
682
683         int lockMode = resultSet.updateTargetLockMode();
684         long heapConglomId = targetTableDescriptor.getHeapConglomerateId();
685         TransactionController tc =
686             getLanguageConnectionContext().getTransactionCompile();
687         StaticCompiledOpenConglomInfo[] indexSCOCIs =
688             new StaticCompiledOpenConglomInfo[indexConglomerateNumbers.length];
689
690         for (int index = 0; index < indexSCOCIs.length; index++)
691         {
692             indexSCOCIs[index] = tc.getStaticCompiledConglomInfo(indexConglomerateNumbers[index]);
693         }
694
695         /*
696         ** Do table locking if the table's lock granularity is
697         ** set to table.
698         */

699         if (targetTableDescriptor.getLockGranularity() == TableDescriptor.TABLE_LOCK_GRANULARITY)
700         {
701             lockMode = TransactionController.MODE_TABLE;
702         }
703
704
705         return getGenericConstantActionFactory().getUpdateConstantAction
706             ( heapConglomId,
707               targetTableDescriptor.getTableType(),
708               tc.getStaticCompiledConglomInfo(heapConglomId),
709               indicesToMaintain,
710               indexConglomerateNumbers,
711               indexSCOCIs,
712               indexNames,
713               emptyHeapRow,
714               deferred,
715               targetTableDescriptor.getUUID(),
716               lockMode,
717               false,
718               changedColumnIds, null, null,
719               getFKInfo(),
720               getTriggerInfo(),
721               (readColsBitSet == null) ? (FormatableBitSet)null : new FormatableBitSet(readColsBitSet),
722               getReadColMap(targetTableDescriptor.getNumberOfColumns(),readColsBitSet),
723               resultColumnList.getStreamStorableColIds(targetTableDescriptor.getNumberOfColumns()),
724               (readColsBitSet == null) ?
725                   targetTableDescriptor.getNumberOfColumns() :
726                   readColsBitSet.getNumBitsSet(),
727               positionedUpdate,
728               resultSet.isOneRowResultSet()
729               );
730     }
731
732     /**
733      * Updates are deferred if they update a column in the index
734      * used to scan the table being updated.
735      */

736     protected void setDeferredForUpdateOfIndexColumn()
737     {
738         /* Don't bother checking if we're already deferred */
739         if (! deferred )
740         {
741             /* Get the conglomerate descriptor for the target table */
742             ConglomerateDescriptor updateCD =
743                                         targetTable.
744                                             getTrulyTheBestAccessPath().
745                                                 getConglomerateDescriptor();
746
747             /* If it an index? */
748             if (updateCD != null && updateCD.isIndex())
749             {
750                 int [] baseColumns =
751                         updateCD.getIndexDescriptor().baseColumnPositions();
752
753                 /* Are any of the index columns updated? */
754                 if (resultSet.
755                         getResultColumns().
756                                         updateOverlaps(baseColumns))
757                 {
758                     deferred = true;
759                 }
760             }
761         }
762     }
763
764     /**
765      * Code generation for update.
766      * The generated code will contain:
767      * o A static member for the (xxx)ResultSet with the RowLocations and
768      * new update values
769      * o The static member will be assigned the appropriate ResultSet within
770      * the nested calls to get the ResultSets. (The appropriate cast to the
771      * (xxx)ResultSet will be generated.)
772      * o The CurrentRowLocation() in SelectNode's select list will generate
773      * a new method for returning the RowLocation as well as a call to
774      * that method when generating the (xxx)ResultSet.
775      *
776      * @param acb The ActivationClassBuilder for the class being built
777      * @param mb The method for the execute() method to be built
778      *
779      *
780      * @exception StandardException Thrown on error
781      */

782     public void generate(ActivationClassBuilder acb,
783                                 MethodBuilder mb)
784                             throws StandardException
785     {
786         //If the DML is on the temporary table, generate the code to mark temporary table as modified in the current UOW
787
generateCodeForTemporaryTable(acb, mb);
788
789         /* generate the parameters */
790         if(!isDependentTable)
791             generateParameterValueSet(acb);
792
793
794         /* Create the static declaration for the scan ResultSet which generates the
795          * RowLocations to be updated
796          * RESOLVE - Need to deal with the type of the static member.
797          */

798         acb.newFieldDeclaration(Modifier.PRIVATE,
799                                 ClassName.CursorResultSet,
800                                 acb.newRowLocationScanResultSetName());
801
802         /*
803         ** Generate the update result set, giving it either the original
804         ** source or the normalize result set, the constant action.
805         */

806
807         acb.pushGetResultSetFactoryExpression(mb);
808         resultSet.generate(acb, mb); // arg 1
809

810         if( null != targetVTI)
811         {
812             targetVTI.assignCostEstimate(resultSet.getNewCostEstimate());
813             mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getUpdateVTIResultSet", ClassName.ResultSet, 1);
814         }
815         else
816         {
817             // generate code to evaluate CHECK CONSTRAINTS
818
generateCheckConstraints( checkConstraints, acb, mb ); // arg 2
819

820             if(isDependentTable)
821             {
822                 mb.push(acb.addItem(makeConstantAction()));
823                 mb.push(acb.addItem(makeResultDescription()));
824                 mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getDeleteCascadeUpdateResultSet",
825                               ClassName.ResultSet, 4);
826             }else
827             {
828                 mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getUpdateResultSet",
829                               ClassName.ResultSet, 2);
830             }
831         }
832     }
833
834     /**
835      * Return the type of statement, something from
836      * StatementType.
837      *
838      * @return the type of statement
839      */

840     protected final int getStatementType()
841     {
842         return StatementType.UPDATE;
843     }
844
845
846     /**
847      * Gets the map of all columns which must be read out of the base table.
848      * These are the columns needed to<UL>:
849      * <LI>maintain indices</LI>
850      * <LI>maintain foreign keys</LI>
851      * <LI>support Replication's Delta Optimization</LI></UL>
852      * <p>
853      * The returned map is a FormatableBitSet with 1 bit for each column in the
854      * table plus an extra, unsued 0-bit. If a 1-based column id must
855      * be read from the base table, then the corresponding 1-based bit
856      * is turned ON in the returned FormatableBitSet.
857      * <p>
858      * <B>NOTE</B>: this method is not expected to be called when
859      * all columns are being updated (i.e. updateColumnList is null).
860      *
861      * @param dd the data dictionary to look in
862      * @param baseTable the base table descriptor
863      * @param updateColumnList the rcl for the update. CANNOT BE NULL
864      *
865      * @return a FormatableBitSet of columns to be read out of the base table
866      *
867      * @exception StandardException Thrown on error
868      */

869     public FormatableBitSet getReadMap
870     (
871         DataDictionary dd,
872         TableDescriptor baseTable,
873         ResultColumnList updateColumnList
874     )
875         throws StandardException
876     {
877         boolean[] needsDeferredProcessing = new boolean[1];
878         needsDeferredProcessing[0] = requiresDeferredProcessing();
879
880         Vector JavaDoc conglomVector = new Vector JavaDoc();
881         relevantCdl = new ConstraintDescriptorList();
882         relevantTriggers = new GenericDescriptorList();
883
884         FormatableBitSet columnMap = UpdateNode.getUpdateReadMap(baseTable,
885             updateColumnList, conglomVector, relevantCdl, relevantTriggers, needsDeferredProcessing );
886
887         markAffectedIndexes( conglomVector );
888
889         adjustDeferredFlag( needsDeferredProcessing[0] );
890
891         return columnMap;
892     }
893
894
895     /**
896      * Construct the changedColumnIds array. Note we sort its entries by
897      * columnId.
898      */

899     private int[] getChangedColumnIds(ResultColumnList rcl)
900     {
901         if (rcl == null) { return (int[])null; }
902         else { return rcl.sortMe(); }
903     }
904     /**
905       * Builds a bitmap of all columns which should be read from the
906       * Store in order to satisfy an UPDATE statement.
907       *
908       * Is passed a list of updated columns. Does the following:
909       *
910       * 1) finds all indices which overlap the updated columns
911       * 2) adds the index columns to a bitmap of affected columns
912       * 3) adds the index descriptors to a list of conglomerate
913       * descriptors.
914       * 4) finds all constraints which overlap the updated columns
915       * and adds the constrained columns to the bitmap
916       * 5) finds all triggers which overlap the updated columns.
917       * 6) if there are any triggers, marks all columns in the bitmap
918       * 7) adds the triggers to an evolving list of triggers
919       *
920       * @param updateColumnList a list of updated columns
921       * @param conglomVector OUT: vector of affected indices
922       * @param relevantConstraints IN/OUT. Empty list is passed in. We hang constraints on it as we go.
923       * @param relevantTriggers IN/OUT. Passed in as an empty list. Filled in as we go.
924       * @param needsDeferredProcessing IN/OUT. true if the statement already needs
925       * deferred processing. set while evaluating this
926       * routine if a trigger or constraint requires
927       * deferred processing
928       *
929       * @return a FormatableBitSet of columns to be read out of the base table
930       *
931       * @exception StandardException Thrown on error
932       */

933     public static FormatableBitSet getUpdateReadMap
934     (
935         TableDescriptor baseTable,
936         ResultColumnList updateColumnList,
937         Vector JavaDoc conglomVector,
938         ConstraintDescriptorList relevantConstraints,
939         GenericDescriptorList relevantTriggers,
940         boolean[] needsDeferredProcessing
941     )
942         throws StandardException
943     {
944         if (SanityManager.DEBUG)
945         {
946             SanityManager.ASSERT(updateColumnList != null, "updateColumnList is null");
947         }
948
949         int columnCount = baseTable.getMaxColumnID();
950         FormatableBitSet columnMap = new FormatableBitSet(columnCount + 1);
951
952         /*
953         ** Add all the changed columns. We don't strictly
954         ** need the before image of the changed column in all cases,
955         ** but it makes life much easier since things are set
956         ** up around the assumption that we have the before
957         ** and after image of the column.
958         */

959         int[] changedColumnIds = updateColumnList.sortMe();
960
961         for (int ix = 0; ix < changedColumnIds.length; ix++)
962         {
963             columnMap.set(changedColumnIds[ix]);
964         }
965
966         /*
967         ** Get a list of the indexes that need to be
968         ** updated. ColumnMap contains all indexed
969         ** columns where 1 or more columns in the index
970         ** are going to be modified.
971         */

972         DMLModStatementNode.getXAffectedIndexes(baseTable, updateColumnList, columnMap, conglomVector );
973  
974         /*
975         ** Add all columns needed for constraints. We don't
976         ** need to bother with foreign key/primary key constraints
977         ** because they are added as a side effect of adding
978         ** their indexes above.
979         */

980         baseTable.getAllRelevantConstraints
981             ( StatementType.UPDATE, false, changedColumnIds, needsDeferredProcessing, relevantConstraints );
982
983         int rclSize = relevantConstraints.size();
984         for (int index = 0; index < rclSize; index++)
985         {
986             ConstraintDescriptor cd = relevantConstraints.elementAt(index);
987             if (cd.getConstraintType() != DataDictionary.CHECK_CONSTRAINT)
988             {
989                 continue;
990             }
991
992             int[] refColumns = ((CheckConstraintDescriptor)cd).getReferencedColumns();
993             for (int i = 0; i < refColumns.length; i++)
994             {
995                 columnMap.set(refColumns[i]);
996             }
997         }
998
999         /*
1000        ** If we have any triggers, then get all the columns
1001        ** because we don't know what the user will ultimately
1002        ** reference.
1003        */

1004
1005        baseTable.getAllRelevantTriggers( StatementType.UPDATE, changedColumnIds, relevantTriggers );
1006        if ( relevantTriggers.size() > 0 ) { needsDeferredProcessing[0] = true; }
1007
1008        if (relevantTriggers.size() > 0)
1009        {
1010            for (int i = 1; i <= columnCount; i++)
1011            {
1012                columnMap.set(i);
1013            }
1014        }
1015
1016        return columnMap;
1017    }
1018
1019    /*
1020     * Force correlated column references in the SET clause to have the
1021     * name of the base table. This dances around the problem alluded to
1022     * in scrubResultColumn().
1023     */

1024    private void normalizeCorrelatedColumns( ResultColumnList rcl, FromTable fromTable )
1025        throws StandardException
1026    {
1027        String JavaDoc correlationName = fromTable.getCorrelationName();
1028
1029        if ( correlationName == null ) { return; }
1030
1031        TableName tableNameNode;
1032
1033        if ( fromTable instanceof CurrentOfNode )
1034        { tableNameNode = ((CurrentOfNode) fromTable).getBaseCursorTargetTableName(); }
1035        else { tableNameNode = makeTableName( null, fromTable.getBaseTableName() ); }
1036        
1037        int count = rcl.size();
1038
1039        for ( int i = 0; i < count; i++ )
1040        {
1041            ResultColumn column = (ResultColumn) rcl.elementAt( i );
1042            ColumnReference reference = column.getReference();
1043
1044            if ( (reference != null) && correlationName.equals( reference.getTableName() ) )
1045            {
1046                reference.setTableNameNode( tableNameNode );
1047            }
1048        }
1049        
1050    }
1051
1052    /**
1053     * Check table name and then clear it from the result set columns.
1054     *
1055     * @exception StandardExcepion if invalid column/table is specified.
1056     */

1057    private void checkTableNameAndScrubResultColumns(ResultColumnList rcl)
1058            throws StandardException
1059    {
1060        int columnCount = rcl.size();
1061        int tableCount = ((SelectNode)resultSet).fromList.size();
1062
1063        for ( int i = 0; i < columnCount; i++ )
1064        {
1065            boolean foundMatchingTable = false;
1066            ResultColumn column = (ResultColumn) rcl.elementAt( i );
1067
1068            if (column.getTableName() != null) {
1069                for (int j = 0; j < tableCount; j++) {
1070                    FromTable fromTable = (FromTable) ((SelectNode)resultSet).
1071                            fromList.elementAt(j);
1072                    final String JavaDoc tableName;
1073                    if ( fromTable instanceof CurrentOfNode ) {
1074                        tableName = ((CurrentOfNode)fromTable).
1075                                getBaseCursorTargetTableName().getTableName();
1076                    } else {
1077                        tableName = fromTable.getBaseTableName();
1078                    }
1079
1080                    if (column.getTableName().equals(tableName)) {
1081                        foundMatchingTable = true;
1082                        break;
1083                    }
1084                }
1085
1086                if (!foundMatchingTable) {
1087                    throw StandardException.newException(
1088                            SQLState.LANG_COLUMN_NOT_FOUND,
1089                            column.getTableName() + "." + column.getName());
1090                }
1091            }
1092
1093            /* The table name is
1094             * unnecessary for an update. More importantly, though, it
1095             * creates a problem in the degenerate case with a positioned
1096             * update. The user must specify the base table name for a
1097             * positioned update. If a correlation name was specified for
1098             * the cursor, then a match for the ColumnReference would not
1099             * be found if we didn't null out the name. (Aren't you
1100             * glad you asked?)
1101             */

1102            column.clearTableName();
1103        }
1104    }
1105    
1106    /**
1107     * Normalize synonym column references to have the name of the base table.
1108     *
1109     * @param rcl The result column list of the target table
1110     * @param fromTable The table name to set the column refs to
1111     *
1112     * @exception StandardException Thrown on error
1113     */

1114    private void normalizeSynonymColumns(
1115    ResultColumnList rcl,
1116    FromTable fromTable)
1117        throws StandardException
1118    {
1119        if (fromTable.getCorrelationName() != null)
1120        {
1121            return;
1122        }
1123        
1124        TableName tableNameNode;
1125        if (fromTable instanceof CurrentOfNode)
1126        {
1127            tableNameNode =
1128                ((CurrentOfNode) fromTable).getBaseCursorTargetTableName();
1129        }
1130        else
1131        {
1132            tableNameNode = makeTableName(null, fromTable.getBaseTableName());
1133        }
1134        
1135        super.normalizeSynonymColumns(rcl, tableNameNode);
1136    }
1137    
1138} // end of UpdateNode
1139
Popular Tags