📄 internaltriggerexecutioncontext.java
字号:
/* Derby - Class org.apache.derby.impl.sql.execute.InternalTriggerExecutionContext Copyright 1999, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.sql.execute;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.error.PublicAPI;import org.apache.derby.iapi.db.TriggerExecutionContext;import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;import org.apache.derby.iapi.sql.ResultSet;import org.apache.derby.iapi.sql.execute.CursorResultSet;import org.apache.derby.iapi.sql.execute.ExecRow;import org.apache.derby.iapi.types.DataValueDescriptor;import org.apache.derby.iapi.sql.execute.ExecutionStmtValidator;import org.apache.derby.iapi.sql.execute.ConstantAction;import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.services.i18n.MessageService;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.jdbc.ConnectionContext; import org.apache.derby.catalog.UUID;import java.util.Enumeration;import java.util.Vector;import java.util.Hashtable;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import org.apache.derby.iapi.error.ExceptionSeverity;/** * There is one of these beasts per INSERT/DELETE/UPDATE * statement. It fulfills the contract for the externally * visible trigger execution context and it validates * that a statement that is about to be executed doesn't * violate the restrictions placed upon what can be executed * from a trigger. * <p> * Note that it is crucial that cleanup() is called once * the DML has completed, cleanup() makes sure that users * can't do something invalid on a tec reference that they * were holding from when the trigger fired. * */public class InternalTriggerExecutionContext implements TriggerExecutionContext, ExecutionStmtValidator{ /* ** Immutable */ protected int[] changedColIds; protected String[] changedColNames; protected int dmlType; protected String statementText; protected ConnectionContext cc; protected UUID targetTableId; protected String targetTableName; protected LanguageConnectionContext lcc; /* ** Mutable */ protected CursorResultSet beforeResultSet; protected CursorResultSet afterResultSet; /** * used exclusively for InsertResultSets which have autoincrement * columns. */ protected ExecRow afterRow; protected boolean cleanupCalled; protected TriggerEvent event; protected TriggerDescriptor triggerd; /* ** Used to track all the result sets we have given out to ** users. When the trigger context is no longer valid, ** we close all the result sets that may be in the user ** space because they can no longer provide meaningful ** results. */ private Vector resultSetVector; /** * aiCounters is a vector of AutoincrementCounters used to keep state which * might be used by the trigger. This is only used by Insert triggers-- * Delete and Update triggers do not use this variable. * * @see AutoincrementCounter * */ private Vector aiCounters; /** * aiHT is a hashtable of autincrement <key, value> pairs. This is used for * ai values generated by the trigger. */ private Hashtable aiHT; /** * Build me a big old nasty trigger execution context. * Damnit. * <p> * About the only thing of real interest to outside observers * is that it pushes itself as the trigger execution context * in the lcc. Be sure to call <i>cleanup()</i> when you * are done, or you will be flogged like the reprobate that * you are. * * @param lcc the lcc * @param jdbcFactory the jdbc factory * @param statementText the text of the statement that caused the * trigger to fire. may be null if we are replicating * @param changedColIds the list of columns that changed. Null * for all columns or INSERT/DELETE. * @param changedColNames the names that correspond to changedColIds * @param targetTableId the UUID of the table upon which the trigger * fired * @param targetTableName the name of the table upon which the trigger * fired * @param aiCounters A vector of AutoincrementCounters to keep state * of the ai columns in this insert trigger.a * * @exception StandardException on error */ public InternalTriggerExecutionContext ( LanguageConnectionContext lcc, ConnectionContext cc, String statementText, int dmlType, int[] changedColIds, String[] changedColNames, UUID targetTableId, String targetTableName, Vector aiCounters ) throws StandardException { this.dmlType = dmlType; this.changedColIds = changedColIds; this.changedColNames = changedColNames; this.statementText = statementText; this.cc = cc; this.lcc = lcc; this.targetTableId = targetTableId; this.targetTableName = targetTableName; this.resultSetVector = new Vector(); this.aiCounters = aiCounters; if (SanityManager.DEBUG) { if ((changedColIds == null) != (changedColNames == null)) { SanityManager.THROWASSERT("bad changed cols, "+ "(changedColsIds == null) = "+(changedColIds == null)+ " (changedColsNames == null) = "+(changedColNames == null)); } if (changedColIds != null) { SanityManager.ASSERT(changedColIds.length == changedColNames.length, "different number of changed col ids vs names"); } } lcc.pushTriggerExecutionContext(this); } void setBeforeResultSet(CursorResultSet rs) { beforeResultSet = rs; } void setAfterResultSet(CursorResultSet rs) throws StandardException { afterResultSet = rs; if (aiCounters != null) { if (triggerd.isRowTrigger()) { // An after row trigger needs to see the "first" row inserted rs.open(); afterRow = rs.getNextRow(); rs.close(); } else { // after statement trigger needs to look at the last value. if (!triggerd.isBeforeTrigger()) resetAICounters(false); } } } void setCurrentTriggerEvent(TriggerEvent event) { this.event = event; } void clearCurrentTriggerEvent() { event = null; } void setTrigger(TriggerDescriptor triggerd) { this.triggerd = triggerd; } void clearTrigger() throws StandardException { event = null; triggerd = null; if (afterResultSet != null) { afterResultSet.close(); afterResultSet = null; } if (beforeResultSet != null) { beforeResultSet.close(); beforeResultSet = null; } } /** * Cleanup the trigger execution context. <B>MUST</B> * be called when the caller is done with the trigger * execution context. * <p> * We go to somewhat exaggerated lengths to free up * all our resources here because a user may hold on * to a TEC after it is valid, so we clean everything * up to be on the safe side. * * @exception StandardException on unexpected error */ protected void cleanup() throws StandardException { lcc.popTriggerExecutionContext(this); /* ** Explicitly close all result sets that we have ** given out to the user. */ for (Enumeration e = resultSetVector.elements(); e.hasMoreElements(); ) { java.sql.ResultSet rs = (java.sql.ResultSet)e.nextElement(); try { rs.close(); } catch (SQLException se) {} } resultSetVector = null; /* ** We should have already closed our underlying ** ExecResultSets by closing the jdbc result sets, ** but in case we got an error that we caught and ** ignored, explicitly close them. */ if (afterResultSet != null) { afterResultSet.close(); afterResultSet = null; } if (beforeResultSet != null) { beforeResultSet.close(); beforeResultSet = null; } lcc = null; cleanupCalled = true; } /** * Make sure that the user isn't trying to get a result * set after we have cleaned up. */ private void ensureProperContext() throws SQLException { if (cleanupCalled) { throw new SQLException( MessageService.getTextMessage( SQLState.LANG_STATEMENT_CLOSED_NO_REASON), "XCL31", ExceptionSeverity.STATEMENT_SEVERITY ); } } ///////////////////////////////////////////////////////// // // ExecutionStmtValidator // ///////////////////////////////////////////////////////// /** * Make sure that whatever statement is about to be executed * is ok from the context of this trigger. * <p> * Note that we are sub classed in replication for checks * for replication specific language. * * @param constantAction the constant action of the action * that we are to validate * * @exception StandardException on error */ public void validateStatement(ConstantAction constantAction) throws StandardException { if (SanityManager.DEBUG) { if (constantAction instanceof DDLConstantAction) SanityManager.THROWASSERT("DDL NOT SUPPORTED IN TRIGGER"); } /* ** No INSERT/UPDATE/DELETE on trigger table ** for a before trigger. */ else if (triggerd.isBeforeTrigger() && constantAction instanceof WriteCursorConstantAction) { if (constantAction.modifiesTableId(targetTableId)) { throw StandardException.newException(SQLState.LANG_NO_DML_IN_TRIGGER, triggerd.getName(), targetTableName); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -