Source Code Cross Referenced for CreateTriggerNode.java in  » Database-DBMS » db-derby-10.2 » org » apache » derby » impl » sql » compile » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » db derby 10.2 » org.apache.derby.impl.sql.compile 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:
003:           Derby - Class org.apache.derby.impl.sql.compile.CreateTriggerNode
004:
005:           Licensed to the Apache Software Foundation (ASF) under one or more
006:           contributor license agreements.  See the NOTICE file distributed with
007:           this work for additional information regarding copyright ownership.
008:           The ASF licenses this file to you under the Apache License, Version 2.0
009:           (the "License"); you may not use this file except in compliance with
010:           the License.  You may obtain a copy of the License at
011:
012:              http://www.apache.org/licenses/LICENSE-2.0
013:
014:           Unless required by applicable law or agreed to in writing, software
015:           distributed under the License is distributed on an "AS IS" BASIS,
016:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017:           See the License for the specific language governing permissions and
018:           limitations under the License.
019:
020:         */
021:
022:        package org.apache.derby.impl.sql.compile;
023:
024:        import org.apache.derby.iapi.services.context.ContextManager;
025:        import org.apache.derby.iapi.services.monitor.Monitor;
026:        import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028:        import org.apache.derby.iapi.error.StandardException;
029:
030:        import org.apache.derby.iapi.sql.compile.CompilerContext;
031:        import org.apache.derby.iapi.sql.compile.Parser;
032:
033:        import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
034:        import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
035:        import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
036:        import org.apache.derby.iapi.sql.dictionary.DataDictionary;
037:        import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
038:        import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
039:        import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
040:        import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
041:
042:        import org.apache.derby.iapi.sql.conn.Authorizer;
043:        import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
044:
045:        import org.apache.derby.iapi.sql.depend.Dependent;
046:
047:        import org.apache.derby.iapi.reference.SQLState;
048:
049:        import org.apache.derby.iapi.sql.execute.ConstantAction;
050:
051:        import org.apache.derby.iapi.sql.ResultSet;
052:
053:        import org.apache.derby.iapi.types.DataTypeDescriptor;
054:        import org.apache.derby.iapi.types.TypeId;
055:
056:        import org.apache.derby.catalog.UUID;
057:        import java.sql.Timestamp;
058:        import java.sql.Types;
059:        import java.util.Enumeration;
060:        import java.util.Hashtable;
061:        import java.util.Vector;
062:
063:        /**
064:         * A CreateTriggerNode is the root of a QueryTree 
065:         * that represents a CREATE TRIGGER
066:         * statement.
067:         *
068:         * @author jamie
069:         */
070:
071:        public class CreateTriggerNode extends DDLStatementNode {
072:            private TableName triggerName;
073:            private TableName tableName;
074:            private int triggerEventMask;
075:            private ResultColumnList triggerCols;
076:            private boolean isBefore;
077:            private boolean isRow;
078:            private boolean isEnabled;
079:            private Vector refClause;
080:            private QueryTreeNode whenClause;
081:            private String whenText;
082:            private int whenOffset;
083:            private StatementNode actionNode;
084:            private String actionText;
085:            private String originalActionText; // text w/o trim of spaces
086:            private int actionOffset;
087:
088:            private SchemaDescriptor triggerSchemaDescriptor;
089:            private SchemaDescriptor compSchemaDescriptor;
090:            private int[] referencedColInts;
091:            private TableDescriptor triggerTableDescriptor;
092:            private UUID actionCompSchemaId;
093:
094:            /*
095:             ** Names of old and new table.  By default we have
096:             ** OLD/old and NEW/new.  The casing is dependent on 
097:             ** the language connection context casing as the rest
098:             ** of other code. Therefore we will set the value of the 
099:             ** String at the init() time.
100:             ** However, if there is a referencing clause
101:             ** we will reset these values to be whatever the user
102:             ** wants.
103:             */
104:            private String oldTableName;
105:            private String newTableName;
106:
107:            private boolean oldTableInReferencingClause;
108:            private boolean newTableInReferencingClause;
109:
110:            /**
111:             * Initializer for a CreateTriggerNode
112:             *
113:             * @param triggerName			name of the trigger	
114:             * @param tableName				name of the table which the trigger is declared upon	
115:             * @param triggerEventMask		TriggerDescriptor.TRIGGER_EVENT_XXX
116:             * @param triggerCols			columns trigger is to fire upon.  Valid
117:             *								for UPDATE case only.
118:             * @param isBefore				is before trigger (false for after)
119:             * @param isRow					true for row trigger, false for statement
120:             * @param isEnabled				true if enabled
121:             * @param refClause				the referencing clause
122:             * @param whenClause			the WHEN clause tree
123:             * @param whenText				the text of the WHEN clause
124:             * @param whenOffset			offset of start of WHEN clause
125:             * @param actionNode			the trigger action tree
126:             * @param actionText			the text of the trigger action
127:             * @param actionOffset			offset of start of action clause
128:             *
129:             * @exception StandardException		Thrown on error
130:             */
131:            public void init(Object triggerName, Object tableName,
132:                    Object triggerEventMask, Object triggerCols,
133:                    Object isBefore, Object isRow, Object isEnabled,
134:                    Object refClause, Object whenClause, Object whenText,
135:                    Object whenOffset, Object actionNode, Object actionText,
136:                    Object actionOffset) throws StandardException {
137:                initAndCheck(triggerName);
138:                this .triggerName = (TableName) triggerName;
139:                this .tableName = (TableName) tableName;
140:                this .triggerEventMask = ((Integer) triggerEventMask).intValue();
141:                this .triggerCols = (ResultColumnList) triggerCols;
142:                this .isBefore = ((Boolean) isBefore).booleanValue();
143:                this .isRow = ((Boolean) isRow).booleanValue();
144:                this .isEnabled = ((Boolean) isEnabled).booleanValue();
145:                this .refClause = (Vector) refClause;
146:                this .whenClause = (QueryTreeNode) whenClause;
147:                this .whenText = (whenText == null) ? null : ((String) whenText)
148:                        .trim();
149:                this .whenOffset = ((Integer) whenOffset).intValue();
150:                this .actionNode = (StatementNode) actionNode;
151:                this .originalActionText = (String) actionText;
152:                this .actionText = (actionText == null) ? null
153:                        : ((String) actionText).trim();
154:                this .actionOffset = ((Integer) actionOffset).intValue();
155:
156:                implicitCreateSchema = true;
157:            }
158:
159:            public String statementToString() {
160:                return "CREATE TRIGGER";
161:            }
162:
163:            /**
164:             * Prints the sub-nodes of this object.  See QueryTreeNode.java for
165:             * how tree printing is supposed to work.
166:             *
167:             * @param depth		The depth of this node in the tree
168:             */
169:
170:            public void printSubNodes(int depth) {
171:                if (SanityManager.DEBUG) {
172:                    super .printSubNodes(depth);
173:
174:                    if (triggerCols != null) {
175:                        printLabel(depth, "triggerColumns: ");
176:                        triggerCols.treePrint(depth + 1);
177:                    }
178:                    if (whenClause != null) {
179:                        printLabel(depth, "whenClause: ");
180:                        whenClause.treePrint(depth + 1);
181:                    }
182:                    if (actionNode != null) {
183:                        printLabel(depth, "actionNode: ");
184:                        actionNode.treePrint(depth + 1);
185:                    }
186:                }
187:            }
188:
189:            // accessors
190:
191:            // We inherit the generate() method from DDLStatementNode.
192:
193:            /**
194:             * Bind this CreateTriggerNode.  This means doing any static error
195:             * checking that can be done before actually creating the table.
196:             *
197:             * @return	The bound query tree
198:             *
199:             * @exception StandardException		Thrown on error
200:             */
201:            public QueryTreeNode bind() throws StandardException {
202:                CompilerContext compilerContext = getCompilerContext();
203:                DataDictionary dd = getDataDictionary();
204:                /*
205:                 ** Grab the current schema.  We will use that for
206:                 ** sps compilation
207:                 */
208:                LanguageConnectionContext lcc = getLanguageConnectionContext();
209:                compSchemaDescriptor = lcc.getDefaultSchema();
210:
211:                /*
212:                 ** Get and check the schema descriptor for this
213:                 ** trigger.  This check will throw the proper exception
214:                 ** if someone tries to create a trigger in the SYS
215:                 ** schema.
216:                 */
217:                triggerSchemaDescriptor = getSchemaDescriptor();
218:
219:                /*
220:                 ** Get the trigger table.
221:                 */
222:                triggerTableDescriptor = getTableDescriptor(tableName);
223:
224:                //throw an exception if user is attempting to create a trigger on a temporary table
225:                if (isSessionSchema(triggerTableDescriptor
226:                        .getSchemaDescriptor())) {
227:                    throw StandardException
228:                            .newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES);
229:                }
230:                if (isPrivilegeCollectionRequired()) {
231:                    compilerContext
232:                            .pushCurrentPrivType(Authorizer.TRIGGER_PRIV);
233:                    compilerContext
234:                            .addRequiredTablePriv(triggerTableDescriptor);
235:                    compilerContext.popCurrentPrivType();
236:                }
237:
238:                /*
239:                 ** Regenerates the actionText and actionNode if necessary.
240:                 */
241:                boolean needInternalSQL = bindReferencesClause(dd);
242:
243:                lcc.pushTriggerTable(triggerTableDescriptor);
244:                try {
245:                    /*
246:                     ** Bind the trigger action and the trigger
247:                     ** when clause to make sure that they are
248:                     ** ok.  Note that we have already substituted 
249:                     ** in various replacements for OLD/NEW transition
250:                     ** tables/variables and reparsed if necessary.
251:                     */
252:                    if (needInternalSQL)
253:                        compilerContext
254:                                .setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
255:
256:                    // For before triggers, the action statement cannot contain calls
257:                    // to procedures that modify SQL data. If the action statement 
258:                    // contains a procedure call, this reliability will be used during
259:                    // bind of the call statement node. 
260:                    if (isBefore)
261:                        compilerContext
262:                                .setReliability(CompilerContext.MODIFIES_SQL_DATA_PROCEDURE_ILLEGAL);
263:
264:                    actionNode.bind();
265:
266:                    if (whenClause != null) {
267:                        whenClause.bind();
268:                    }
269:                } finally {
270:                    lcc.popTriggerTable(triggerTableDescriptor);
271:                }
272:
273:                /* 
274:                 ** Statement is dependent on the TableDescriptor 
275:                 */
276:                compilerContext.createDependency(triggerTableDescriptor);
277:
278:                /*
279:                 ** If there is a list of columns, then no duplicate columns,
280:                 ** and all columns must be found.
281:                 */
282:                if (triggerCols != null && triggerCols.size() != 0) {
283:                    referencedColInts = new int[triggerCols.size()];
284:                    Hashtable columnNames = new Hashtable();
285:                    int tcSize = triggerCols.size();
286:                    for (int i = 0; i < tcSize; i++) {
287:                        ResultColumn rc = (ResultColumn) triggerCols
288:                                .elementAt(i);
289:                        if (columnNames.put(rc.getName(), rc) != null) {
290:                            throw StandardException
291:                                    .newException(
292:                                            SQLState.LANG_DUPLICATE_COLUMN_IN_TRIGGER_UPDATE,
293:                                            rc.getName(), triggerName);
294:                        }
295:
296:                        ColumnDescriptor cd = triggerTableDescriptor
297:                                .getColumnDescriptor(rc.getName());
298:                        if (cd == null) {
299:                            throw StandardException.newException(
300:                                    SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, rc
301:                                            .getName(), tableName);
302:                        }
303:
304:                        referencedColInts[i] = cd.getPosition();
305:                    }
306:
307:                    // sort the list
308:                    java.util.Arrays.sort(referencedColInts);
309:                }
310:
311:                //If attempting to reference a SESSION schema table (temporary or permanent) in the trigger action, throw an exception
312:                if (actionNode.referencesSessionSchema())
313:                    throw StandardException
314:                            .newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES);
315:
316:                return this ;
317:            }
318:
319:            /**
320:             * Return true if the node references SESSION schema tables (temporary or permanent)
321:             *
322:             * @return	true if references SESSION schema tables, else false
323:             *
324:             * @exception StandardException		Thrown on error
325:             */
326:            public boolean referencesSessionSchema() throws StandardException {
327:                //If create trigger is part of create statement and the trigger is defined on or it references SESSION schema tables,
328:                //it will get caught in the bind phase of trigger and exception will be thrown by the trigger bind. 
329:                return (isSessionSchema(triggerTableDescriptor.getSchemaName()) || actionNode
330:                        .referencesSessionSchema());
331:            }
332:
333:            /*
334:             ** BIND OLD/NEW TRANSITION TABLES/VARIABLES
335:             **
336:             ** 1) validate the referencing clause (if any)
337:             **
338:             ** 2) convert trigger action text.  e.g. 
339:             **		DELETE FROM t WHERE c = old.c
340:             ** turns into
341:             **		DELETE FROM t WHERE c = org.apache.derby.iapi.db.Factory::
342:             **					getTriggerExecutionContext().getOldRow().getInt('C');
343:             ** or
344:             **		DELETE FROM t WHERE c in (SELECT c FROM OLD)
345:             ** turns into
346:             **		DELETE FROM t WHERE c in (SELECT c FROM new TriggerOldTransitionTable OLD)
347:             **
348:             ** 3) check all column references against new/old transition 
349:             **	variables (since they are no longer 'normal' column references
350:             ** 	that will be checked during bind)
351:             **
352:             ** 4) reparse the new action text
353:             **
354:             ** You might be wondering why we regenerate the text and reparse
355:             ** instead of just reworking the tree to have the nodes we want.
356:             ** Well, the primary reason is that if we screwed with the tree,
357:             ** then we would have a major headache if this trigger action
358:             ** was ever recompiled -- spses don't really know that they are
359:             ** triggers so it would be quite arduous to figure out that an
360:             ** sps is a trigger and munge up its query tree after figuring
361:             ** out what its OLD/NEW tables are, etc.  Also, it is just plain
362:             ** easier to just generate the sql and rebind.  
363:             */
364:            private boolean bindReferencesClause(DataDictionary dd)
365:                    throws StandardException {
366:                validateReferencesClause(dd);
367:
368:                StringBuffer newText = new StringBuffer();
369:                boolean regenNode = false;
370:                int start = 0;
371:                if (isRow) {
372:                    /*
373:                     ** For a row trigger, we find all column references.  If
374:                     ** they are referencing NEW or OLD we turn them into
375:                     ** getTriggerExecutionContext().getOldRow().getInt('C');
376:                     */
377:                    CollectNodesVisitor visitor = new CollectNodesVisitor(
378:                            ColumnReference.class);
379:                    actionNode.accept(visitor);
380:                    Vector refs = visitor.getList();
381:                    /* we need to sort on position in string, beetle 4324
382:                     */
383:                    QueryTreeNode[] cols = sortRefs(refs, true);
384:
385:                    for (int i = 0; i < cols.length; i++) {
386:                        ColumnReference ref = (ColumnReference) cols[i];
387:
388:                        /*
389:                         ** Only occurrences of those OLD/NEW transition tables/variables 
390:                         ** are of interest here.  There may be intermediate nodes in the 
391:                         ** parse tree that have its own RCL which contains copy of 
392:                         ** column references(CR) from other nodes. e.g.:  
393:                         **
394:                         ** CREATE TRIGGER tt 
395:                         ** AFTER INSERT ON x
396:                         ** REFERENCING NEW AS n 
397:                         ** FOR EACH ROW
398:                         **    INSERT INTO y VALUES (n.i), (999), (333);
399:                         ** 
400:                         ** The above trigger action will result in InsertNode that 
401:                         ** contains a UnionNode of RowResultSetNodes.  The UnionNode
402:                         ** will have a copy of the CRs from its left child and those CRs 
403:                         ** will not have its beginOffset set which indicates they are 
404:                         ** not relevant for the conversion processing here, so we can 
405:                         ** safely skip them. 
406:                         */
407:                        if (ref.getBeginOffset() == -1) {
408:                            continue;
409:                        }
410:
411:                        TableName tableName = ref.getTableNameNode();
412:                        if ((tableName == null)
413:                                || ((oldTableName == null || !oldTableName
414:                                        .equals(tableName.getTableName())) && (newTableName == null || !newTableName
415:                                        .equals(tableName.getTableName())))) {
416:                            continue;
417:                        }
418:
419:                        int tokBeginOffset = tableName.getBeginOffset();
420:                        int tokEndOffset = tableName.getEndOffset();
421:                        if (tokBeginOffset == -1) {
422:                            continue;
423:                        }
424:
425:                        regenNode = true;
426:                        checkInvalidTriggerReference(tableName.getTableName());
427:                        String colName = ref.getColumnName();
428:                        int columnLength = ref.getEndOffset()
429:                                - ref.getBeginOffset() + 1;
430:
431:                        newText.append(originalActionText.substring(start,
432:                                tokBeginOffset - actionOffset));
433:                        newText.append(genColumnReferenceSQL(dd, colName,
434:                                tableName.getTableName(), tableName
435:                                        .getTableName().equals(oldTableName)));
436:                        start = tokEndOffset - actionOffset + columnLength + 2;
437:                    }
438:                } else {
439:                    /*
440:                     ** For a statement trigger, we find all FromBaseTable nodes.  If
441:                     ** the from table is NEW or OLD (or user designated alternates
442:                     ** REFERENCING), we turn them into a trigger table VTI.
443:                     */
444:                    CollectNodesVisitor visitor = new CollectNodesVisitor(
445:                            FromBaseTable.class);
446:                    actionNode.accept(visitor);
447:                    Vector refs = visitor.getList();
448:                    QueryTreeNode[] tabs = sortRefs(refs, false);
449:                    for (int i = 0; i < tabs.length; i++) {
450:                        FromBaseTable fromTable = (FromBaseTable) tabs[i];
451:                        String refTableName = fromTable.getTableName()
452:                                .getTableName();
453:                        String baseTableName = fromTable.getBaseTableName();
454:                        if ((baseTableName == null)
455:                                || ((oldTableName == null || !oldTableName
456:                                        .equals(baseTableName)) && (newTableName == null || !newTableName
457:                                        .equals(baseTableName)))) {
458:                            continue;
459:                        }
460:                        int tokBeginOffset = fromTable.getTableNameField()
461:                                .getBeginOffset();
462:                        int tokEndOffset = fromTable.getTableNameField()
463:                                .getEndOffset();
464:                        if (tokBeginOffset == -1) {
465:                            continue;
466:                        }
467:
468:                        checkInvalidTriggerReference(baseTableName);
469:
470:                        regenNode = true;
471:                        newText.append(originalActionText.substring(start,
472:                                tokBeginOffset - actionOffset));
473:                        newText
474:                                .append(baseTableName.equals(oldTableName) ? "new org.apache.derby.catalog.TriggerOldTransitionRows() "
475:                                        : "new org.apache.derby.catalog.TriggerNewTransitionRows() ");
476:                        /*
477:                         ** If the user supplied a correlation, then just
478:                         ** pick it up automatically; otherwise, supply
479:                         ** the default.
480:                         */
481:                        if (refTableName.equals(baseTableName)) {
482:                            newText.append(baseTableName).append(" ");
483:                        }
484:                        start = tokEndOffset - actionOffset + 1;
485:                    }
486:                }
487:
488:                /*
489:                 ** Parse the new action text with the substitutions.
490:                 ** Also, we reset the actionText to this new value.  This
491:                 ** is what we are going to stick in the system tables.
492:                 */
493:                if (regenNode) {
494:                    if (start < originalActionText.length()) {
495:                        newText.append(originalActionText.substring(start));
496:                    }
497:                    actionText = newText.toString();
498:                    actionNode = (StatementNode) reparseTriggerText();
499:                }
500:
501:                return regenNode;
502:            }
503:
504:            /*
505:             ** Sort the refs into array.
506:             */
507:            private QueryTreeNode[] sortRefs(Vector refs, boolean isRow) {
508:                int size = refs.size();
509:                QueryTreeNode[] sorted = new QueryTreeNode[size];
510:                int i = 0;
511:                for (Enumeration e = refs.elements(); e.hasMoreElements();) {
512:                    if (isRow)
513:                        sorted[i++] = (ColumnReference) e.nextElement();
514:                    else
515:                        sorted[i++] = (FromBaseTable) e.nextElement();
516:                }
517:
518:                /* bubble sort
519:                 */
520:                QueryTreeNode temp;
521:                for (i = 0; i < size - 1; i++) {
522:                    temp = null;
523:                    for (int j = 0; j < size - i - 1; j++) {
524:                        if ((isRow && sorted[j].getBeginOffset() > sorted[j + 1]
525:                                .getBeginOffset())
526:                                || (!isRow && ((FromBaseTable) sorted[j])
527:                                        .getTableNameField().getBeginOffset() > ((FromBaseTable) sorted[j + 1])
528:                                        .getTableNameField().getBeginOffset())) {
529:                            temp = sorted[j];
530:                            sorted[j] = sorted[j + 1];
531:                            sorted[j + 1] = temp;
532:                        }
533:                    }
534:                    if (temp == null) // sorted
535:                        break;
536:                }
537:
538:                return sorted;
539:            }
540:
541:            /*
542:             ** Parse the text and return the tree.
543:             */
544:            private QueryTreeNode reparseTriggerText() throws StandardException {
545:                /*
546:                 ** Get a new compiler context, so the parsing of the text
547:                 ** doesn't mess up anything in the current context 
548:                 */
549:                LanguageConnectionContext lcc = getLanguageConnectionContext();
550:                CompilerContext newCC = lcc.pushCompilerContext();
551:                newCC.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
552:
553:                try {
554:                    return QueryTreeNode.parseQueryText(newCC, actionText,
555:                            (Object[]) null, lcc);
556:                }
557:
558:                finally {
559:                    lcc.popCompilerContext(newCC);
560:                }
561:            }
562:
563:            /*
564:             ** Make sure the given column name is found in the trigger
565:             ** target table.  Generate the appropriate SQL to get it.
566:             **
567:             ** @return a string that is used to get the column using
568:             ** getObject() on the desired result set and CAST it back
569:             ** to the proper type in the SQL domain. 
570:             **
571:             ** @exception StandardException on invalid column name
572:             */
573:            private String genColumnReferenceSQL(DataDictionary dd,
574:                    String colName, String tabName, boolean isOldTable)
575:                    throws StandardException {
576:                ColumnDescriptor colDesc = null;
577:                if ((colDesc = triggerTableDescriptor
578:                        .getColumnDescriptor(colName)) == null) {
579:                    throw StandardException.newException(
580:                            SQLState.LANG_COLUMN_NOT_FOUND, tabName + "."
581:                                    + colName);
582:                }
583:
584:                /*
585:                 ** Generate something like this:
586:                 **
587:                 ** 		cast (org.apache.derby.iapi.db.Factory::
588:                 **			getTriggerExecutionContext().getNewRow().
589:                 **				getObject(<colPosition>) AS DECIMAL(6,2))
590:                 **
591:                 ** Column position is used to avoid the wrong column being
592:                 ** selected problem (DERBY-1258) caused by the case insensitive
593:                 ** JDBC rules for fetching a column by name.
594:                 **
595:                 ** The cast back to the SQL Domain may seem redundant
596:                 ** but we need it to make the column reference appear
597:                 ** EXACTLY like a regular column reference, so we need
598:                 ** the object in the SQL Domain and we need to have the
599:                 ** type information.  Thus a user should be able to do 
600:                 ** something like
601:                 **
602:                 **		CREATE TRIGGER ... INSERT INTO T length(Column), ...
603:                 */
604:                StringBuffer methodCall = new StringBuffer();
605:                methodCall
606:                        .append("CAST (org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().");
607:                methodCall.append(isOldTable ? "getOldRow()" : "getNewRow()");
608:                methodCall.append(".getObject(");
609:                methodCall.append(colDesc.getPosition());
610:                methodCall.append(") AS ");
611:                DataTypeDescriptor dts = colDesc.getType();
612:                TypeId typeId = dts.getTypeId();
613:
614:                /*
615:                 ** getSQLString() returns <typeName> 
616:                 ** for user types, so call getSQLTypeName in that
617:                 ** case.
618:                 */
619:                methodCall.append((typeId.userType() ? typeId.getSQLTypeName()
620:                        : dts.getSQLstring()));
621:
622:                methodCall.append(") ");
623:
624:                return methodCall.toString();
625:            }
626:
627:            /*
628:             ** Check for illegal combinations here: insert & old or
629:             ** delete and new
630:             */
631:            private void checkInvalidTriggerReference(String tableName)
632:                    throws StandardException {
633:                if (tableName.equals(oldTableName)
634:                        && (triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_INSERT) == TriggerDescriptor.TRIGGER_EVENT_INSERT) {
635:                    throw StandardException.newException(
636:                            SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "INSERT",
637:                            "new");
638:                } else if (tableName.equals(newTableName)
639:                        && (triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_DELETE) == TriggerDescriptor.TRIGGER_EVENT_DELETE) {
640:                    throw StandardException.newException(
641:                            SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "DELETE",
642:                            "old");
643:                }
644:            }
645:
646:            /*
647:             ** Make sure that the referencing clause is legitimate.
648:             ** While we are at it we set the new/oldTableName to
649:             ** be whatever the user wants.
650:             */
651:            private void validateReferencesClause(DataDictionary dd)
652:                    throws StandardException {
653:                if ((refClause == null) || (refClause.size() == 0)) {
654:                    return;
655:                }
656:
657:                for (Enumeration e = refClause.elements(); e.hasMoreElements();) {
658:                    TriggerReferencingStruct trn = (TriggerReferencingStruct) e
659:                            .nextElement();
660:
661:                    /*
662:                     ** 1) Make sure that we don't try to refer
663:                     ** to a table for a row trigger or a row for
664:                     ** a table trigger.
665:                     */
666:                    if (isRow && !trn.isRow) {
667:                        throw StandardException.newException(
668:                                SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "ROW",
669:                                "row");
670:                    } else if (!isRow && trn.isRow) {
671:                        throw StandardException.newException(
672:                                SQLState.LANG_TRIGGER_BAD_REF_MISMATCH,
673:                                "STATEMENT", "table");
674:                    }
675:
676:                    /*
677:                     ** 2) Make sure we have no dups
678:                     */
679:                    if (trn.isNew) {
680:
681:                        if (newTableInReferencingClause) {
682:                            throw StandardException
683:                                    .newException(SQLState.LANG_TRIGGER_BAD_REF_CLAUSE_DUPS);
684:                        }
685:
686:                        /*
687:                         ** 3a) No NEW reference in delete trigger
688:                         */
689:                        if ((triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_DELETE) == TriggerDescriptor.TRIGGER_EVENT_DELETE) {
690:                            throw StandardException.newException(
691:                                    SQLState.LANG_TRIGGER_BAD_REF_MISMATCH,
692:                                    "DELETE", "old");
693:                        }
694:                        newTableName = trn.identifier;
695:                        newTableInReferencingClause = true;
696:                    } else {
697:                        if (oldTableInReferencingClause) {
698:                            throw StandardException
699:                                    .newException(SQLState.LANG_TRIGGER_BAD_REF_CLAUSE_DUPS);
700:                        }
701:                        /*
702:                         ** 3b) No OLD reference in insert trigger
703:                         */
704:                        if ((triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_INSERT) == TriggerDescriptor.TRIGGER_EVENT_INSERT) {
705:                            throw StandardException.newException(
706:                                    SQLState.LANG_TRIGGER_BAD_REF_MISMATCH,
707:                                    "INSERT", "new");
708:                        }
709:                        oldTableName = trn.identifier;
710:                        oldTableInReferencingClause = true;
711:                    }
712:
713:                    /*
714:                     ** 4) Additional restriction on BEFORE triggers
715:                     */
716:                    if (this .isBefore && !trn.isRow) {
717:                        // OLD_TABLE and NEW_TABLE not allowed for BEFORE triggers.
718:                        throw StandardException.newException(
719:                                SQLState.LANG_TRIGGER_BAD_REF_MISMATCH,
720:                                "BEFORE", "row");
721:                    }
722:
723:                }
724:
725:            }
726:
727:            /**
728:             * Create the Constant information that will drive the guts of Execution.
729:             *
730:             * @exception StandardException		Thrown on failure
731:             */
732:            public ConstantAction makeConstantAction() throws StandardException {
733:                String oldReferencingName = (oldTableInReferencingClause) ? oldTableName
734:                        : null;
735:                String newReferencingName = (newTableInReferencingClause) ? newTableName
736:                        : null;
737:
738:                return getGenericConstantActionFactory()
739:                        .getCreateTriggerConstantAction(
740:                                triggerSchemaDescriptor.getSchemaName(),
741:                                getRelativeName(),
742:                                triggerEventMask,
743:                                isBefore,
744:                                isRow,
745:                                isEnabled,
746:                                triggerTableDescriptor,
747:                                (UUID) null, // when SPSID
748:                                whenText,
749:                                (UUID) null, // action SPSid 
750:                                actionText,
751:                                (actionCompSchemaId == null) ? compSchemaDescriptor
752:                                        .getUUID()
753:                                        : actionCompSchemaId,
754:                                (Timestamp) null, // creation time
755:                                referencedColInts, originalActionText,
756:                                oldTableInReferencingClause,
757:                                newTableInReferencingClause,
758:                                oldReferencingName, newReferencingName);
759:            }
760:
761:            /**
762:             * Convert this object to a String.  See comments in QueryTreeNode.java
763:             * for how this should be done for tree printing.
764:             *
765:             * @return	This object as a String
766:             */
767:            public String toString() {
768:                if (SanityManager.DEBUG) {
769:                    String refString = "null";
770:                    if (refClause != null) {
771:                        StringBuffer buf = new StringBuffer();
772:                        for (Enumeration e = refClause.elements(); e
773:                                .hasMoreElements();) {
774:                            buf.append("\t");
775:                            TriggerReferencingStruct trn = (TriggerReferencingStruct) e
776:                                    .nextElement();
777:                            buf.append(trn.toString());
778:                            buf.append("\n");
779:                        }
780:                        refString = buf.toString();
781:                    }
782:
783:                    return super .toString() + "tableName: " + tableName
784:                            + "\ntriggerEventMask: " + triggerEventMask
785:                            + "\nisBefore: " + isBefore + "\nisRow: " + isRow
786:                            + "\nisEnabled: " + isEnabled + "\nwhenText: "
787:                            + whenText + "\nrefClause: " + refString
788:                            + "\nactionText: " + actionText + "\n";
789:                } else {
790:                    return "";
791:                }
792:            }
793:
794:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.