📄 createtriggernode.java
字号:
/* Derby - Class org.apache.derby.impl.sql.compile.CreateTriggerNode 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.compile;import org.apache.derby.iapi.services.context.ContextManager;import org.apache.derby.iapi.services.monitor.Monitor;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.sql.compile.CompilerContext;import org.apache.derby.iapi.sql.compile.Parser;import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;import org.apache.derby.iapi.sql.dictionary.DataDictionary;import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;import org.apache.derby.iapi.sql.dictionary.TableDescriptor;import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;import org.apache.derby.iapi.sql.depend.Dependent;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.sql.execute.ConstantAction;import org.apache.derby.iapi.sql.ResultSet;import org.apache.derby.iapi.types.DataTypeDescriptor;import org.apache.derby.iapi.types.TypeId;import org.apache.derby.catalog.UUID;import java.sql.Timestamp;import java.sql.Types;import java.util.Enumeration;import java.util.Hashtable;import java.util.Vector;/** * A CreateTriggerNode is the root of a QueryTree * that represents a CREATE TRIGGER * statement. * * @author jamie */public class CreateTriggerNode extends CreateStatementNode{ private TableName triggerName; private TableName tableName; private int triggerEventMask; private ResultColumnList triggerCols; private boolean isBefore; private boolean isRow; private boolean isEnabled; private Vector refClause; private QueryTreeNode whenClause; private String whenText; private int whenOffset; private StatementNode actionNode; private String actionText; private String originalActionText; // text w/o trim of spaces private int actionOffset; private SchemaDescriptor triggerSchemaDescriptor; private SchemaDescriptor compSchemaDescriptor; private int[] referencedColInts; private TableDescriptor triggerTableDescriptor; private UUID actionCompSchemaId; /* ** Names of old and new table. By default we have ** OLD/old and NEW/new. The casing is dependent on ** the language connection context casing as the rest ** of other code. Therefore we will set the value of the ** String at the init() time. ** However, if there is a referencing clause ** we will reset these values to be whatever the user ** wants. */ private String oldTableName; private String newTableName; private boolean oldTableInReferencingClause; private boolean newTableInReferencingClause; /** * Initializer for a CreateTriggerNode * * @param triggerName name of the trigger * @param tableName name of the table which the trigger is declared upon * @param triggerEventMask TriggerDescriptor.TRIGGER_EVENT_XXX * @param triggerCols columns trigger is to fire upon. Valid * for UPDATE case only. * @param isBefore is before trigger (false for after) * @param isRow true for row trigger, false for statement * @param isEnabled true if enabled * @param refClause the referencing clause * @param whenClause the WHEN clause tree * @param whenText the text of the WHEN clause * @param whenOffset offset of start of WHEN clause * @param actionNode the trigger action tree * @param actionText the text of the trigger action * @param actionOffset offset of start of action clause * * @return A CreateTriggerNode * * @exception StandardException Thrown on error */ public void init ( Object triggerName, Object tableName, Object triggerEventMask, Object triggerCols, Object isBefore, Object isRow, Object isEnabled, Object refClause, Object whenClause, Object whenText, Object whenOffset, Object actionNode, Object actionText, Object actionOffset ) throws StandardException { initAndCheck(triggerName); this.triggerName = (TableName) triggerName; this.tableName = (TableName) tableName; this.triggerEventMask = ((Integer) triggerEventMask).intValue(); this.triggerCols = (ResultColumnList) triggerCols; this.isBefore = ((Boolean) isBefore).booleanValue(); this.isRow = ((Boolean) isRow).booleanValue(); this.isEnabled = ((Boolean) isEnabled).booleanValue(); this.refClause = (Vector) refClause; this.whenClause = (QueryTreeNode) whenClause; this.whenText = (whenText == null) ? null : ((String) whenText).trim(); this.whenOffset = ((Integer) whenOffset).intValue(); this.actionNode = (StatementNode) actionNode; this.originalActionText = (String) actionText; this.actionText = (actionText == null) ? null : ((String) actionText).trim(); this.actionOffset = ((Integer) actionOffset).intValue(); implicitCreateSchema = true; } public String statementToString() { return "CREATE TRIGGER"; } /** * Prints the sub-nodes of this object. See QueryTreeNode.java for * how tree printing is supposed to work. * * @param depth The depth of this node in the tree * * @return Nothing */ public void printSubNodes(int depth) { if (SanityManager.DEBUG) { super.printSubNodes(depth); if (triggerCols != null) { printLabel(depth, "triggerColumns: "); triggerCols.treePrint(depth + 1); } if (whenClause != null) { printLabel(depth, "whenClause: "); whenClause.treePrint(depth + 1); } if (actionNode != null) { printLabel(depth, "actionNode: "); actionNode.treePrint(depth + 1); } } } // accessors // We inherit the generate() method from DDLStatementNode. /** * Bind this CreateTriggerNode. This means doing any static error * checking that can be done before actually creating the table. * * @return The bound query tree * * @exception StandardException Thrown on error */ public QueryTreeNode bind() throws StandardException { CompilerContext compilerContext = getCompilerContext(); DataDictionary dd = getDataDictionary(); /* ** Grab the current schema. We will use that for ** sps compilation */ LanguageConnectionContext lcc = getLanguageConnectionContext(); compSchemaDescriptor = lcc.getDefaultSchema(); /* ** Get and check the schema descriptor for this ** trigger. This check will throw the proper exception ** if someone tries to create a trigger in the SYS ** schema. */ triggerSchemaDescriptor = getSchemaDescriptor(); /* ** Get the trigger table. */ triggerTableDescriptor = getTableDescriptor(tableName); //throw an exception if user is attempting to create a trigger on a temporary table if (isSessionSchema(triggerTableDescriptor.getSchemaDescriptor())) { throw StandardException.newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES); } /* ** Regenerates the actionText and actionNode if necessary. */ boolean needInternalSQL = bindReferencesClause(dd); lcc.pushTriggerTable(triggerTableDescriptor); try { /* ** Bind the trigger action and the trigger ** when clause to make sure that they are ** ok. Note that we have already substituted ** in various replacements for OLD/NEW transition ** tables/variables and reparsed if necessary. */ if (needInternalSQL) compilerContext.setReliability(CompilerContext.INTERNAL_SQL_LEGAL); actionNode.bind(); if (whenClause != null) { whenClause.bind(); } } finally { lcc.popTriggerTable(triggerTableDescriptor); } /* ** Statement is dependent on the TableDescriptor */ compilerContext.createDependency(triggerTableDescriptor); /* ** If there is a list of columns, then no duplicate columns, ** and all columns must be found. */ if (triggerCols != null && triggerCols.size() != 0) { referencedColInts = new int[triggerCols.size()]; Hashtable columnNames = new Hashtable(); int tcSize = triggerCols.size(); for (int i = 0; i < tcSize; i++) { ResultColumn rc = (ResultColumn) triggerCols.elementAt(i); if (columnNames.put(rc.getName(), rc) != null) { throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_IN_TRIGGER_UPDATE, rc.getName(), triggerName); } ColumnDescriptor cd = triggerTableDescriptor.getColumnDescriptor(rc.getName()); if (cd == null) { throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, rc.getName(), tableName); } referencedColInts[i] = cd.getPosition(); } // sort the list java.util.Arrays.sort(referencedColInts); } //If attempting to reference a SESSION schema table (temporary or permanent) in the trigger action, throw an exception if (actionNode.referencesSessionSchema()) throw StandardException.newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES); return this; } /** * Return true if the node references SESSION schema tables (temporary or permanent) * * @return true if references SESSION schema tables, else false * * @exception StandardException Thrown on error */ public boolean referencesSessionSchema() throws StandardException { //If create trigger is part of create statement and the trigger is defined on or it references SESSION schema tables, //it will get caught in the bind phase of trigger and exception will be thrown by the trigger bind. return (isSessionSchema(triggerTableDescriptor.getSchemaName()) || actionNode.referencesSessionSchema()); } /* ** BIND OLD/NEW TRANSITION TABLES/VARIABLES ** ** 1) validate the referencing clause (if any) ** ** 2) convert trigger action text. e.g. ** DELETE FROM t WHERE c = old.c ** turns into ** DELETE FROM t WHERE c = org.apache.derby.iapi.db.Factory:: ** getTriggerExecutionContext().getOldRow().getInt('C'); ** or ** DELETE FROM t WHERE c in (SELECT c FROM OLD) ** turns into ** DELETE FROM t WHERE c in (SELECT c FROM new TriggerOldTransitionTable OLD) ** ** 3) check all column references against new/old transition ** variables (since they are no longer 'normal' column references ** that will be checked during bind) ** ** 4) reparse the new action text ** ** You might be wondering why we regenerate the text and reparse ** instead of just reworking the tree to have the nodes we want. ** Well, the primary reason is that if we screwed with the tree, ** then we would have a major headache if this trigger action ** was ever recompiled -- spses don't really know that they are ** triggers so it would be quite arduous to figure out that an ** sps is a trigger and munge up its query tree after figuring ** out what its OLD/NEW tables are, etc. Also, it is just plain ** easier to just generate the sql and rebind. */ private boolean bindReferencesClause(DataDictionary dd) throws StandardException { validateReferencesClause(dd); StringBuffer newText = new StringBuffer(); boolean regenNode = false; int start = 0; if (isRow) { /* ** For a row trigger, we find all column references. If ** they are referencing NEW or OLD we turn them into ** getTriggerExecutionContext().getOldRow().getInt('C'); */ CollectNodesVisitor visitor = new CollectNodesVisitor(ColumnReference.class); actionNode.accept(visitor); Vector refs = visitor.getList(); /* we need to sort on position in string, beetle 4324 */ QueryTreeNode[] cols = sortRefs(refs, true); for (int i = 0; i < cols.length; i++) { ColumnReference ref = (ColumnReference) cols[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -