0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.UpdateNode
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.compile;
0023:
0024: import org.apache.derby.iapi.services.context.ContextManager;
0025:
0026: import org.apache.derby.iapi.services.loader.GeneratedMethod;
0027:
0028: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0029:
0030: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0031: import org.apache.derby.iapi.sql.conn.Authorizer;
0032: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0033: import org.apache.derby.impl.sql.execute.FKInfo;
0034: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0035:
0036: import org.apache.derby.iapi.services.sanity.SanityManager;
0037: import org.apache.derby.iapi.error.StandardException;
0038: import org.apache.derby.iapi.sql.compile.CompilerContext;
0039: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0040: import org.apache.derby.iapi.sql.compile.Visitable;
0041: import org.apache.derby.iapi.sql.compile.Visitor;
0042:
0043: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0044:
0045: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
0046: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
0047: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
0048: import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
0049: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0050: import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
0051: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
0052: import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
0053:
0054: import org.apache.derby.iapi.reference.SQLState;
0055: import org.apache.derby.iapi.sql.execute.ConstantAction;
0056: import org.apache.derby.iapi.sql.execute.CursorResultSet;
0057: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
0058: import org.apache.derby.iapi.sql.execute.ExecRow;
0059:
0060: import org.apache.derby.iapi.sql.Activation;
0061: import org.apache.derby.iapi.sql.ResultSet;
0062: import org.apache.derby.iapi.sql.StatementType;
0063:
0064: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
0065: import org.apache.derby.iapi.store.access.TransactionController;
0066:
0067: import org.apache.derby.vti.DeferModification;
0068:
0069: import org.apache.derby.iapi.services.io.FormatableBitSet;
0070: import org.apache.derby.iapi.reference.ClassName;
0071:
0072: import org.apache.derby.iapi.util.ReuseFactory;
0073: import org.apache.derby.iapi.services.classfile.VMOpcode;
0074:
0075: import java.lang.reflect.Modifier;
0076: import java.sql.SQLException;
0077: import java.util.Properties;
0078: import java.util.Vector;
0079:
0080: /**
0081: * An UpdateNode represents an UPDATE statement. It is the top node of the
0082: * query tree for that statement.
0083: * For positioned update, there may be no from table specified.
0084: * The from table will be derived from the cursor specification of
0085: * the named cursor.
0086: *
0087: * @author Jeff Lichtman
0088: */
0089:
0090: public final class UpdateNode extends DMLModStatementNode {
0091: //Note: These are public so they will be visible to
0092: //the RepUpdateNode.
0093: public int[] changedColumnIds;
0094: public ExecRow emptyHeapRow;
0095: public boolean deferred;
0096: public ValueNode checkConstraints;
0097: public FKInfo fkInfo;
0098:
0099: protected FromTable targetTable;
0100: protected FormatableBitSet readColsBitSet;
0101: protected boolean positionedUpdate;
0102:
0103: /* Column name for the RowLocation in the ResultSet */
0104: public static final String COLUMNNAME = "###RowLocationToUpdate";
0105:
0106: /**
0107: * Initializer for an UpdateNode.
0108: *
0109: * @param targetTableName The name of the table to update
0110: * @param resultSet The ResultSet that will generate
0111: * the rows to update from the given table
0112: */
0113:
0114: public void init(Object targetTableName, Object resultSet) {
0115: super .init(resultSet);
0116: this .targetTableName = (TableName) targetTableName;
0117: }
0118:
0119: /**
0120: * Convert this object to a String. See comments in QueryTreeNode.java
0121: * for how this should be done for tree printing.
0122: *
0123: * @return This object as a String
0124: */
0125:
0126: public String toString() {
0127: if (SanityManager.DEBUG) {
0128: return targetTableName.toString() + "\n" + super .toString();
0129: } else {
0130: return "";
0131: }
0132: }
0133:
0134: public String statementToString() {
0135: return "UPDATE";
0136: }
0137:
0138: /**
0139: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0140: * how tree printing is supposed to work.
0141: *
0142: * @param depth The depth of this node in the tree
0143: */
0144:
0145: public void printSubNodes(int depth) {
0146: if (SanityManager.DEBUG) {
0147: super .printSubNodes(depth);
0148:
0149: if (targetTableName != null) {
0150: printLabel(depth, "targetTableName: ");
0151: targetTableName.treePrint(depth + 1);
0152: }
0153:
0154: /* RESOLVE - need to print out targetTableDescriptor */
0155: }
0156: }
0157:
0158: /**
0159: * Bind this UpdateNode. This means looking up tables and columns and
0160: * getting their types, and figuring out the result types of all
0161: * expressions, as well as doing view resolution, permissions checking,
0162: * etc.
0163: * <p>
0164: * Binding an update will also massage the tree so that
0165: * the ResultSetNode has a set of columns to contain the old row
0166: * value, followed by a set of columns to contain the new row
0167: * value, followed by a column to contain the RowLocation of the
0168: * row to be updated.
0169: *
0170: * @return The bound query tree
0171: *
0172: * @exception StandardException Thrown on error
0173: */
0174:
0175: public QueryTreeNode bind() throws StandardException {
0176: // We just need select privilege on the expressions
0177: getCompilerContext()
0178: .pushCurrentPrivType(Authorizer.SELECT_PRIV);
0179:
0180: FromList fromList = (FromList) getNodeFactory().getNode(
0181: C_NodeTypes.FROM_LIST,
0182: getNodeFactory().doJoinOrderOptimization(),
0183: getContextManager());
0184: ResultColumn rowLocationColumn = null;
0185: ValueNode rowLocationNode = null;
0186: TableName cursorTargetTableName = null;
0187: CurrentOfNode currentOfNode = null;
0188: FromList resultFromList;
0189: ResultColumnList afterColumns = null;
0190:
0191: DataDictionary dataDictionary = getDataDictionary();
0192:
0193: // check if targetTable is a synonym
0194: if (targetTableName != null) {
0195: TableName synonymTab = resolveTableToSynonym(this .targetTableName);
0196: if (synonymTab != null) {
0197: this .synonymTableName = targetTableName;
0198: this .targetTableName = synonymTab;
0199: }
0200: }
0201:
0202: bindTables(dataDictionary);
0203:
0204: // wait to bind named target table until the cursor
0205: // binding is done, so that we can get it from the
0206: // cursor if this is a positioned update.
0207:
0208: // for positioned update, get the cursor's target table.
0209: if (SanityManager.DEBUG) {
0210: SanityManager
0211: .ASSERT(
0212: (resultSet != null && resultSet instanceof SelectNode),
0213: "Update must have a select result set");
0214: }
0215:
0216: SelectNode sel;
0217: sel = (SelectNode) resultSet;
0218: targetTable = (FromTable) sel.fromList.elementAt(0);
0219:
0220: if (targetTable instanceof CurrentOfNode) {
0221: positionedUpdate = true;
0222: currentOfNode = (CurrentOfNode) targetTable;
0223: cursorTargetTableName = currentOfNode
0224: .getBaseCursorTargetTableName();
0225:
0226: // instead of an assert, we might say the cursor is not updatable.
0227: if (SanityManager.DEBUG) {
0228: SanityManager.ASSERT(cursorTargetTableName != null);
0229: }
0230: }
0231:
0232: if (targetTable instanceof FromVTI) {
0233: targetVTI = (FromVTI) targetTable;
0234: targetVTI.setTarget();
0235: } else {
0236: // positioned update can leave off the target table.
0237: // we get it from the cursor supplying the position.
0238: if (targetTableName == null) {
0239: // verify we have current of
0240: if (SanityManager.DEBUG)
0241: SanityManager.ASSERT(cursorTargetTableName != null);
0242:
0243: targetTableName = cursorTargetTableName;
0244: }
0245: // for positioned update, we need to verify that
0246: // the named table is the same as the cursor's target.
0247: else if (cursorTargetTableName != null) {
0248: // this match requires that the named table in the update
0249: // be the same as a correlation name in the cursor.
0250: if (!targetTableName.equals(cursorTargetTableName)) {
0251: throw StandardException.newException(
0252: SQLState.LANG_CURSOR_UPDATE_MISMATCH,
0253: targetTableName, currentOfNode
0254: .getCursorName());
0255: }
0256: }
0257: }
0258:
0259: // because we verified that the tables match
0260: // and we already bound the cursor or the select,
0261: // the table descriptor should always be found.
0262: verifyTargetTable();
0263:
0264: /* OVERVIEW - We generate a new ResultColumn, CurrentRowLocation(), and
0265: * prepend it to the beginning of the source ResultColumnList. This
0266: * will tell us which row(s) to update at execution time. However,
0267: * we must defer prepending this generated column until the other
0268: * ResultColumns are bound since there will be no ColumnDescriptor
0269: * for the generated column. Thus, the sequence of actions is:
0270: *
0271: * o Bind existing ResultColumnList (columns in SET clause)
0272: * o If this is a positioned update with a FOR UPDATE OF list,
0273: * then verify that all of the target columns are in the
0274: * FOR UPDATE OF list.
0275: * o Get the list of indexes that need to be updated.
0276: * o Create a ResultColumnList of all the columns in the target
0277: * table - this represents the old row.
0278: * o If we don't know which columns are being updated,
0279: * expand the original ResultColumnList to include all the
0280: * columns in the target table, and sort it to be in the
0281: * order of the columns in the target table. This represents
0282: * the new row. Append it to the ResultColumnList representing
0283: * the old row.
0284: * o Construct the changedColumnIds array sorted by column position.
0285: * o Generate the read column bit map and append any columns
0286: * needed for index maint, etc.
0287: * o Generate a new ResultColumn for CurrentRowLocation() and
0288: * mark it as a generated column.
0289: * o Append the new ResultColumn to the ResultColumnList
0290: * (This must be done before binding the expressions, so
0291: * that the proper type info gets propagated to the new
0292: * ResultColumn.)
0293: * o Bind the expressions.
0294: * o Bind the generated ResultColumn.
0295: */
0296:
0297: /* Verify that all underlying ResultSets reclaimed their FromList */
0298: if (SanityManager.DEBUG) {
0299: SanityManager.ASSERT(fromList.size() == 0,
0300: "fromList.size() is expected to be 0, not "
0301: + fromList.size()
0302: + " on return from RS.bindExpressions()");
0303: }
0304:
0305: /*
0306: ** The current result column list is the one supplied by the user.
0307: ** Mark these columns as "updated", so we can tell later which
0308: ** columns are really being updated, and which have been added
0309: ** but are not really being updated.
0310: */
0311: resultSet.getResultColumns().markUpdated();
0312:
0313: /* Prepend CurrentRowLocation() to the select's result column list. */
0314: if (SanityManager.DEBUG)
0315: SanityManager
0316: .ASSERT((resultSet.resultColumns != null),
0317: "resultColumns is expected not to be null at bind time");
0318:
0319: /*
0320: ** Get the result FromTable, which should be the only table in the
0321: ** from list.
0322: */
0323: resultFromList = resultSet.getFromList();
0324: if (SanityManager.DEBUG)
0325: SanityManager
0326: .ASSERT(resultFromList.size() == 1,
0327: "More than one table in result from list in an update.");
0328:
0329: /* Normalize the SET clause's result column list for synonym */
0330: if (synonymTableName != null)
0331: normalizeSynonymColumns(resultSet.resultColumns,
0332: targetTable);
0333:
0334: /* Bind the original result columns by column name */
0335: normalizeCorrelatedColumns(resultSet.resultColumns, targetTable);
0336:
0337: getCompilerContext().pushCurrentPrivType(getPrivType()); // Update privilege
0338: resultSet.bindResultColumns(targetTableDescriptor, targetVTI,
0339: resultSet.resultColumns, this , fromList);
0340: getCompilerContext().popCurrentPrivType();
0341:
0342: LanguageConnectionContext lcc = getLanguageConnectionContext();
0343: if (lcc.getAutoincrementUpdate() == false)
0344: resultSet.getResultColumns().checkAutoincrement(null);
0345:
0346: /*
0347: ** Mark the columns in this UpdateNode's result column list as
0348: ** updateable in the ResultColumnList of the table being updated.
0349: ** only do this for FromBaseTables - if the result table is a
0350: ** CurrentOfNode, it already knows what columns in its cursor
0351: ** are updateable.
0352: */
0353: boolean allColumns = false;
0354: if (targetTable instanceof FromBaseTable) {
0355: ((FromBaseTable) targetTable).markUpdated(resultSet
0356: .getResultColumns());
0357: } else if (targetTable instanceof FromVTI) {
0358: resultColumnList = resultSet.getResultColumns();
0359: } else {
0360: /*
0361: ** Positioned update: WHERE CURRENT OF
0362: */
0363: if (SanityManager.DEBUG) {
0364: SanityManager.ASSERT(currentOfNode != null,
0365: "currentOfNode is null");
0366: }
0367:
0368: ExecPreparedStatement cursorStmt = currentOfNode
0369: .getCursorStatement();
0370: String[] ucl = cursorStmt.getUpdateColumns();
0371:
0372: /*
0373: ** If there is no update column list, we need to build
0374: ** out the result column list to have all columns.
0375: */
0376: if (ucl == null || (ucl.length == 0)) {
0377: /*
0378: ** Get the resultColumnList representing ALL of the columns in the
0379: ** base table. This is the "before" portion of the result row.
0380: */
0381: getResultColumnList();
0382:
0383: /*
0384: ** Add the "after" portion of the result row. This is the update
0385: ** list augmented to include every column in the target table.
0386: ** Those columns that are not being updated are set to themselves.
0387: ** The expanded list will be in the order of the columns in the base
0388: ** table.
0389: */
0390: afterColumns = resultSet.getResultColumns()
0391: .expandToAll(targetTableDescriptor,
0392: targetTable.getTableName());
0393:
0394: /*
0395: ** Need to get all indexes here since we aren't calling
0396: ** getReadMap().
0397: */
0398: getAffectedIndexes(targetTableDescriptor,
0399: (ResultColumnList) null,
0400: (FormatableBitSet) null);
0401: allColumns = true;
0402: } else {
0403: /* Check the updatability */
0404: resultSet.getResultColumns().checkColumnUpdateability(
0405: ucl, currentOfNode.getCursorName());
0406: }
0407: }
0408:
0409: changedColumnIds = getChangedColumnIds(resultSet
0410: .getResultColumns());
0411:
0412: /*
0413: ** We need to add in all the columns that are needed
0414: ** by the constraints on this table.
0415: */
0416: if (!allColumns && targetVTI == null) {
0417: getCompilerContext().pushCurrentPrivType(
0418: Authorizer.NULL_PRIV);
0419: try {
0420: readColsBitSet = new FormatableBitSet();
0421: FromBaseTable fbt = getResultColumnList(resultSet
0422: .getResultColumns());
0423: afterColumns = resultSet.getResultColumns()
0424: .copyListAndObjects();
0425:
0426: readColsBitSet = getReadMap(dataDictionary,
0427: targetTableDescriptor, afterColumns);
0428:
0429: afterColumns = fbt.addColsToList(afterColumns,
0430: readColsBitSet);
0431: resultColumnList = fbt.addColsToList(resultColumnList,
0432: readColsBitSet);
0433:
0434: /*
0435: ** If all bits are set, then behave as if we chose all
0436: ** in the first place
0437: */
0438: int i = 1;
0439: int size = targetTableDescriptor.getMaxColumnID();
0440: for (; i <= size; i++) {
0441: if (!readColsBitSet.get(i)) {
0442: break;
0443: }
0444: }
0445:
0446: if (i > size) {
0447: readColsBitSet = null;
0448: allColumns = true;
0449: }
0450: } finally {
0451: getCompilerContext().popCurrentPrivType();
0452: }
0453: }
0454:
0455: if (targetVTI == null) {
0456: /*
0457: ** Construct an empty heap row for use in our constant action.
0458: */
0459: emptyHeapRow = targetTableDescriptor
0460: .getEmptyExecRow(getContextManager());
0461:
0462: /* Append the list of "after" columns to the list of "before" columns,
0463: * preserving the afterColumns list. (Necessary for binding
0464: * check constraints.)
0465: */
0466: resultColumnList.appendResultColumns(afterColumns, false);
0467:
0468: /* Generate the RowLocation column */
0469: rowLocationNode = (CurrentRowLocationNode) getNodeFactory()
0470: .getNode(C_NodeTypes.CURRENT_ROW_LOCATION_NODE,
0471: getContextManager());
0472: } else {
0473: rowLocationNode = (NumericConstantNode) getNodeFactory()
0474: .getNode(C_NodeTypes.INT_CONSTANT_NODE,
0475: ReuseFactory.getInteger(0),
0476: getContextManager());
0477: }
0478:
0479: rowLocationColumn = (ResultColumn) getNodeFactory().getNode(
0480: C_NodeTypes.RESULT_COLUMN, COLUMNNAME, rowLocationNode,
0481: getContextManager());
0482: rowLocationColumn.markGenerated();
0483:
0484: /* Append to the ResultColumnList */
0485: resultColumnList.addResultColumn(rowLocationColumn);
0486:
0487: /*
0488: * The last thing that we do to the generated RCL is to clear
0489: * the table name out from each RC. See comment on
0490: * checkTableNameAndScrubResultColumns().
0491: */
0492: checkTableNameAndScrubResultColumns(resultColumnList);
0493:
0494: /* Set the new result column list in the result set */
0495: resultSet.setResultColumns(resultColumnList);
0496:
0497: /* Bind the expressions */
0498: getCompilerContext().pushCurrentPrivType(getPrivType()); // Update privilege
0499: super .bindExpressions();
0500: getCompilerContext().popCurrentPrivType();
0501:
0502: /* Bind untyped nulls directly under the result columns */
0503: resultSet.getResultColumns().bindUntypedNullsToResultColumns(
0504: resultColumnList);
0505:
0506: if (null != rowLocationColumn) {
0507: /* Bind the new ResultColumn */
0508: rowLocationColumn.bindResultColumnToExpression();
0509: }
0510:
0511: resultColumnList.checkStorableExpressions();
0512:
0513: /* Insert a NormalizeResultSetNode above the source if the source
0514: * and target column types and lengths do not match.
0515: */
0516: if (!resultColumnList.columnTypesAndLengthsMatch()) {
0517: resultSet = resultSet.genNormalizeResultSetNode(resultSet,
0518: true);
0519: resultColumnList.copyTypesAndLengthsToSource(resultSet
0520: .getResultColumns());
0521:
0522: if (hasCheckConstraints(dataDictionary,
0523: targetTableDescriptor)) {
0524: /* Get and bind all check constraints on the columns
0525: * being updated. We want to bind the check constraints against
0526: * the after columns. We need to bind against the portion of the
0527: * resultColumns in the new NormalizeResultSet that point to
0528: * afterColumns. Create an RCL composed of just those RCs in
0529: * order to bind the check constraints.
0530: */
0531: int afterColumnsSize = afterColumns.size();
0532: afterColumns = (ResultColumnList) getNodeFactory()
0533: .getNode(C_NodeTypes.RESULT_COLUMN_LIST,
0534: getContextManager());
0535: ResultColumnList normalizedRCs = resultSet
0536: .getResultColumns();
0537: for (int index = 0; index < afterColumnsSize; index++) {
0538: afterColumns.addElement(normalizedRCs
0539: .elementAt(index + afterColumnsSize));
0540: }
0541: }
0542: }
0543:
0544: if (null != targetVTI) {
0545: deferred = VTIDeferModPolicy.deferIt(
0546: DeferModification.UPDATE_STATEMENT, targetVTI,
0547: resultColumnList.getColumnNames(), sel
0548: .getWhereClause());
0549: } else // not VTI
0550: {
0551: /* we always include triggers in core language */
0552: boolean hasTriggers = (getAllRelevantTriggers(
0553: dataDictionary, targetTableDescriptor,
0554: changedColumnIds, true).size() > 0);
0555:
0556: /* Get and bind all constraints on the columns being updated */
0557: checkConstraints = bindConstraints(dataDictionary,
0558: getNodeFactory(), targetTableDescriptor, null,
0559: hasTriggers ? resultColumnList : afterColumns,
0560: changedColumnIds, readColsBitSet, false, true); /* we always include triggers in core language */
0561:
0562: /* If the target table is also a source table, then
0563: * the update will have to be in deferred mode
0564: * For updates, this means that the target table appears in a
0565: * subquery. Also, self referencing foreign keys are
0566: * deferred. And triggers cause an update to be deferred.
0567: */
0568: if (resultSet.subqueryReferencesTarget(
0569: targetTableDescriptor.getName(), true)
0570: || requiresDeferredProcessing()) {
0571: deferred = true;
0572: }
0573: }
0574:
0575: getCompilerContext().popCurrentPrivType();
0576:
0577: return this ;
0578: } // end of bind()
0579:
0580: int getPrivType() {
0581: return Authorizer.UPDATE_PRIV;
0582: }
0583:
0584: /**
0585: * Return true if the node references SESSION schema tables (temporary or permanent)
0586: *
0587: * @return true if references SESSION schema tables, else false
0588: *
0589: * @exception StandardException Thrown on error
0590: */
0591: public boolean referencesSessionSchema() throws StandardException {
0592: //If this node references a SESSION schema table, then return true.
0593: return (resultSet.referencesSessionSchema());
0594:
0595: }
0596:
0597: /**
0598: * Compile constants that Execution will use
0599: *
0600: * @exception StandardException Thrown on failure
0601: */
0602: public ConstantAction makeConstantAction() throws StandardException {
0603: /*
0604: ** Updates are also deferred if they update a column in the index
0605: ** used to scan the table being updated.
0606: */
0607: if (!deferred) {
0608: ConglomerateDescriptor updateCD = targetTable
0609: .getTrulyTheBestAccessPath()
0610: .getConglomerateDescriptor();
0611:
0612: if (updateCD != null && updateCD.isIndex()) {
0613: int[] baseColumns = updateCD.getIndexDescriptor()
0614: .baseColumnPositions();
0615:
0616: if (resultSet.getResultColumns().updateOverlaps(
0617: baseColumns)) {
0618: deferred = true;
0619: }
0620: }
0621: }
0622:
0623: if (null == targetTableDescriptor) {
0624: /* Return constant action for VTI
0625: * NOTE: ConstantAction responsible for preserving instantiated
0626: * VTIs for in-memory queries and for only preserving VTIs
0627: * that implement Serializable for SPSs.
0628: */
0629: return getGenericConstantActionFactory()
0630: .getUpdatableVTIConstantAction(
0631: DeferModification.UPDATE_STATEMENT,
0632: deferred, changedColumnIds);
0633: }
0634:
0635: int lockMode = resultSet.updateTargetLockMode();
0636: long heapConglomId = targetTableDescriptor
0637: .getHeapConglomerateId();
0638: TransactionController tc = getLanguageConnectionContext()
0639: .getTransactionCompile();
0640: StaticCompiledOpenConglomInfo[] indexSCOCIs = new StaticCompiledOpenConglomInfo[indexConglomerateNumbers.length];
0641:
0642: for (int index = 0; index < indexSCOCIs.length; index++) {
0643: indexSCOCIs[index] = tc
0644: .getStaticCompiledConglomInfo(indexConglomerateNumbers[index]);
0645: }
0646:
0647: /*
0648: ** Do table locking if the table's lock granularity is
0649: ** set to table.
0650: */
0651: if (targetTableDescriptor.getLockGranularity() == TableDescriptor.TABLE_LOCK_GRANULARITY) {
0652: lockMode = TransactionController.MODE_TABLE;
0653: }
0654:
0655: return getGenericConstantActionFactory()
0656: .getUpdateConstantAction(
0657: heapConglomId,
0658: targetTableDescriptor.getTableType(),
0659: tc.getStaticCompiledConglomInfo(heapConglomId),
0660: indicesToMaintain,
0661: indexConglomerateNumbers,
0662: indexSCOCIs,
0663: indexNames,
0664: emptyHeapRow,
0665: deferred,
0666: targetTableDescriptor.getUUID(),
0667: lockMode,
0668: false,
0669: changedColumnIds,
0670: null,
0671: null,
0672: getFKInfo(),
0673: getTriggerInfo(),
0674: (readColsBitSet == null) ? (FormatableBitSet) null
0675: : new FormatableBitSet(readColsBitSet),
0676: getReadColMap(targetTableDescriptor
0677: .getNumberOfColumns(), readColsBitSet),
0678: resultColumnList
0679: .getStreamStorableColIds(targetTableDescriptor
0680: .getNumberOfColumns()),
0681: (readColsBitSet == null) ? targetTableDescriptor
0682: .getNumberOfColumns()
0683: : readColsBitSet.getNumBitsSet(),
0684: positionedUpdate, resultSet.isOneRowResultSet());
0685: }
0686:
0687: /**
0688: * Updates are deferred if they update a column in the index
0689: * used to scan the table being updated.
0690: */
0691: protected void setDeferredForUpdateOfIndexColumn() {
0692: /* Don't bother checking if we're already deferred */
0693: if (!deferred) {
0694: /* Get the conglomerate descriptor for the target table */
0695: ConglomerateDescriptor updateCD = targetTable
0696: .getTrulyTheBestAccessPath()
0697: .getConglomerateDescriptor();
0698:
0699: /* If it an index? */
0700: if (updateCD != null && updateCD.isIndex()) {
0701: int[] baseColumns = updateCD.getIndexDescriptor()
0702: .baseColumnPositions();
0703:
0704: /* Are any of the index columns updated? */
0705: if (resultSet.getResultColumns().updateOverlaps(
0706: baseColumns)) {
0707: deferred = true;
0708: }
0709: }
0710: }
0711: }
0712:
0713: /**
0714: * Code generation for update.
0715: * The generated code will contain:
0716: * o A static member for the (xxx)ResultSet with the RowLocations and
0717: * new update values
0718: * o The static member will be assigned the appropriate ResultSet within
0719: * the nested calls to get the ResultSets. (The appropriate cast to the
0720: * (xxx)ResultSet will be generated.)
0721: * o The CurrentRowLocation() in SelectNode's select list will generate
0722: * a new method for returning the RowLocation as well as a call to
0723: * that method when generating the (xxx)ResultSet.
0724: *
0725: * @param acb The ActivationClassBuilder for the class being built
0726: * @param mb The method for the execute() method to be built
0727: *
0728: *
0729: * @exception StandardException Thrown on error
0730: */
0731: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
0732: throws StandardException {
0733: //If the DML is on the temporary table, generate the code to mark temporary table as modified in the current UOW
0734: generateCodeForTemporaryTable(acb, mb);
0735:
0736: /* generate the parameters */
0737: if (!isDependentTable)
0738: generateParameterValueSet(acb);
0739:
0740: /* Create the static declaration for the scan ResultSet which generates the
0741: * RowLocations to be updated
0742: * RESOLVE - Need to deal with the type of the static member.
0743: */
0744: acb.newFieldDeclaration(Modifier.PRIVATE,
0745: ClassName.CursorResultSet, acb
0746: .newRowLocationScanResultSetName());
0747:
0748: /*
0749: ** Generate the update result set, giving it either the original
0750: ** source or the normalize result set, the constant action.
0751: */
0752:
0753: acb.pushGetResultSetFactoryExpression(mb);
0754: resultSet.generate(acb, mb); // arg 1
0755:
0756: if (null != targetVTI) {
0757: targetVTI
0758: .assignCostEstimate(resultSet.getNewCostEstimate());
0759: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
0760: "getUpdateVTIResultSet", ClassName.ResultSet, 1);
0761: } else {
0762: // generate code to evaluate CHECK CONSTRAINTS
0763: generateCheckConstraints(checkConstraints, acb, mb); // arg 2
0764:
0765: if (isDependentTable) {
0766: mb.push(acb.addItem(makeConstantAction()));
0767: mb.push(acb.addItem(makeResultDescription()));
0768: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
0769: "getDeleteCascadeUpdateResultSet",
0770: ClassName.ResultSet, 4);
0771: } else {
0772: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
0773: "getUpdateResultSet", ClassName.ResultSet, 2);
0774: }
0775: }
0776: }
0777:
0778: /**
0779: * Return the type of statement, something from
0780: * StatementType.
0781: *
0782: * @return the type of statement
0783: */
0784: protected final int getStatementType() {
0785: return StatementType.UPDATE;
0786: }
0787:
0788: /**
0789: * Gets the map of all columns which must be read out of the base table.
0790: * These are the columns needed to<UL>:
0791: * <LI>maintain indices</LI>
0792: * <LI>maintain foreign keys</LI>
0793: * <LI>support Replication's Delta Optimization</LI></UL>
0794: * <p>
0795: * The returned map is a FormatableBitSet with 1 bit for each column in the
0796: * table plus an extra, unsued 0-bit. If a 1-based column id must
0797: * be read from the base table, then the corresponding 1-based bit
0798: * is turned ON in the returned FormatableBitSet.
0799: * <p>
0800: * <B>NOTE</B>: this method is not expected to be called when
0801: * all columns are being updated (i.e. updateColumnList is null).
0802: *
0803: * @param dd the data dictionary to look in
0804: * @param baseTable the base table descriptor
0805: * @param updateColumnList the rcl for the update. CANNOT BE NULL
0806: *
0807: * @return a FormatableBitSet of columns to be read out of the base table
0808: *
0809: * @exception StandardException Thrown on error
0810: */
0811: public FormatableBitSet getReadMap(DataDictionary dd,
0812: TableDescriptor baseTable, ResultColumnList updateColumnList)
0813: throws StandardException {
0814: boolean[] needsDeferredProcessing = new boolean[1];
0815: needsDeferredProcessing[0] = requiresDeferredProcessing();
0816:
0817: Vector conglomVector = new Vector();
0818: relevantCdl = new ConstraintDescriptorList();
0819: relevantTriggers = new GenericDescriptorList();
0820:
0821: FormatableBitSet columnMap = UpdateNode.getUpdateReadMap(
0822: baseTable, updateColumnList, conglomVector,
0823: relevantCdl, relevantTriggers, needsDeferredProcessing);
0824:
0825: markAffectedIndexes(conglomVector);
0826:
0827: adjustDeferredFlag(needsDeferredProcessing[0]);
0828:
0829: return columnMap;
0830: }
0831:
0832: /**
0833: * Construct the changedColumnIds array. Note we sort its entries by
0834: * columnId.
0835: */
0836: private int[] getChangedColumnIds(ResultColumnList rcl) {
0837: if (rcl == null) {
0838: return (int[]) null;
0839: } else {
0840: return rcl.sortMe();
0841: }
0842: }
0843:
0844: /**
0845: * Builds a bitmap of all columns which should be read from the
0846: * Store in order to satisfy an UPDATE statement.
0847: *
0848: * Is passed a list of updated columns. Does the following:
0849: *
0850: * 1) finds all indices which overlap the updated columns
0851: * 2) adds the index columns to a bitmap of affected columns
0852: * 3) adds the index descriptors to a list of conglomerate
0853: * descriptors.
0854: * 4) finds all constraints which overlap the updated columns
0855: * and adds the constrained columns to the bitmap
0856: * 5) finds all triggers which overlap the updated columns.
0857: * 6) if there are any triggers, marks all columns in the bitmap
0858: * 7) adds the triggers to an evolving list of triggers
0859: *
0860: * @param updateColumnList a list of updated columns
0861: * @param conglomVector OUT: vector of affected indices
0862: * @param relevantConstraints IN/OUT. Empty list is passed in. We hang constraints on it as we go.
0863: * @param relevantTriggers IN/OUT. Passed in as an empty list. Filled in as we go.
0864: * @param needsDeferredProcessing IN/OUT. true if the statement already needs
0865: * deferred processing. set while evaluating this
0866: * routine if a trigger or constraint requires
0867: * deferred processing
0868: *
0869: * @return a FormatableBitSet of columns to be read out of the base table
0870: *
0871: * @exception StandardException Thrown on error
0872: */
0873: public static FormatableBitSet getUpdateReadMap(
0874: TableDescriptor baseTable,
0875: ResultColumnList updateColumnList, Vector conglomVector,
0876: ConstraintDescriptorList relevantConstraints,
0877: GenericDescriptorList relevantTriggers,
0878: boolean[] needsDeferredProcessing) throws StandardException {
0879: if (SanityManager.DEBUG) {
0880: SanityManager.ASSERT(updateColumnList != null,
0881: "updateColumnList is null");
0882: }
0883:
0884: int columnCount = baseTable.getMaxColumnID();
0885: FormatableBitSet columnMap = new FormatableBitSet(
0886: columnCount + 1);
0887:
0888: /*
0889: ** Add all the changed columns. We don't strictly
0890: ** need the before image of the changed column in all cases,
0891: ** but it makes life much easier since things are set
0892: ** up around the assumption that we have the before
0893: ** and after image of the column.
0894: */
0895: int[] changedColumnIds = updateColumnList.sortMe();
0896:
0897: for (int ix = 0; ix < changedColumnIds.length; ix++) {
0898: columnMap.set(changedColumnIds[ix]);
0899: }
0900:
0901: /*
0902: ** Get a list of the indexes that need to be
0903: ** updated. ColumnMap contains all indexed
0904: ** columns where 1 or more columns in the index
0905: ** are going to be modified.
0906: */
0907: DMLModStatementNode.getXAffectedIndexes(baseTable,
0908: updateColumnList, columnMap, conglomVector);
0909:
0910: /*
0911: ** Add all columns needed for constraints. We don't
0912: ** need to bother with foreign key/primary key constraints
0913: ** because they are added as a side effect of adding
0914: ** their indexes above.
0915: */
0916: baseTable.getAllRelevantConstraints(StatementType.UPDATE,
0917: false, changedColumnIds, needsDeferredProcessing,
0918: relevantConstraints);
0919:
0920: int rclSize = relevantConstraints.size();
0921: for (int index = 0; index < rclSize; index++) {
0922: ConstraintDescriptor cd = relevantConstraints
0923: .elementAt(index);
0924: if (cd.getConstraintType() != DataDictionary.CHECK_CONSTRAINT) {
0925: continue;
0926: }
0927:
0928: int[] refColumns = ((CheckConstraintDescriptor) cd)
0929: .getReferencedColumns();
0930: for (int i = 0; i < refColumns.length; i++) {
0931: columnMap.set(refColumns[i]);
0932: }
0933: }
0934:
0935: /*
0936: ** If we have any triggers, then get all the columns
0937: ** because we don't know what the user will ultimately
0938: ** reference.
0939: */
0940:
0941: baseTable.getAllRelevantTriggers(StatementType.UPDATE,
0942: changedColumnIds, relevantTriggers);
0943: if (relevantTriggers.size() > 0) {
0944: needsDeferredProcessing[0] = true;
0945: }
0946:
0947: if (relevantTriggers.size() > 0) {
0948: for (int i = 1; i <= columnCount; i++) {
0949: columnMap.set(i);
0950: }
0951: }
0952:
0953: return columnMap;
0954: }
0955:
0956: /*
0957: * Force correlated column references in the SET clause to have the
0958: * name of the base table. This dances around the problem alluded to
0959: * in scrubResultColumn().
0960: */
0961: private void normalizeCorrelatedColumns(ResultColumnList rcl,
0962: FromTable fromTable) throws StandardException {
0963: String correlationName = fromTable.getCorrelationName();
0964:
0965: if (correlationName == null) {
0966: return;
0967: }
0968:
0969: TableName tableNameNode;
0970:
0971: if (fromTable instanceof CurrentOfNode) {
0972: tableNameNode = ((CurrentOfNode) fromTable)
0973: .getBaseCursorTargetTableName();
0974: } else {
0975: tableNameNode = makeTableName(null, fromTable
0976: .getBaseTableName());
0977: }
0978:
0979: int count = rcl.size();
0980:
0981: for (int i = 0; i < count; i++) {
0982: ResultColumn column = (ResultColumn) rcl.elementAt(i);
0983: ColumnReference reference = column.getReference();
0984:
0985: if ((reference != null)
0986: && correlationName.equals(reference.getTableName())) {
0987: reference.setTableNameNode(tableNameNode);
0988: }
0989: }
0990:
0991: }
0992:
0993: /**
0994: * Check table name and then clear it from the result set columns.
0995: *
0996: * @exception StandardExcepion if invalid column/table is specified.
0997: */
0998: private void checkTableNameAndScrubResultColumns(
0999: ResultColumnList rcl) throws StandardException {
1000: int columnCount = rcl.size();
1001: int tableCount = ((SelectNode) resultSet).fromList.size();
1002:
1003: for (int i = 0; i < columnCount; i++) {
1004: boolean foundMatchingTable = false;
1005: ResultColumn column = (ResultColumn) rcl.elementAt(i);
1006:
1007: if (column.getTableName() != null) {
1008: for (int j = 0; j < tableCount; j++) {
1009: FromTable fromTable = (FromTable) ((SelectNode) resultSet).fromList
1010: .elementAt(j);
1011: final String tableName;
1012: if (fromTable instanceof CurrentOfNode) {
1013: tableName = ((CurrentOfNode) fromTable)
1014: .getBaseCursorTargetTableName()
1015: .getTableName();
1016: } else {
1017: tableName = fromTable.getBaseTableName();
1018: }
1019:
1020: if (column.getTableName().equals(tableName)) {
1021: foundMatchingTable = true;
1022: break;
1023: }
1024: }
1025:
1026: if (!foundMatchingTable) {
1027: throw StandardException.newException(
1028: SQLState.LANG_COLUMN_NOT_FOUND, column
1029: .getTableName()
1030: + "." + column.getName());
1031: }
1032: }
1033:
1034: /* The table name is
1035: * unnecessary for an update. More importantly, though, it
1036: * creates a problem in the degenerate case with a positioned
1037: * update. The user must specify the base table name for a
1038: * positioned update. If a correlation name was specified for
1039: * the cursor, then a match for the ColumnReference would not
1040: * be found if we didn't null out the name. (Aren't you
1041: * glad you asked?)
1042: */
1043: column.clearTableName();
1044: }
1045: }
1046:
1047: /**
1048: * Normalize synonym column references to have the name of the base table.
1049: *
1050: * @param rcl The result column list of the target table
1051: * @param fromTable The table name to set the column refs to
1052: *
1053: * @exception StandardException Thrown on error
1054: */
1055: private void normalizeSynonymColumns(ResultColumnList rcl,
1056: FromTable fromTable) throws StandardException {
1057: if (fromTable.getCorrelationName() != null) {
1058: return;
1059: }
1060:
1061: TableName tableNameNode;
1062: if (fromTable instanceof CurrentOfNode) {
1063: tableNameNode = ((CurrentOfNode) fromTable)
1064: .getBaseCursorTargetTableName();
1065: } else {
1066: tableNameNode = makeTableName(null, fromTable
1067: .getBaseTableName());
1068: }
1069:
1070: super .normalizeSynonymColumns(rcl, tableNameNode);
1071: }
1072:
1073: } // end of UpdateNode
|